Manual Staging Setup
Create a staging environment manually — CI/CD foundations.
Focus: create a staging environment manually
Have you ever deployed a change directly to production because there was no staging environment, and then watched a preventable bug ruin your users' day? Or spent an entire afternoon manually configuring servers only to discover your staging environment drifts so far from production that it's useless? You are not alone — this is the exact pain this lesson solves. By the end, you will be able to create a staging environment manually, understand the mental model behind environment parity, and confidently configure a staging sandbox that mirrors production — even before you automate it with pipelines.
The problem this lesson solves
Without a staging environment, your team is flying blind. Every commit becomes a gamble: will it work in production or will it crash at 2 AM? The lack of a safe, production-like testing ground leads to fear-based deployments, delayed releases, and costly rollbacks.
When teams manually assemble a staging environment, they often fall into two traps: configuration drift and environment inconsistency. A developer might say, "It worked on my machine," but staging never matches production, so bugs slip through. Even worse, without a defined process, each engineer creates staging differently — one uses a Docker Compose file, another spins up a cloud VM, and a third just points at the production database (a cardinal sin).
This lesson gives you a systematic, repeatable approach to manually create a staging environment that is small enough to understand, but faithful enough to catch real bugs. You will learn to control variables, document your steps, and lay the groundwork for the automated pipelines you will build later in this track.
Core concept / mental model
Think of a staging environment as a flight simulator — a full-scale replica of the cockpit (your production stack) minus the risk of a real takeoff. It must behave like production, but it exists solely for safe practice.
Definitions
- Staging environment: A mirror of production that runs your application with production-like configuration, dependencies, and data — but isolated from real users.
- Production parity: The degree to which staging matches production in terms of OS, runtime versions, environment variables, database schema, and network topology.
- Manual creation: The process of preparing this environment by hand, using scripts and checklists, as opposed to a fully automated pipeline.
The mental model rests on three pillars: parity, isolation, and reproducibility. Parity ensures what you test is what you ship. Isolation guarantees your experiments do not corrupt production data. Reproducibility means you can spin up the same environment tomorrow, next week, or on a colleague's laptop.
Pro tip: Visualize staging as a stack of layers — infrastructure, dependencies, application code, and data. Each layer must match production, or the test is worthless.
How it works step by step
Creating a staging environment manually is a disciplined sequence. Follow this order to avoid chaos:
- Model production: Inventory your production stack — OS, runtime, database, web server, environment variables, and external services. Write this down.
- Choose isolation: Decide if staging lives on a separate VM, container, or cloud project. It must never share a database with production.
- Provision infrastructure: Create the base environment (e.g.,
docker-compose.ymlor a cloud VM). - Install dependencies: Pin versions of every tool and library — use lock files, Docker tags, or configuration management.
- Configure the app: Set environment variables for the staging context (e.g., different API keys, a smaller database).
- Load a data snapshot: Copy a sanitized subset of production data (or synthetic data) to test real-world scenarios.
- Verify parity: Run a checklist — versions match, endpoints respond, background jobs work, logs are visible.
- Document everything: Write a README or runbook so anyone can recreate it.
Each step feeds into the next. Skipping #7, for instance, means you might test against an environment that is subtly broken — and deploy a bug.
Hands-on walkthrough
Let us create a staging environment for a simple Flask app with a Postgres database, using Docker Compose — a manual approach that is still reproducible.
Step 1: Model production
Assume production runs on Ubuntu 22.04, Python 3.11, and PostgreSQL 14. Your requirements.txt is known. Capture that in the project.
Step 2: Write the Docker Compose file
Create docker-compose.staging.yml:
version: '3.8'
services:
web:
build: .
environment:
- FLASK_ENV=staging
- DATABASE_URL=postgresql://staging_user:staging_pass@db:5432/staging_db
ports:
- "5000:5000"
depends_on:
- db
db:
image: postgres:14
environment:
- POSTGRES_USER=staging_user
- POSTGRES_PASSWORD=staging_pass
- POSTGRES_DB=staging_db
volumes:
- staging_db_data:/var/lib/postgresql/data
volumes:
staging_db_data:
Note we pin the Postgres version (postgres:14) to match production — a crucial parity decision.
Step 3: Provide a data snapshot
Create seed.sql with a few rows of realistic data:
INSERT INTO users (name, email) VALUES ('Test User', 'test@example.com');
INSERT INTO products (name, price) VALUES ('Sample Widget', 9.99);
Mount it into the database container (add to db service):
volumes:
- ./seed.sql:/docker-entrypoint-initdb.d/seed.sql
Step 4: Build and run
Run the commands:
# Build the app image
docker compose -f docker-compose.staging.yml build
# Start the staging stack
docker compose -f docker-compose.staging.yml up -d
# Verify the web app responds
curl http://localhost:5000/health
Expected output:
{"status":"ok"}
Step 5: Verify parity
Check the app logs for your staging flag:
docker compose -f docker-compose.staging.yml logs web | grep "FLASK_ENV"
You have now manually created a staging environment — it is isolated, reproducible, and close to production.
Compare options / when to choose what
Manual creation is not the only way. Here is how it stacks against other strategies:
| Approach | Pros | Cons | Best for |
|---|---|---|---|
| Manual (this lesson) | Full control, teaches fundamentals | Slow, error-prone at scale | Small teams, learning, one-off scenarios |
| Infrastructure-as-Code (Terraform) | Reproducible, versioned | Upfront learning curve | Teams managing many environments |
| Container orchestration (K8s) | Scales, production-like | Complex to operate | Larger microservice deployments |
| Ephemeral previews (Vercel/Netlify) | Instant per-PR | Limited to certain platforms | Frontend-only or serverless apps |
Choose manual when you need to debug deeply or the environment is simple. Switch to automation when the staging setup harms developer velocity.
Pro tip: Even if you later automate, keep the manual steps as a runbook — it will serve as the specification for your pipeline.
Troubleshooting & edge cases
Every manual setup hits snags. Here are the most common:
Port conflicts
Error: Bind for 0.0.0.0:5432 failed: port is already allocated — another service uses the database port. Fix: Change the host port in the compose file (e.g., "5433:5432") or stop the conflicting process.
Version mismatch
Symptom: App runs but SQL syntax errors appear — the staging database is older (e.g., Postgres 12) than production (14). Fix: Always pin the same version, and verify with SELECT version();.
Missing environment variables
Error: KeyError: 'DATABASE_URL' — your app cannot connect. Fix: Check the container environment via docker compose exec web env, and ensure your .env.staging is loaded.
Permission issues on data volumes
Symptom: Database fails to initialize due to file permissions. Fix: Set the volume path ownership or use a named volume (as in our example) to avoid host-folder permission problems.
Configuration drift over time
Symptom: Staging works today, but breaks next month because packages auto-updated. Fix: Re-run your setup script after any production change, and document a weekly parity check.
What you learned & what's next
You now understand why creating a staging environment manually is a foundational CI/CD skill. You can explain the core concept of environment parity, apply the step-by-step process to spin up your own staging stack, and troubleshoot common pitfalls — all without code automation.
This manual foundation makes the next lessons in the CI/CD foundations track easier. Next, you will learn how to automate this manual process within a CI/CD pipeline, turning your runbook into a repeatable, on-demand staging environment that triggers on every pull request. You will take the same Docker Compose file and wrap it in a GitHub Actions workflow, so that your team can test every change in a production-like sandbox — automatically.
Before you go, run a quick check: can you reproduce this lesson's staging environment from scratch on a fresh machine? If yes, you have mastered the art of manual staging setup.
Practice recap
To cement your skills, take the Docker Compose file from this lesson and extend it — add a Redis service and a background worker. Then introduce a deliberate bug (e.g., change the Postgres version) and practice verifying parity to catch it. Finally, write your own runbook documenting each step you took, ready for the next lesson on automating this with CI/CD pipelines.
Common mistakes
- Pointing your staging environment at the production database to get realistic data — this is dangerous and can corrupt production data; always use a sanitized snapshot.
- Forgetting to pin dependency versions in Docker images or package files, causing staging and production to drift whenever a package releases.
- Skipping the parity verification step — assuming staging works just because the containers started, without testing endpoints, jobs, or schema.
- Hardcoding environment variables for staging directly in code or deploy scripts, which breaks reproducibility and risks leaking secrets.
- Not documenting the manual steps, so weeks later nobody (including you) can recreate the same staging setup.
Variations
- Use a cloud VM (e.g., AWS EC2) with a shell script instead of Docker Compose to mimic production infrastructure more closely.
- Adopt Infrastructure-as-Code tools like Terraform or Ansible to describe your staging stack declaratively, making it versionable and reproducible.
- Use container orchestration platforms like Kubernetes with a separate namespace for staging if you need scaling or production parity in a microservices world.
Real-world use cases
- A small startup manually configures a staging VM to test new features against realistic data before every release, catching regressions early.
- A frontend team creates a staging preview environment with a mock API to share design updates with stakeholders without touching shared services.
- A compliance-driven company sets up a manually isolated staging network to validate security updates and data migrations before applying them to production.
Key takeaways
- A staging environment mirrors production but stays isolated, catching bugs before they reach real users.
- Manual staging creation follows a disciplined sequence: model production, provision, configure, load data, and verify parity.
- Pinning versions and documenting every step ensures reproducibility and avoids configuration drift.
- Docker Compose is a quick, effective way to create a local staging environment for simple stacks.
- Choose manual creation for control and learning; automate when scale or speed demands it.
- Troubleshooting staging is about checking versions, ports, environment variables, and permissions systematically.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.