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 |
PLACEMENT PRO TIP**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 THIS**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 / WARNING**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.
COMMON MISTAKE / WARNING**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.