GitHub Actions Tutorial — Environments

2 minute read

Environments

Environments are named deployment targets — like staging, production, or preview. They let you attach protection rules, environment-specific secrets, and deployment tracking to your workflows.


Creating Environments

Go to: Settings → Environments → New environment

Common environments:

Environment Purpose
development Dev/feature testing
staging Pre-production validation
production Live deployment

Using Environments in Workflows


jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - uses: actions/checkout@v4
      - run: echo "Deploying to staging..."
        env:
          API_URL: ${{ vars.API_URL }}           # env-specific variable
          API_KEY: ${{ secrets.API_KEY }}         # env-specific secret

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - run: echo "Deploying to production..."
        env:
          API_URL: ${{ vars.API_URL }}           # different value than staging
          API_KEY: ${{ secrets.API_KEY }}         # different secret than staging


Protection Rules

Rule Purpose
Required reviewers Someone must approve before deployment
Wait timer Delay deployment by N minutes
Branch restrictions Only specific branches can deploy
Custom rules Use deployment protection rules API

Required Reviewers

When a workflow reaches a job with environment: production and required reviewers are set, it pauses and waits for approval:

Job reaches environment: production
    ↓
⏸ Waiting for approval
    ↓
Reviewer approves in Actions UI
    ↓
Job continues

Environment-Specific Secrets and Variables

Each environment can have its own secrets and variables:

Variable staging production
API_URL https://api-staging.example.com https://api.example.com
API_KEY stg_key_xxx prod_key_yyy
DB_HOST db-staging.internal db-prod.internal

The same ${{ secrets.API_KEY }} reference resolves to different values based on the environment.


Environment with URL

Display the deployment URL in the Actions UI:


jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://myapp.example.com
    steps:
      - run: echo "Deployed!"


Deployment Pipeline Pattern


name: Deploy Pipeline

on:
  push:
    branches: [main]

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

  deploy-staging:
    needs: test
    runs-on: ubuntu-latest
    environment:
      name: staging
      url: https://staging.myapp.com
    steps:
      - uses: actions/checkout@v4
      - run: echo "Deploying to staging..."
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://myapp.com
    steps:
      - uses: actions/checkout@v4
      - run: echo "Deploying to production..."
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}

┌──────┐    ┌─────────────┐    ┌──────────────────┐
│ test │ → │ staging     │ → │ production       │
│      │    │ (auto)      │    │ (requires review)│
└──────┘    └─────────────┘    └──────────────────┘

Common Mistakes

Mistake Fix
Environment secrets not available Ensure environment: is set on the job
Approval not required Configure required reviewers in Settings → Environments
Wrong secret value Check you’re using the correct environment name
Can’t deploy from feature branch Check branch restrictions on the environment

Quick Reference

Feature Syntax
Use environment environment: production
With URL environment: name: prod, url: https://...
Env secret ${{ secrets.NAME }} (resolved per environment)
Env variable ${{ vars.NAME }} (resolved per environment)
Protection Required reviewers, wait timer, branch rules

← Previous: GITHUB_TOKEN

Next: OIDC Tokens →

Updated: