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
+------------------------------------------+| 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:
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 productionKubernetes namespace per environment pattern:
## Each environment maps to a Kubernetes namespacekubectl create namespace payment-api-devkubectl create namespace payment-api-stagingkubectl create namespace payment-api-production ## Deploy to specific namespacekubectl apply -f manifests/ -n payment-api-staging ## View deployments per environmentkubectl get deployments -n payment-api-stagingkubectl get deployments -n payment-api-productionTroubleshooting
| 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 |
TipSet 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.
RememberEnvironment-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 MistakeUsing 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.
SecurityEnvironment 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.