Reference

Environment values

Where each kind of value lives, which file wins, and how the secret store is organised.

Every value the applications read comes from one of three places: a default in the schema, a committed file for one runtime context, or the secret store. This page explains the model. The pages under Configuration are the procedures that apply it.

Runtime contexts

A runtime context is the situation the code is running in. There are six, and they differ in what they own rather than only in name.

ContextWhat it isWhat selects it
localA developer machineThe default when nothing sets one
testThe test suiteEvery CI job, and pnpm test
previewA pull request deploymentAny pull request
developmentThe shared development deploymentA push to the development branch
stagingThe pre-production deploymentA push to the staging branch
productionThe live deploymentA push to main

Nothing reads a branch name to work this out at load time. The deploy workflow maps each event to a context, and that mapping lives in one place.

Two contexts deploy nothing. local runs against Docker and your filesystem, and test reaches nothing at all — it reads committed fake values, so a clean checkout runs the test suite with no credentials.

local is the one context with no value file of its own. It loads the development file, which is why local overrides have to go somewhere that loads later.

Three homes for a value

Ask one question per bucket. The answer decides the home.

HomeForAsk
.env.schema defaultA value that's the same in every contextDoes it change between contexts?
Committed .env.<context>A non-secret value that differs by contextWould you show it in a pull request?
The secret storeA credentialWould a leak hurt?

Your project's identity is a schema default that lives outside either application: .env.project.schema at the repository root, which both application schemas import. Every deterministic resource name and sending domain is built from it rather than written down. Project identity is that file and everything derived from it.

Non-secret configuration belongs in Git. Git gives you review, history, attribution, and rollback next to the code that reads the value. Infisical's free plan keeps no secret history, so a value moved there loses all four and gains nothing.

Two homes sit outside the three: a gitignored file for anything specific to one machine, and CI for anything generated per build.

Technical details lists the environment files each application owns.

Which file wins

Files load in this order, and a later file overrides an earlier one:

  1. Defaults written in .env.schema, including everything it imports — layer schemas and .env.project.schema
  2. .env.local
  3. .env.<context>, the value file for the current context
  4. .env.<context>.local

Both gitignored files exist because they sit on opposite sides of the value file:

  • .env.local loads before it, so it can supply a key no committed file sets, and can't override one that's already set.
  • .env.<context>.local loads after it, so it overrides a committed value.

The second file is named after the value file, not after the context. Under local the value file is .env.development, so the override file is .env.development.local.

How the secret store is organised

NuxtStart stores credentials in Infisical. Three Infisical concepts carry a NuxtStart meaning:

Infisical conceptHoldsHere it is
ProjectA set of secretsOne application: apps/web or apps/site
EnvironmentA slice of a projectA secret tier: development, staging, production
FolderA path in a tierOne runtime context

Two projects, because the two applications deploy separately and must never read each other's secrets. apps/web holds every credential. apps/site declares none today, and still has a project so a credential has somewhere to go later.

Three contexts share the development tier, so that tier carries three folders:

Runtime contextEnvironmentFolder
localdevelopment/local
previewdevelopment/preview
developmentdevelopment/development
stagingstaging/
productionproduction/
testnonenone

Folders carry runtime context, not repository structure. There's no folder per layer or per package: environment variables are one flat namespace at runtime, and the schemas already say which layer owns which key.

/local is shared team configuration for the local runtime context. It isn't storage for one developer's personal values — those belong in a gitignored file.

How each context authenticates

Two mechanisms, and exactly one runs per build.

WhereWhat's setMechanism
A developer machineA client ID and client secretUniversal Auth
A GitHub Actions runnerA machine identity IDOIDC

A runner sets no client credentials and falls through to OIDC, where GitHub mints a token for one workflow run of one repository and Infisical exchanges it. No long-lived Infisical credential is stored in GitHub at all.

Two OIDC identities exist, and the workflow picks between them:

Trusted forGranted
Pull requestsThe /preview folder
Branch pushesThe /development folder, plus the staging and production tiers

A machine identity ID is an identifier rather than a credential, which is why it travels as a repository variable instead of a repository secret.

Why some credentials are committed

GOOGLE_CLIENT_ID and GITHUB_CLIENT_ID sit in Git while their client secrets sit in the secret store. That isn't an oversight. A browser sends an OAuth client ID in every authorization request, so keeping one private can't be a security boundary. Committing it buys review and history; hiding it buys nothing.

The same reasoning applies to an Infisical project ID and to a Cloudflare account ID. Each names something without granting access to it.

Next steps

Copyright © 2026