Skip to content
FrameworkTypeScript

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.

NuxtAt a glance

Env file that wins
.env

The 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

nuxt.com/docs/4.x/guide/going-further/runtime-config

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

  1. The built server never reads .env. In production only real environment variables reach runtimeConfig.
  2. Only NUXT_ and NUXT_PUBLIC_ variables override runtimeConfig at runtime, and only for keys already declared in nuxt.config.
  3. Reading process.env.OTHER_NAME in nuxt.config works at build time and breaks at runtime, because the config is not evaluated again.
  4. Overrides are cast with destr, so a value like 4848e0 becomes a number unless you wrap it in double quotes.
  5. 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

PhaseCommandsWhere values come from
Development and buildnuxt dev, nuxt build, nuxt generatethe process environment plus .env, or the file passed with --dotenv
Production servernode .output/server/index.mjsthe process environment only, mapped through NUXT_ names
Local previewnuxt 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:

TypeScript
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:

KeyVariable
apiSecretNUXT_API_SECRET
public.apiBaseNUXT_PUBLIC_API_BASE

Three rules make or break this:

  1. 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 undeclared NUXT_STRIPE_KEY is silently ignored.
  2. The config file runs once, at build time. Writing apiSecret: process.env.STRIPE_KEY reads STRIPE_KEY during the build and bakes the result into the output. Changing STRIPE_KEY on the server later does nothing. The guide recommends variables that match the runtimeConfig structure instead.
  3. Values are converted. Nuxt casts overrides with destr, so true becomes a boolean and numeric-looking strings become numbers. A hex color like 4848e0 is read as scientific notation and becomes the number 4848 unless 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:

  1. Is the key declared? Open nuxt.config and find the key under runtimeConfig or runtimeConfig.public. If it isn't there, no environment variable can set it.
  2. Does the name match the key? Convert the key path by hand: public.stripePublishableKey becomes NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY. A missing underscore at a case change is enough to break it.
  3. 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 with nuxt preview, which does load the file, to see whether that's the difference.
  4. Was the value baked in? If nuxt.config copies from another variable with process.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.

  1. Put the production variables, named with the NUXT_ and NUXT_PUBLIC_ rules, into a Crunu environment such as web-app/production, and add NODE_ENV=production to it.
  2. 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.
  3. Build in CI with exec crunu run --env web-app/build -- npx nuxt build if the build needs values at all, for example for a prerendered page.
  4. 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.

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.

  1. Nuxt: Runtime config · checked October 30, 2026
  2. Nuxt: .env · checked October 30, 2026
  3. Nuxt: Deployment · checked October 30, 2026

Keep your .env values in one place

Free during early access. Values are encrypted on your devices, each environment has its own key, and crunu run hands them to your app without writing a file.