Platform
Hosting & deploysPush to a branch, get a live linkDatabaseReal Postgres, one per appSign-inEmail and Google, users includedFile storageUploads, photos and documentsAI gatewayEvery model, with a hard spend cap
Resources
DocsGuides written for people who are newFor AI agentsClaude Code, Cursor, MCP, JSON CLICompareCamplax beside the stack it replacesPricingFrom $25/month plus prepaid creditsSupportA person answers, not a queue botPoliciesTerms, privacy and how we handle data Get started
Open in ChatGPT Open in Claude Last updated 2026-11-07

Environment variables and secrets

Environment variables per environment — production, preview and development — with branch overlays, sealed secrets, and the keys Camplax injects for you.

Every project has a vault for environment variables — named values your app reads at runtime from process.env: API keys, feature flags, a payment provider's secret. Values live in the vault, never in your repo, and reach the running app as environment variables — your code cannot tell the difference between a value you set and one Camplax injected.

Three environments, plus branch overlays

Each key can exist once per environment — and, inside preview, once per git branch:

LayerWho reads it
productionThe live app at your-app.camplax.app and your custom domains
previewEvery branch preview — production values never reach a preview
preview + branchOne specific branch's preview, overlaid on preview
developmentcamplax dev and camplax env pull on your laptop, overlaid on production

The merge order, with later layers winning:

  • Production: production.
  • Preview: preview → that branch's overlay. A preview never inherits a production value: set what previews need under preview.
  • Development: production → development.

So a live STRIPE_KEY in production, a test key under preview, and a different test key under preview + branch feature/billing gives you three apps on three sets of credentials without a single if in your code.

Production and Preview never share a sealed value

A preview runs branch code nobody has reviewed yet, on a public URL. A preview never reads a Production value, so it only gets one if someone types it in. The easy slip — choosing Production and Preview for one sealed value, or importing one .env into both — is refused with 409 same_as_production (an import leaves that name out and names it), naming the variable and branch, never the value. Give Preview its own test value instead. A value that is not sealed (a site name, a public key) may be shared; you get a warning.

Camplax only compares the values you send in that one request, never one you send with what is already stored: a sealed value cannot be read back, and an answer like "that matches Production" would let anyone who can set Preview values test guesses of it. The variable list flags a sealed Production value that older (pre-cell) previews inherit because Preview has no value of its own.

Setting secrets

In the console: open Settings → Secrets. Add a key, pick the environments it applies to, optionally scope a preview value to a branch, and choose whether it is sensitive. Paste a whole .env file into the import box to load many at once — existing keys are updated, not duplicated.

From the CLI:

camplax env pull      # vault → local .env
camplax env push      # local .env → vault

env pull writes the development view — your development keys over any non-sensitive production keys, with managed names like DATABASE_URL left out. It is the one read that can reveal values, so it is owner-only; project keys cannot call it. env push uploads KEY=value lines to both the production and development layers, then restarts the app so the new values are live. It only accepts UPPER_SNAKE_CASE keys, skips empty values, and refuses keys Camplax manages — DATABASE_URL, DIRECT_URL, BETTER_AUTH_SECRET, R2_PREFIX, AUTH_PROVIDERS, AUTH_CLAIMS, PORT and anything starting CAMPLAX_. When camplax.json lists env names, push sends only those keys.

What the build can see

Your running app gets every key. The build — install and build scripts, including every package in your dependency tree — gets only what building needs, so a compromised package cannot read your database password or private API keys:

  • keys starting NEXT_PUBLIC_, VITE_, PUBLIC_, NUXT_PUBLIC_, EXPO_PUBLIC_ or REACT_APP_ — your framework puts these in the browser bundle anyway;
  • registry and tool settings: NPM_TOKEN, NODE_AUTH_TOKEN, NPM_CONFIG_*, YARN_*, NODE_ENV, NODE_OPTIONS;
  • your app's URL, slug and environment, the deploy id, and the commit and branch being built.

If a build really needs another key — a CMS token for static pages or a Sentry token for source maps — open its ⋯ menu under Settings → Environment variables and choose Use in builds. The row then says "used in builds", and every build from the next deploy on gets it.

