Adding staging
Staging is optional. It's a pre-production copy of your project, with its own Workers, bucket,
content database, pool and Postgres, deployed from a staging branch so you can check a promotion
before it reaches production. Add it when you want that rehearsal, or skip this page and promote
development straight to main. Skipping costs nothing: only a push to a staging branch deploys
staging, so nothing reads its value files until one exists.
flowchart LR
L["1. Local setup<br/>Both apps on your machine"] --> D["2. Deploying development<br/>A push deploys"]
L --> R["3. Adding pull request previews<br/>Independent of development"]
D --> S["**4. Adding staging**<br/>Optional"]
S --> P["5. Going to production<br/>Your domain, real users"]
D -. "without staging" .-> P
Prerequisites
| You need | How to get it | Required |
|---|---|---|
| Deploying development finished | development deploying | Yes |
| A Postgres database for staging | Any managed Postgres reachable from the internet, never one another context uses | Yes |
| Google, GitHub, and Polar credentials for staging | Set up sign-in and payments — a separate client per context | Only for the feature that reads them |
The repository needs no new secret or variable. The push identity already reads the staging tier,
and the Cloudflare token already creates every resource type staging binds.
Add the context
1. Fill staging's Secret Tier
staging reads the staging environment at / in apps/web's Infisical project. Store the
connection string there:
infisical secrets set DATABASE_URL='<the staging connection string>' \
--env=staging --path=/ --projectId='<apps/web project id>'
Then add staging's own value for every key that
Set up your secret store put in /local. A feature you're
skipping takes a stand-in, as it does locally.
2. Write staging's values
Staging's origins take the same shape as development's, with its own context in them. Its client IDs and contact address ship empty too:
NUXT_PUBLIC_APP_BASE_URL=https://<slug>-web-staging.<subdomain>.workers.dev
NUXT_PUBLIC_APP_WEBSITE_BASE_URL=https://<slug>-site-staging.<subdomain>.workers.dev
GOOGLE_CLIENT_ID=<staging's Google client ID>
GITHUB_CLIENT_ID=<staging's GitHub client ID>
NUXT_PUBLIC_APP_BASE_URL=https://<slug>-site-staging.<subdomain>.workers.dev
NUXT_PUBLIC_CONTACT_EMAIL=hello@example.com
Commit both on development and push.
3. Create the staging branch
From here promotion runs development → staging → main. Create the middle branch:
git checkout -b staging development
git push -u origin staging
That first push creates staging's Workers, bucket, content database and pool, each named
<slug>-<app>-staging, and deploys both applications. Every later push to staging deploys it
again. Nothing enforces the order — the gate reads only the branch pushed — so promote by merging
forward.
4. Onboard staging's sending domains
Staging sends from its own hostnames, <slug>-<app>-staging.<zone>. Onboard each one the way
Email will not send until you onboard a domain
walks it, reading the hostname with APP_RUNTIME_CONTEXT=staging.
A red run reads the same way it did for development:
When it does not work lists the causes.
Next steps
- Going to production — the last stage, promoted
through
staging, once the application and its name are final - Resources — what staging's first push created, and how to delete a context's resources if you drop it later
