Docs · Guides

Environment variables & secrets.

Configuration lives outside your code. Set variables per project in the dashboard; they’re present in your build and injected into your running app — encrypted at rest either way.

Setting variables

Open your project’s Env Vars tab and add key/value pairs. To migrate an existing app, paste your whole .env file into the bulk editor — it parses the standard format:

DATABASE_URL=postgres://…
STRIPE_SECRET_KEY=sk_live_…
RESEND_API_KEY=re_…

Changes apply on the next deploy — redeploy after editing so the build and the running process both see the new values.

Sensitive variables

Mark a variable sensitive and its value becomes write-only: it still reaches your app normally, but the dashboard never displays it again. Use this for API keys, tokens, and anything you wouldn’t want visible over a shoulder.

Scopes

Each variable has a scope that decides which deploys and phases receive it:

  • production: production deploys only (the build, the release command and the running app).
  • preview: pull-request previews only.
  • all: production and previews.
  • runtime only: the running app on production deploys. Never a build, the release command or a preview. Use it for secrets your build has no business seeing, such as a payment provider key.

Build time vs. runtime

Variables are present during the build and at runtime, except those scoped runtime only. The distinction that matters is your framework’s browser-exposure rule:

  • Next.js — browser code only sees NEXT_PUBLIC_*, baked at build time.
  • Vite — browser code only sees VITE_* via import.meta.env, baked at build time.
  • Servers (Node, Python, Go, …) — read anything from the environment at runtime; no prefix rules.

Variables Canner sets for you

  • PORT — the port your server must listen on.
  • DATABASE_URL — set automatically when you provision a Postgres database.
  • NODE_ENV=production for Node builds and runtimes.

Previews get only the variables scoped all or preview, but they share the project’s database: a preview runs a pull request’s code against the production database. If a preview needs different values (a staging API, say), or the project holds live credentials, split it into its own project. The simple arrangement is one project for production with Pull-request previews turned off (project settings, or previews_enabled: false in the API), with its own database and variables, and a staging project, which may be connected to the same repository, that keeps previews on with sandbox credentials and a staging database. Turning previews off stops new pull-request previews and branch deploys; you can also remove the ones already running.