Guides

Environment variables and secrets

What Launchpad injects, how to add your own, and how the three environments differ.

Your app reads its database URL, API keys and settings from environment variables. Launchpad fills in the ones that come from services it created, and you add the rest.

What Launchpad injects

Each service contributes variables under the names frameworks expect, for example DATABASE_URL from the database and RESEND_API_KEY from email. The full list is in Providers and credentials, and every deploy prints the names it injected.

You never paste these yourself, and they're updated when something changes, such as a rotated key.

Adding your own

In the console, open a project and use Environment variables. From the terminal:

launchpad secrets set STRIPE_SECRET_KEY=sk_live_… --env production
printf %s "$TOKEN" | launchpad secrets set GITHUB_TOKEN   # piped: never in shell history
launchpad secrets generate SESSION_SECRET                 # random, 32 bytes
launchpad secrets list                                    # names only, values hidden

A value you set wins over a service's value of the same name, so you can override EMAIL_FROM, for example, and keep the rest.

The three environments

EnvironmentUsed by
productionYour live app, and every process (workers, cron, APIs)
previewVercel's preview deployments, one per pull request, listed on the project's page
developmentLocal work, through launchpad env --dotenv

A secret set with no environment goes to all three. In the console, a project's Environment variables section shows which variables differ between them (names only, never values).

Previews get their own database

Every pull request's preview is a real copy of your app, and people click around in it: signing up, adding items, deleting things. If it ran against your production database, all of that would land in your live data.

So on Neon, previews and local development get their own copy of the database: a Neon branch called launchpad-preview, made from production's data at the moment it's created. Production's DATABASE_URL points at your real database; preview and development get the copy's. Nothing done in a preview reaches production, and branches are copy-on-write, so the copy costs almost nothing until it changes.

  • The copy is made once, on the first deploy, and doesn't follow production after that. To start it again from production's current data, reset the branch from its parent in the Neon console.
  • It holds a copy of production's data, including your users' details. Keep previews private (Vercel asks visitors to sign in by default) and don't share preview links outside your team.
  • To give previews production's database instead, use the switch in the console's Environment variables section, or set "previews": "shared" under services.database. The copy is deleted.
  • On Supabase, previews use the production database: Supabase's branching is a paid add-on that Launchpad doesn't set up. The console says so on the project's page.

Payment keys in previews

Previews also get production's payment keys unless you set others for them. With live Razorpay or Stripe keys, a test checkout in a preview charges a real card, and the console warns you. Add your test-mode keys under the same names, ticking only preview and development: they replace the live ones there and production keeps the live keys.

Looking at one environment

To see what one environment holds:

launchpad env --env preview            # names and values for that environment
launchpad env --dotenv > .env.local    # development values, for running locally

Limiting what a part can see

By default every part of the app sees every variable. To narrow that, list what a part needs under its secrets in launchpad.json:

"hosting": {
  "secrets": ["NEXT_PUBLIC_SUPABASE_URL", "NEXT_PUBLIC_SUPABASE_ANON_KEY"]
}

A worker that only writes to the database then never receives your Stripe key.

Values that refer to other values

Inside launchpad.json, env entries can refer to a secret or to another part of the app:

"hosting": { "env": { "NEXT_PUBLIC_API_URL": "${processes.api.url}" } },
"processes": [{ "name": "api", "env": { "SECRET_KEY": "${env.APP_SECRET}" } }]

That's how the web app learns its API's address without you copying it after each deploy.

Removing one

Remove a secret, or take a name out of a part's secrets list, and the next deploy deletes it from Vercel and Render. Launchpad only ever deletes variables it set itself: anything you added by hand in a provider's dashboard is left alone.

Rules of thumb

  • Anything starting NEXT_PUBLIC_, VITE_ or PUBLIC_ is visible in the browser. Never put a secret key behind those prefixes.
  • Use test keys while you build, and switch to live keys in production only. Previews get your test keys as long as you set them for preview and development.
  • After adding or changing a variable, deploy again: a running app keeps the values it started with.

Something missing or wrong? Edit this page on GitHub or email hello@axonscape.com.