# How to Set Up a Staging Environment
Tests catch a lot, but not everything. A staging environment — a production-like deployment where you validate changes before they reach users — is where you catch the bugs that only appear under real configuration, real integrations, and real-ish data. Here's how to set one up that's actually useful.
What staging is for
Staging exists to answer one question: *will this work in production?* It should be close enough to production that a passing test in staging gives you real confidence. Specifically, staging is where you validate:
- Configuration and environment variables (the stuff that's never in unit tests).
- Real integrations — payment providers (in test mode), email, third-party APIs.
- Database migrations against a realistic schema and data shape.
- Deploy mechanics themselves — does the build and release actually work end to end?
The cardinal rule: dev/prod parity
The [Twelve-Factor App](https://12factor.net/dev-prod-parity) makes this explicit: keep your environments as similar as possible. A staging environment that runs a different runtime version, a different database, or a different deploy process tests a system you're not actually shipping. The closer staging is to production, the more its results mean.
| Dimension | Should match production? |
|---|---|
| Runtime/language version | Yes |
| Database engine + version | Yes |
| Deploy process | Yes (identical pipeline) |
| Infrastructure shape | Yes (smaller scale is fine) |
| Data volume | No (smaller is fine) |
| Secrets/keys | No (use test/sandbox) |
| Resource sizing | No (can be smaller) |
The pattern: same *kinds* of things, smaller *scale*, *non-production* secrets and data.
Step 1: Replicate the deploy pipeline
Staging should deploy through the exact same mechanism as production. If production builds a container and deploys via Helm, staging does too. The only differences should be values: which branch deploys, which database it points at, which secrets it uses. A common setup is to deploy main (or a staging branch) to staging automatically, and promote to production after staging validation.
Step 2: Use a separate database — never production
Staging must have its own database. Pointing staging at production data is dangerous: a buggy migration or a test that writes garbage will corrupt real user data. Options for staging data:
- Seeded fixtures — fast, safe, but may not reflect real-world edge cases.
- Anonymized production snapshot — realistic data shape with PII scrubbed. The gold standard for catching data-dependent bugs.
- Empty + generate — fine for early-stage apps.
Whatever you choose, scrub PII. Copying raw production data into a less-secured staging environment is both a privacy risk and often a compliance violation.
Step 3: Use sandbox secrets
Staging needs API keys, but giving it live credentials means staging can charge real cards or send real emails to customers. Use test/sandbox keys everywhere:
# Production
STRIPE_SECRET_KEY=sk_live_...
SENDGRID_API_KEY=<live>
# Staging
STRIPE_SECRET_KEY=sk_test_...
SENDGRID_API_KEY=<sandbox or restricted>Step 4: Protect access
Staging often has weaker data but the same code paths, making it a tempting target. At minimum, put it behind authentication (basic auth, SSO, or IP allowlist) so it's not publicly indexable and crawlable. Don't let staging leak the existence of unreleased features.
Staging vs. preview environments
These are related but distinct, and teams increasingly use both.
| Staging | Preview | |
|---|---|---|
| Lifespan | Long-lived, shared | Ephemeral, per-PR |
| Tracks | A branch (e.g. main) | A specific pull request |
| Purpose | Final pre-prod validation | Review individual changes |
| Data | Stable, seeded/anonymized | Isolated, disposable |
Previews catch problems per-change during review; staging is the final gate before production. Many teams run preview environments for PRs and a single staging environment for integration validation.
Setting up staging on PandaStack
Because PandaStack deploys from your Git repo through one consistent pipeline (BuildKit → Artifact Registry → Helm), getting dev/prod parity is straightforward: create a separate staging service (or a staging-branch deployment) that uses the same build and the same deploy mechanics as production, differing only in its environment variables and database.
For staging data, provision a separate managed database — PandaStack supports PostgreSQL, MySQL, MongoDB, and Redis, and auto-injects DATABASE_URL so your staging app wires up the same way production does, just pointed at a different instance. Set sandbox API keys via per-app environment variables, and use the platform's teams/orgs and RBAC (owner/admin/member) plus custom domains to keep staging access controlled. The result is a staging environment that genuinely mirrors production because it's built and deployed by the same machinery.
Checklist
- ✅ Same runtime, database engine, and deploy pipeline as production
- ✅ Separate database, never production data
- ✅ PII scrubbed from any copied data
- ✅ Sandbox/test API keys, not live
- ✅ Access protected (auth/SSO/IP allowlist)
- ✅ Migrations validated here before production
References
- [The Twelve-Factor App: Dev/prod parity](https://12factor.net/dev-prod-parity)
- [Google SRE Book: Testing for Reliability](https://sre.google/sre-book/testing-reliability/)
- [Stripe: Test mode](https://docs.stripe.com/test-mode)
- [OWASP: Secrets Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html)
Spin up a production-mirroring staging environment on PandaStack with its own managed database — start free at [dashboard.pandastack.io](https://dashboard.pandastack.io).