Secrets and Config Management for Deployments
The same artifact ships everywhere; only configuration and secrets change per environment.
A safe deployment pipeline never bakes credentials into the build. This lesson covers separating config from code, scoping secrets per environment, and sourcing secrets from a manager at deploy or run time rather than storing them in many places.
Config separate from code
Build one immutable artifact and inject configuration per environment at deploy or runtime - through environment variables, mounted config, or a secrets manager lookup. Hardcoding endpoints or keys forces a rebuild per environment and breaks the build-once promise.
Scope secrets to environments
In GitHub Actions, put production credentials in the production environment, not as repo-wide secrets. A staging job then cannot read production secrets. Combine this with required reviewers so reaching production secrets requires approval.
Pull from a secrets manager
Prefer fetching secrets at deploy time from AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, or Vault. This centralizes rotation and avoids copying secrets into many systems:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- run: |
DB_URL=$(aws secretsmanager get-secret-value --secret-id prod/db --query SecretString --output text)
./deploy.shHygiene rules
- Never echo secrets into logs; mask them and avoid debug dumps.
- Prefer OIDC over long-lived keys so there is less to leak.
- Rotate regularly and on any suspected exposure.
- Grant least privilege - each deploy reads only the secrets it needs.
Key takeaways
- Keep config out of the build and inject it per environment.
- Scope production secrets to the production environment behind approval.
- Source secrets from a manager at deploy time to centralize rotation.