Netlify environment variables: build vs runtime
Netlify variables have scopes and deploy contexts: builds, functions and runtime each see different values. What netlify.toml can't do, and where Crunu fits.
Part of the guide
NetlifyAt a glance
- Env files, highest priority first
- No .env files: values come from the process environment or the platform.
- Public prefix
- None
No names are copied into browser code by a prefix.
- Values applied
- Build and runtime
Some values are fixed at build time, the rest are read when the app runs.
- Built-in secret store
- Environment variables with scopes, deploy contexts and write-only secret values
- Crunu command
crunu run -- npm run buildRecipeCrunu has no dedicated integration here: this is the general setup.
Common pitfalls
- Variables declared in netlify.toml are not available to serverless functions, and they override UI values with the same key.
- Functions use the values that were set when the deploy was made. After changing a variable, create a new deploy.
- A variable reaches functions only if its scope includes Functions, and the build only if its scope includes Builds.
- In Lambda compatibility mode, all environment variables of a function together must fit in 4 KB.
- Secret scanning looks only for values Netlify stores as secrets. Values injected from elsewhere during the build aren't on its list.
On this page
On Netlify, an environment variable has a value per deploy context and, on Pro and Enterprise plans, a set of scopes that decide where it's available: the build, serverless and edge functions, runtime features like forms and signed redirects, and post-processing such as snippet injection. Values set in the Netlify UI, CLI or API can reach functions. Values declared in netlify.toml can't, and they override the UI for the same key during the build. Every deploy is pinned to the values that existed when it was created, so a change needs a new deploy. This page follows the Netlify environment variables overview, last updated October 3, 2026 and checked October 30, 2026.
Scopes: who sees a variable
| Scope | Where the value is available |
|---|---|
| Builds | the build step, build plugins and framework and function builds |
| Functions | serverless functions, edge functions and on-demand builders while they run |
| Runtime | forms and signed proxy redirects |
| Post processing | snippet injection into served pages |
By default a variable applies to all scopes. Narrowing scopes is the main tool for keeping a value where it belongs. A deploy key for a private package needs only Builds. A payment provider's secret key used by a checkout function needs only Functions. Nothing secret should have Post processing, and Netlify enforces that for values flagged as secret.
Deploy contexts: which value
A variable can have one value for all contexts or a different value for each: Production, Deploy Previews, Branch deploys (with per-branch overrides and wildcard prefixes such as release/*), Preview server and Local development for the Netlify CLI. A common setup gives Deploy Previews a non-production API key, so code from a pull request never runs with production credentials.
During builds, Netlify sets read-only variables you can use to tell contexts apart: CONTEXT (production, deploy-preview, branch-deploy or dev), BRANCH, COMMIT_REF, URL and DEPLOY_PRIME_URL. Only URL, SITE_NAME and SITE_ID are available to functions at runtime.
netlify.toml versus the UI
netlify.toml can declare environment variables too, and two rules about them cause most confusion:
- They don't reach functions. The functions documentation says it twice: variables declared in
netlify.tomlare not available to serverless functions. - They override the UI during builds. A value in
netlify.tomlbeats a variable with the same key set in the UI, CLI or API.
Netlify recommends the UI, CLI or API where possible, so sensitive values don't end up in your repository. Treat netlify.toml as a place for non-secret build settings that you're happy to review in pull requests.
Functions get deploy-time values
The functions guide is explicit: each deploy, including Deploy Previews and branch deploys, uses the environment variable values set at the time of deployment, and to apply updated values you create a new deploy. Rotating a key used by a function is therefore two steps: change the variable, then deploy. Functions read values with process.env. In Lambda compatibility mode, AWS's limit applies and all variables of a function together can't exceed 4 KB. Narrowing scopes helps stay under it, and the newer functions format removes the limit.
Secret values and scanning
Flagging a variable as Contains secret values makes it write-only. After saving, nobody can read it back through the UI, CLI or API, outside the dev context, and the flag can't be removed. Secret values must be set for explicit contexts and scopes and can't use post processing.
Secret scanning then checks your repository and build output for those values, in plain, base64 and URL-encoded form, and fails the build before publishing if it finds one. Smart detection adds a scan for things that look like secrets without any configuration.
Local development
The Local development context exists for the Netlify CLI, which gives your local dev server the values set for that context. It's handy, and it has one documented limit: variables in the Local development context can't be marked as secret. Whatever you put there is readable by anyone on the team who can see the site's variables, and it's copied onto every laptop that runs the CLI. Keep real secrets out of that context. Use throwaway development credentials, or let developers get their values another way, as described next, so a local run never needs anything stronger than a development key.
Where crunu run fits on Netlify
Netlify Functions run on Netlify's platform, and Crunu has no way to run inside them and no built-in sync into Netlify's variables. So the split is clear: function secrets stay in Netlify, scoped to Functions and flagged as secret. Crunu covers the build and local development, which is where most framework values are read anyway.
- Create one Crunu service token per deploy context, each granted a single environment, such as
site/productionfor Production andsite/previewfor Deploy Previews. - Store them as the Netlify variable
CRUNU_TOKENwith a different value per context, the Builds scope only, and Contains secret values checked. AddCRUNU_SERVER, your Crunu server address, as an ordinary variable. - In the build command, install
crunuwith the install script's--user-installmode, then runcrunu run -- npm run build. - Locally, run
crunu run --env site/development -- npm run devaftercrunu login.
Without --env, crunu run uses every environment granted to the token, which is exactly one here, so the same build command builds previews with preview values and production with production values. A pull request can't reach the production token, because Deploy Previews get the preview token.
Some honest limits. Netlify stores the tokens, so Netlify's build system can decrypt the environments they were granted, and for those environments the promise that the server can't read your values doesn't extend to Netlify. A token's lease is a window of inactivity from 1 hour to 30 days that each sign-in extends, so a site that isn't built for longer than the lease fails its next build until an owner or admin resumes the token. Scopes need a Pro or Enterprise plan. On other plans every variable applies to all scopes, so the token would also be visible to your functions. And Netlify's secret scanning knows only the values it stores. A value that crunu run passes into the build isn't on that list, so if a build writes it into a public file, only smart detection might notice. Values your framework inlines into client code, such as Vite's VITE_ variables, are public by design, so keep secrets out of those names entirely.
Related reading
- Vite, Astro and Nuxt for what frameworks do with build-time values.
- CI/CD secrets with OIDC for pipelines that build outside Netlify.
- Service tokens and crunu run.
- Masked CI variables still leak.
Frequently asked questions
Something else? Write to support@crunu.com
Are Netlify build variables available to functions at runtime?
Only variables set through the Netlify UI, CLI or API with a scope that includes Functions. Variables in netlify.toml are build-only, and a value exported inside the build command doesn't become a function variable.
Do I need to redeploy after changing a Netlify environment variable?
Yes. Each deploy uses the values that were set when it was created. Create a new deploy to apply updated values to functions and builds.
Can crunu run inside Netlify Functions?
No. Functions run on Netlify's serverless platform, where you can't run the crunu agent or wrap the function process. Function values come from Netlify's own store. Use crunu run around the build and for local development.
How do I keep a Crunu service token away from my functions?
Give the CRUNU_TOKEN variable only the Builds scope and flag it as containing secret values. Scopes are available on Pro and Enterprise plans. On other plans, a variable applies to all scopes, including functions.
Sources
Every source this page relies on, with the date we last checked it.
- Netlify Docs: Environment variables overview · checked October 30, 2026
- Netlify Docs: Build environment variables · checked October 30, 2026
- Netlify Docs: Environment variables and functions · checked October 30, 2026
- Netlify Docs: Secrets Controller · checked October 30, 2026