GitHub Actions Tutorial — Deployment Protection

2 minute read

Deployment Protection

GitHub Environments let you attach protection rules to deployment targets. Jobs referencing an environment pause until all protection rules are satisfied — approvals, wait timers, or branch restrictions.


Environments

Create environments in Repository Settings → Environments:

Repository: my-org/my-app
├── staging
│   ├── No protection rules
│   └── Secrets: STAGING_URL
├── production
│   ├── Required reviewers: @lead-dev, @devops-team
│   ├── Wait timer: 5 minutes
│   ├── Branch policy: main only
│   └── Secrets: PROD_URL, DB_PASSWORD

Use in Workflows


jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging               # no gates — runs immediately
    steps:
      - run: echo "Deploying to ${{ vars.STAGING_URL }}"

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production             # pauses for approval
    steps:
      - run: echo "Deploying to ${{ vars.PROD_URL }}"


Protection Rules

Required Reviewers

One or more people (or teams) must approve before the job runs:

Environment: production
Required reviewers: @alice, @bob, @devops-team

When a workflow reaches this job:

  1. Reviewers receive a notification
  2. Any one reviewer can approve or reject
  3. Job proceeds only after approval

Wait Timer

Add a delay before the job starts (even after approval):

Environment: production
Wait timer: 10 minutes

Useful for:

  • Cool-down period between deploy stages
  • Time to verify staging before production
  • Allowing rollback window

Branch Restrictions

Limit which branches can deploy to an environment:

Environment: production
Deployment branches: main only

Options:

  • All branches — any branch can deploy
  • Protected branches — only branches with protection rules
  • Selected branches — specific branch name patterns

Environment Secrets and Variables

Each environment has its own secrets and variables that override repository-level values:


jobs:
  deploy:
    environment: production
    steps:
      - run: |
          # These come from the production environment
          echo "URL: ${{ vars.APP_URL }}"
          echo "Deploying..."
        env:
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }}


Multi-Environment Pipeline


name: Deploy Pipeline

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test

  deploy-staging:
    needs: test
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - uses: actions/checkout@v4
      - run: |
          echo "Deploying to staging..."
          # deploy commands here

  smoke-test:
    needs: deploy-staging
    runs-on: ubuntu-latest
    steps:
      - run: |
          curl -f ${{ vars.STAGING_URL }}/health || exit 1
          echo "Smoke test passed"

  deploy-production:
    needs: smoke-test
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - run: |
          echo "Deploying to production..."
          # deploy commands here

Flow: test → deploy staging → smoke test → (approval gate) → deploy production.


Common Mistakes

Mistake Fix
Environment secrets not found Secrets are scoped to the environment — they’re only available in jobs with environment:
Job doesn’t pause for approval Ensure the job has environment: <name> and reviewers are configured
Branch deploy rejected Check branch restriction settings on the environment
Approval notification not received Reviewers must have repo access and notification settings enabled

Quick Reference

Feature Configuration
Create environment Settings → Environments → New
Required reviewers Add users or teams as reviewers
Wait timer 0-43200 minutes (up to 30 days)
Branch policy All, protected, or selected branches
Env secrets secrets.NAME — only in jobs with environment:
Env variables vars.NAME — only in jobs with environment:

← Previous: Deploy Docker Images

Next: Practice Exercises →

Updated: