Skip to main content

Environment

A CI/CD environment is a named deployment target — such as dev, staging, or production — that tracks deployment history, enforces approval requirements, and holds environment-specific secrets. Environments make promotion workflows explicit and auditable.

Understanding CI/CD Environments

What Is an Environment in Simple Terms

An environment is a named destination for your application. Dev is where every commit lands automatically. Staging is where the team tests before releasing. Production is where real users are. Environments give your pipeline structure: this build goes here first, then there, then there — with appropriate gates between them.

In GitHub Actions, environments are more than names — they carry protection rules (who must approve before deployment), environment-specific secrets (different database credentials per environment), and deployment history (who deployed what version when).

How It Works

◈ DIAGRAM
+------------------------------------------+
| Environment: dev |
| Protection: none |
| Deploys: automatically on every commit |
| Secrets: dev database credentials |
+------------------------------------------+
|
auto-deploy
|
v
+------------------------------------------+
| Environment: staging |
| Protection: none |
| Deploys: automatically on merge to main |
| Secrets: staging database credentials |
+------------------------------------------+
|
manual approval gate
Required reviewers:
tech-lead, senior-engineer
|
v
+------------------------------------------+
| Environment: production |
| Protection: 2 required reviewers |
| Wait timer: 5 minutes |
| Deploys: after approval |
| Secrets: production database credentials |
+------------------------------------------+

Environment configuration in GitHub Actions:

YAML
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.razorpay.internal
steps:
- name: Deploy to staging
env:
## This secret only exists in the staging environment
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
run: ./deploy.sh staging
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://api.razorpay.com
## This job pauses here until required reviewers approve
## in the GitHub Actions UI
steps:
- name: Deploy to production
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
run: ./deploy.sh production

Kubernetes namespace per environment pattern:

Bash
## Each environment maps to a Kubernetes namespace
kubectl create namespace payment-api-dev
kubectl create namespace payment-api-staging
kubectl create namespace payment-api-production
## Deploy to specific namespace
kubectl apply -f manifests/ -n payment-api-staging
## View deployments per environment
kubectl get deployments -n payment-api-staging
kubectl get deployments -n payment-api-production

Troubleshooting

Symptom Check What to Look For
Deployment waiting forever Approval settings Required reviewer has not approved
Wrong secrets in environment Secret scope Secret defined at repo vs environment level
Environment URL not showing url field in environment Set the url parameter in environment block
Tip

Set a wait timer (5-10 minutes) on the production environment in addition to required approvals. This gives anyone who spots a problem in staging time to block the production deployment before it starts — even if approvers move quickly.

Remember

Environment-level secrets override repository-level secrets of the same name. Use this to your advantage: set DB_PASSWORD at the environment level for staging and production with different values. The pipeline code stays identical — only the environment context changes.

Common Mistake

Using repository-level secrets for database passwords and API keys that differ between environments. A repository secret is shared across all environments — there is no way to have a different DB_PASSWORD for staging vs production using the same secret name. Always define environment-level secrets for any value that differs between deployment targets.

Security

Environment protection rules are only as strong as your branch protection rules. If engineers can push directly to main without a PR, they can bypass environment approval gates entirely. Always enforce branch protection on your deployment branches: require PR reviews, require status checks to pass, and prohibit force pushes.

Frequently Asked Questions

What functional difference does declaring an 'environment' add beyond just a deployment target name?

Beyond labeling where code goes, environments in platforms like GitHub Actions or GitLab let you attach protection rules — required reviewers before a job runs, wait timers, and branch restrictions — plus environment-scoped secrets that aren't visible to other environments. This turns a deploy step into an auditable gate: you get a history of who approved what deployed where and when, not just a log line.

What's a common mistake teams make when setting up CI/CD environments?

Reusing the same secrets across dev, staging, and production environments defeats the isolation environments are meant to provide — a compromised or misconfigured dev pipeline can then reach production credentials. Best practice is scoping secrets per environment and requiring manual approval gates specifically on production, while letting dev/staging deploy automatically on merge to keep feedback loops fast.