Camplax's own database and sign-in secret have no row there. For pages built from data, add an env var named CAMPLAX_BUILD_ENV listing the names, for example DATABASE_URL (the same list also works for any other name). With DATABASE_URL listed, every build gets its own environment's database: production builds get production's, and preview builds get the preview's. A preview build never gets the production database or sign-in secret.

A build also cannot connect to your database unless it was given DATABASE_URL this way: the network rules around your app turn build machines away at the database.

Migrations do not belong in the build: the build runs before the new version is known to work. Put them in build.release in camplax.json instead (see How the build runs). The release command always gets the environment's database.

Applying without a rebuild

Saving a secret does not bounce the app by itself. Ask for apply on the write (the console's Restart the app when saved switch), or run it explicitly:

POST /v1/projects/:slug/settings/secrets/apply

This pushes the current vault into the running app's environment and restarts it — a few seconds of restart, not a rebuild. camplax env push does this for you at the end. A preview value saved this way goes to every running preview at once (only the matching branch's preview for a branch value).

With the switch off, the value waits for the next deploy: every deploy compares the environment's vault with what the app was last given and, if anything changed, hands the app the new set before it starts. A deploy with nothing changed costs no extra restart. Previews work the same way on their next push. If the app has not been deployed yet, the values are simply saved for the first deploy. One guarantee worth knowing: deleting a key in the vault really does remove it from the process — it does not linger as a stale value.

Rotating the secrets Camplax manages

You never create or rotate your app's sign-in secret or database password — but if you think one leaked, rotate it from its ⋯ menu under Settings → Environment variables (owners only), or:

