Nuxt environment variables and runtimeConfig
Nuxt reads .env only in dev and build, then overrides runtimeConfig from NUXT_ variables at runtime. How the mapping works and how to start Nuxt with Crunu.
Part of the guide
NuxtAt a glance
- Env file that wins
.envThe only .env file it reads. See the load order
- Public prefix
NUXT_PUBLIC_Only names with this prefix reach browser code, and anyone can read them there.
- Values applied
- Build and runtime
Some values are fixed at build time, the rest are read when the app runs.
- Built-in secret store
- None: runtimeConfig is filled from the server's environment
- Crunu command
crunu run --env web-app/production -- node .output/server/index.mjsRecipeCrunu has no dedicated integration here: this is the general setup.
Common pitfalls
- The built server never reads .env. In production only real environment variables reach runtimeConfig.
- Only NUXT_ and NUXT_PUBLIC_ variables override runtimeConfig at runtime, and only for keys already declared in nuxt.config.
- Reading process.env.OTHER_NAME in nuxt.config works at build time and breaks at runtime, because the config is not evaluated again.
- Overrides are cast with destr, so a value like 4848e0 becomes a number unless you wrap it in double quotes.
- Everything under runtimeConfig.public is added to each page payload, so it is readable by every visitor.
On this page
Nuxt reads environment variables in two different moments. While you run nuxt dev, nuxt build or nuxt generate, the Nuxt CLI loads the .env file in the project root into process.env. When the built server runs in production, it doesn't read .env at all. Instead, Nuxt fills runtimeConfig from the server's real environment: a variable named NUXT_ plus the key in upper snake case replaces a private key, and NUXT_PUBLIC_ plus the key replaces a public one. Your code reads the result with useRuntimeConfig(). This page follows the Nuxt 4 runtime config guide, checked October 30, 2026.
Two phases, two sources
| Phase | Commands | Where values come from |
|---|---|---|
| Development and build | nuxt dev, nuxt build, nuxt generate | the process environment plus .env, or the file passed with --dotenv |
| Production server | node .output/server/index.mjs | the process environment only, mapped through NUXT_ names |
| Local preview | nuxt preview | .env is loaded for convenience |
The .env page explains the production rule: once the server is built, you are responsible for setting variables with the tools of your host, because Nuxt also targets serverless and edge platforms that may not have a normal file system. In development, editing .env restarts the Nuxt instance so new values apply. Removing a line doesn't unset a value that was already loaded.
Only one file is read by default. To use another, pass it explicitly, for example nuxt dev --dotenv .env.local. There is no automatic chain of .local and mode files as in Vite.
How runtimeConfig and NUXT_ names fit together
You declare the shape and defaults in nuxt.config:
export default defineNuxtConfig({
runtimeConfig: {
apiSecret: '',
public: {
apiBase: '/api'
}
}
})At runtime, Nuxt looks for matching variables. The name is NUXT_, then the key path in upper case with an underscore at each case change and nesting level:
| Key | Variable |
|---|---|
apiSecret | NUXT_API_SECRET |
public.apiBase | NUXT_PUBLIC_API_BASE |
Three rules make or break this:
- The key must already exist. Nuxt only overrides keys declared in
nuxt.config, so arbitrary environment variables can't leak into your app's configuration. An undeclaredNUXT_STRIPE_KEYis silently ignored. - The config file runs once, at build time. Writing
apiSecret: process.env.STRIPE_KEYreadsSTRIPE_KEYduring the build and bakes the result into the output. ChangingSTRIPE_KEYon the server later does nothing. The guide recommends variables that match theruntimeConfigstructure instead. - Values are converted. Nuxt casts overrides with
destr, sotruebecomes a boolean and numeric-looking strings become numbers. A hex color like4848e0is read as scientific notation and becomes the number4848unless you wrap it in literal double quotes.
Private and public keys
Top-level keys are private. On the server, useRuntimeConfig() returns the whole object, read-only. In the browser it returns only public and Nuxt's internal app section. Public values are added to each page's payload, so treat anything under public as published. The guide also warns against rendering config keys into templates or passing them to useState, both of which can send private values to the client.
Runtime config is serialized before it's handed to Nitro, Nuxt's server engine. Functions, Sets and Maps don't survive that, so put such logic in a plugin or server middleware.
In server routes, pass the event: useRuntimeConfig(event) makes sure the per-request environment overrides apply.
When a value doesn't show up
Most "my variable is ignored" reports in Nuxt come down to one of four causes, and you can check them in order without printing any secret:
- Is the key declared? Open
nuxt.configand find the key underruntimeConfigorruntimeConfig.public. If it isn't there, no environment variable can set it. - Does the name match the key? Convert the key path by hand:
public.stripePublishableKeybecomesNUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY. A missing underscore at a case change is enough to break it. - Is the server reading a file you expect it to ignore? In production it never reads
.env, so a value that only exists in that file is simply absent. Compare withnuxt preview, which does load the file, to see whether that's the difference. - Was the value baked in? If
nuxt.configcopies from another variable withprocess.env, the build machine's value is what you get, regardless of the server.
To confirm what the server received, log the names of the runtime config keys at startup, or whether each is non-empty, never the values themselves. Logs travel further than you expect.
Run Nuxt with values from Crunu
The production rule that Nuxt reads only real environment variables is exactly what crunu run provides. It decrypts a Crunu environment in memory and starts the server with those variables in its process environment, so NUXT_API_SECRET overrides apiSecret without any file on the machine.
- Put the production variables, named with the
NUXT_andNUXT_PUBLIC_rules, into a Crunu environment such asweb-app/production, and addNODE_ENV=productionto it. - Develop with
crunu run --env web-app/development -- npx nuxt dev, which gives the dev server the same variables the CLI would read from.env. - Build in CI with
exec crunu run --env web-app/build -- npx nuxt buildif the build needs values at all, for example for a prerendered page. - On a Linux server with the agent, start
crunu run --env web-app/production --restart on-change -- node .output/server/index.mjs.
NODE_ENV belongs in the environment because crunu run passes only a short allowlist from your shell, such as PATH and HOME, and Nuxt's deployment guide runs the server with NODE_ENV=production to avoid development-only warnings from libraries. The server's own settings, NITRO_PORT or PORT and NITRO_HOST or HOST, can sit in the same environment.
With --restart on-change, crunu restarts the Nitro server after a value changes, and the new NUXT_ values apply. Prerendered pages and anything read in nuxt.config stay as built.
One set of names for every stage
Because the NUXT_ names follow mechanically from runtimeConfig, a team can use the exact same variable names in development, staging and production, and change only the values. That keeps a few things simple. The .env.example in the repository lists each NUXT_ name once. Each Crunu environment for the project holds the same names with stage-specific values, and Crunu's environment comparison shows which names exist in one stage but not another, with values hidden until someone chooses to show them. A new key added to runtimeConfig then needs one new variable in each environment, and a review can check that it's there before the deploy instead of after the first error.
Platforms that host Nuxt for you
On Netlify, Vercel and similar hosts, the platform holds the runtime variables and passes them to Nuxt's server functions. Crunu has no built-in sync into those stores. The honest setup there is to keep runtime values in the platform's own settings, and use crunu run around the build and around local development. The Netlify page describes that split, and the Vercel integration shows a pipeline recipe for pushing values into Vercel from Crunu.
Related reading
- Vite, which Nuxt uses for client builds.
- Next.js and SvelteKit for comparison.
- How to share .env files with your team.
- crunu run and installing the agent.
Frequently asked questions
Something else? Write to support@crunu.com
Why is my NUXT_API_SECRET ignored in production?
Nuxt overrides only keys that exist in runtimeConfig in nuxt.config. Declare apiSecret there, even with an empty default, and NUXT_API_SECRET will replace it at runtime.
Does nuxt preview read my .env file?
Yes. nuxt preview loads .env into process.env for convenience. The production server started with node .output/server/index.mjs does not.
Can I change runtime config on a prerendered static site?
No. For a purely static site, the values are fixed when the pages are generated. Rebuild to change them.
Should NODE_ENV be in the Crunu environment?
For production, yes. Nuxt's deployment guide starts the server with NODE_ENV set to production, and crunu run doesn't copy NODE_ENV from your shell by default.
Sources
Every source this page relies on, with the date we last checked it.
- Nuxt: Runtime config · checked October 30, 2026
- Nuxt: .env · checked October 30, 2026
- Nuxt: Deployment · checked October 30, 2026