Skip to main content
Percher is still being built and account creation is paused — get notified when it opens.

Environment variables

Set, unset, list, encryption

Ask your agent
Set up the secret keys and settings my app needs.Read the guide at percher.app/docs/env-vars
Make one of my settings available when the app is built.Read the guide at percher.app/docs/env-vars
For agents and developers

Env vars are encrypted at rest (AES-256-GCM) and injected at container startup. Changes take effect on the next deploy — run percher publish to apply immediately.

bunx percher env set STRIPE_KEY=sk_live_...
bunx percher env set --from-env-file .env   # every KEY=VALUE line at once
bunx percher env list          # values are masked
bunx percher env unset STRIPE_KEY

--from-env-file sets every KEY=VALUE line from a .env-format file in one call (comments, quotes, and export prefixes handled; the file is only read when you pass the flag). The dashboard env editor has the same flow — "Paste a .env file" previews what will be set, with conflict badges, before anything is saved.

Deploy scopes: unscoped vars (the default) apply everywhere. --scope preview applies a var only to preview deploys and overrides the unscoped value there — --scope live does the reverse. Point previews at a staging database with percher env set DATABASE_URL=... --scope preview and live keeps its own value. Unset is scope-specific, and the env-requirement gate resolves against the deploy type, so a preview-only key never satisfies a live deploy. Three keys can't be scoped: SITE_PASSWORD (the login gate reads one password per app) and the PocketBase admin credentials (platform-managed).

Build-time exposure: keys matching a public prefix (NEXT_PUBLIC_*, VITE_*, …) are forwarded to the build automatically and baked into the client bundle — the env editor marks them "build + runtime". Everything else is only available at runtime, which is correct for secrets but breaks frameworks that expect a non-prefix value at build time. Opt those in per-key via [build] pass_env in percher.toml:

# percher.toml
[build]
pass_env = [
  "NEXT_PUBLIC_API_URL",
  "VITE_POCKETBASE_URL",
  "EXPO_PUBLIC_API_URL",
]

# values still come from the env store, not the TOML
bunx percher env set NEXT_PUBLIC_API_URL=https://api.example.com

For Dockerfile-based projects (runtime = "docker") the listed keys are forwarded as --build-arg; declare ARG NEXT_PUBLIC_API_URL in your Dockerfile to read them.

Outbound HTTPS (calling external APIs from your app): Apps run on an internal Docker network and reach the public internet through a forward HTTP proxy that Percher injects automatically:

HTTP_PROXY=http://egress-proxy:8888
HTTPS_PROXY=http://egress-proxy:8888
NO_PROXY=localhost,127.0.0.1,.local
NODE_USE_ENV_PROXY=1

Bun and Node 22.21+ / 24+ use the proxy automatically — fetch("https://api.exa.ai/...") just works. Python (requests/httpx/urllib) and Go (http.DefaultTransport) also honor HTTPS_PROXY natively. For Node 20 (and the 21 / 23 lines) wire up undici manually at app startup:

import { ProxyAgent, setGlobalDispatcher } from "undici";
if (process.env.HTTPS_PROXY) {
  setGlobalDispatcher(new ProxyAgent(process.env.HTTPS_PROXY));
}

Direct outbound is blocked by design — apps can't scan internal hosts or hit cloud metadata. If a smoke probe sees ENETUNREACH on 1.1.1.1:443, that's the symptom of the runtime not using the proxy, not a missing-egress problem.