POST /v1/projects/:slug/settings/secrets/rotate   { "target": "auth_secret" }
POST /v1/projects/:slug/settings/secrets/rotate   { "target": "database_password" }
  • Authentication secret (BETTER_AUTH_SECRET, which signs your users' sessions). Camplax publishes a new secret next to the old ones (BETTER_AUTH_SECRETS, newest first) and keeps BETTER_AUTH_SECRET as it was. Better Auth signs with the newest and still opens everything the older ones protect — two-factor secrets, backup codes, token signing keys — so nobody loses their second factor. Session cookies are checked against the newest secret only, so everyone signed in to the app signs in again. This needs Better Auth 1.4 or later, which the Camplax starters use; code that reads BETTER_AUTH_SECRET (or AUTH_SECRET / NEXTAUTH_SECRET) itself keeps the old value. A second rotation within two minutes is refused.
  • Database password. Postgres keeps one password per user, so this one cannot overlap: Camplax sets new passwords for your app's database user (and the one the console uses) inside the cell, never seeing them, and restarts the app so it connects with the new one. The old password stops working straight away.

Both are recorded in the project's activity log.

A restart does not reach values your frontend reads in the browser — names starting NEXT_PUBLIC_, VITE_, PUBLIC_, NUXT_PUBLIC_, REACT_APP_, EXPO_PUBLIC_ or GATSBY_. The bundler copies them into the JavaScript at build time, so pages keep the old value until the next deploy. When you change one in the console, it says so and offers Redeploy production.

The keys Camplax injects

Some environment variables are set by the platform on every deploy, and they always win — a vault entry with the same name is ignored. These are the ones your code will actually use:

VariableWhat it carries
DATABASE_URLPooled connection string for your project's database
DIRECT_URLDirect (unpooled) connection string — for migrations and admin tools
BETTER_AUTH_SECRETThe sign-in secret your app's auth is built with
CAMPLAX_URLThis deployment's own URL — https://your-app.camplax.app or the preview host
CAMPLAX_API_URLThe Camplax API base URL
CAMPLAX_PROJECT_ID / CAMPLAX_PROJECT_SLUGWhich project this app is
CAMPLAX_ENVIRONMENTproduction or preview
NEXT_PUBLIC_CAMPLAX_URL / VITE_CAMPLAX_URL / PUBLIC_CAMPLAX_URLThe same URL as CAMPLAX_URL, under the names browser code can read, so a preview's pages call the preview and not production
NEXT_DEPLOYMENT_ID / NEXT_SERVER_ACTIONS_ENCRYPTION_KEYNext.js version-skew protection, set at build time: after a deploy, an open tab reloads instead of failing to load code, and a form opened before the deploy still submits. The Server Actions key stays the same for each environment; set your own and it is kept
CAMPLAX_GIT_COMMIT / CAMPLAX_GIT_BRANCH / CAMPLAX_DEPLOY_IDExactly which code this is
CAMPLAX_ANALYTICS_KEYThe key your app sends analytics and errors with
CAMPLAX_PROJECT_TOKENYour app's own key to Camplax — AI, file storage, payments and realtime publishing, with nothing to paste. Previews get their own, limited to AI and that preview's own files and realtime channels; camplax dev gets one with AI and a storage sandbox of yours. CAMPLAX_AI_API_KEY carries the same value for older code
CAMPLAX_LOG_INGEST / CAMPLAX_LOG_INGEST_TOKENWhere and how the runtime ships its logs
R2_PREFIXThis environment's storage prefix
PORTThe port your server must listen on. Read it from the environment; don't hard-code a number

A few more appear situationally — CAMPLAX_REALTIME_SESSION_SECRET (the key your app mints realtime user tokens with — each environment gets its own), CAMPLAX_REALTIME_TOKEN on older production apps, CAMPLAX_PREVIEW_GATE_SHA256 or CAMPLAX_PREVIEW_GATE_KEY on locked previews, CAMPLAX_TURNSTILE_SITE_KEY on legacy pre-cell apps with their old bot check on, AUTH_PROVIDERS and AUTH_CLAIMS for sign-in configuration. Because the platform owns them, they cannot be set or edited in the vault — trying returns a forbidden error.

To match what hosted templates expect, Camplax also fills the usual framework aliases only when you did not set them: POSTGRES_URL from DATABASE_URL, AUTH_SECRET and NEXTAUTH_SECRET from BETTER_AUTH_SECRET, AUTH_URL and NEXTAUTH_URL from CAMPLAX_URL.

Write-only, on purpose

A production or preview secret is sealed by default: the console shows it as dots, and it cannot be read back — not by the list endpoint, not by env pull, not by the reveal call. To change a sealed value you replace it. This is the same contract as Vercel's sensitive variables:

  • Sealing is one-way. A sensitive variable cannot be made readable again; replace the value instead.
  • Development is the exception. Development values are always readable, which is what lets env pull round-trip a working .env.
  • Reveal exists but is narrow. GET /settings/secrets/:key/reveal returns a value only for a non-sensitive secret you set, only for owner and member roles, and every reveal is written to the project's activity log. Managed values can never be revealed.
  • MCP and project keys can list secret names and presence — never values.

The API

All under /v1/projects/:slug/settings, authenticated with a console session, a CLI token, or a project key holding env:read (reads) or env:write (writes):

MethodPathWhat it does
GET/secretsList keys — metadata only, never values
GET/secrets/for-buildWhich key names exist — for build and log surfaces
POST/secretsSet a key: { key, value, environments, branch, sensitive, apply }
POST/secrets/importBulk-upsert a pasted .env body
POST/secrets/applyPush the vault onto the running app and its previews, and restart them
POST/secrets/rotateRotate the app's sign-in secret or database password: { target } (owners, signed in)
POST/secrets/redactScrub known values out of a string — used on logs
PATCH/secrets/:keyReplace a value or change sensitivity — ?environment= / ?branch= select the layer
GET/secrets/:key/revealRead back a non-sensitive value (owner/member, logged)
DELETE/secrets/:keyDelete a key from one layer

How values are stored

Every value is encrypted before it is stored (AES-256-GCM), and each one is bound to where it lives — its project, environment, branch and name — so a stored value moved anywhere else will not decrypt. The key comes from a Camplax platform secret, never from anything in your project, and each stored value says which key version sealed it so the platform key can be rotated without losing a value. On its way to your app the set travels encrypted again, to the cell your app runs on, and lands in your app's own namespace, which is encrypted at rest too. The platform database holds only names and flags.

When Lee proposes setting a variable or a sign-in provider's client secret, the value waits for your click encrypted to that one proposal, and the record kept afterwards shows the name, never the value.

Camplax

Hosting, database, sign-in and storage in one project. One login, one bill.

Platform
More
Legal
Company
© 2026 Camplax From $25 / month · credits stop at zero