Skip to main content
Percher is getting started, with a limited number of registrations each day. Create an account.

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

How it works, with exact details

Environment variables are how you give your app secrets and settings — API keys, database URLs — without putting them in your code. Percher encrypts them at rest (AES-256-GCM) and injects them when your app starts. 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
Import a whole .env file at once

--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.

Different values for preview and live (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).

Expose a key at build time (NEXT_PUBLIC_*, [build] pass_env)

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

Dockerfile projects keep legacy private build arguments until you migrate; those values can persist in image history and cache. Deploys show a warning. Replace each private ARG/ENV consumer with a secret mount and add the acknowledgement comment after all parser directives, before the first instruction. Keep the keys in pass_env and their values in the env store.

# syntax=docker/dockerfile:1
# percher: build-secrets=mount
FROM node:24-alpine AS build
WORKDIR /app
COPY . .
RUN --mount=type=secret,id=NPM_TOKEN,env=NPM_TOKEN,required=true npm ci

Use each key's name for id and env; mount every consuming step. Never print or save values. Percher leaves your Dockerfile unchanged and does not prove its commands safe. Fresh mounted builds skip Docker layer reuse; identical source and secret values can reuse the complete image cache.

  1. If a token may already have reached old layers or history, rotate or revoke it at its issuer, coordinating with your app's use of it. Set its replacement with bunx percher env set KEY (hidden prompt), using the intended app and deploy scope.
  2. From the edited project, run bunx percher publish (agents: percher_publish). Redeploy reuses uploaded source and does not upload your Dockerfile changes.
  3. Wait for successful deployment completion (agents follow the returned percher_wait_for_deploy recovery). In the dashboard, open this app's Publishes, select Deploys & previews, and expand that deployment. Resolve any build-secret-policy warning in its build problems. Agents can inspect deploy.problems with percher_deploys_inspect. CLI output is only a log tail; unavailable diagnostics or absence of the warning alone do not verify migration.

Old images, caches and history remain; a pinned rollback can reuse a legacy image, so review that target before using it.

Call external APIs from your app (outbound HTTPS)

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.

There is no host allowlist, but proxied HTTPS is port 443 only — a URL with an explicit non-standard port (https://host:8443) gets a 403 straight from the proxy. Plain http:// fetches are unrestricted by port.