GitHub Actions Cheatsheet
Deployment
Use this GitHub Actions reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Deployment Environments
Environments add protection rules, required reviewers, and scoped secrets to deployment jobs.
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com # shown in the Actions UI and PR deployments
steps:
- run: ./deploy.sh
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} # from 'production' environmentConfigure environments at: Repo → Settings → Environments.
Environment Protection Rules
| Rule | What it does |
|---|---|
| Required reviewers | Job pauses and notifies reviewers; must be approved to proceed |
| Wait timer | Delays job start by N minutes (1–43,200) |
| Deployment branches | Restrict which branches/tags can deploy |
| Deployment policies | Custom rules (Enterprise) |
Branch Protection via Environments
# Only allow main and release/* to deploy to production
# Set in: Settings → Environments → production → Deployment branches
# Pattern: main, release/**In the workflow, this blocks the job from running on other branches — no code change needed.
Concurrency — Prevent Duplicate Deployments
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: false # queue, don't cancel (safe for deploys)Or cancel for preview/staging:
concurrency:
group: deploy-preview-${{ github.event.pull_request.number }}
cancel-in-progress: trueDeploy on Push to Main
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- run: ./scripts/deploy.sh
env:
API_KEY: ${{ secrets.API_KEY }}Deploy on Release Publish
on:
release:
types: [published]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name }}
- run: echo "Deploying ${{ github.event.release.tag_name }}"
- run: ./deploy.shDeploy to AWS (OIDC — no static keys)
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActions
aws-region: us-east-1
- uses: aws-actions/amazon-ecr-login@v2
- run: |
docker build -t $ECR_URI:${{ github.sha }} .
docker push $ECR_URI:${{ github.sha }}
env:
ECR_URI: 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp
- run: |
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--force-new-deploymentDeploy to Google Cloud (OIDC)
permissions:
id-token: write
contents: read
steps:
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/123/locations/global/workloadIdentityPools/github/providers/github
service_account: deployer@my-project.iam.gserviceaccount.com
- uses: google-github-actions/deploy-cloudrun@v2
with:
service: my-service
image: gcr.io/my-project/app:${{ github.sha }}
region: us-central1Deploy to Azure (OIDC)
permissions:
id-token: write
contents: read
steps:
- uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- uses: azure/webapps-deploy@v3
with:
app-name: my-app
package: dist/GitHub Pages Deployment
permissions:
contents: read
pages: write
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- uses: actions/upload-pages-artifact@v3
with:
path: out/
- id: deploy
uses: actions/deploy-pages@v4Preview / PR Environments
on:
pull_request:
types: [opened, synchronize, reopened, closed]
jobs:
deploy-preview:
if: github.event.action != 'closed'
runs-on: ubuntu-latest
environment:
name: pr-${{ github.event.number }}
url: https://pr-${{ github.event.number }}.preview.example.com
steps:
- run: ./deploy-preview.sh ${{ github.event.number }}
teardown:
if: github.event.action == 'closed'
runs-on: ubuntu-latest
steps:
- run: ./teardown-preview.sh ${{ github.event.number }}Post Deployment Status to PR
- uses: actions/github-script@v7
with:
script: |
github.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: '${{ steps.deploy.outputs.deployment-id }}',
state: 'success',
environment_url: 'https://example.com',
description: 'Deploy complete',
});Or use the simpler PR comment approach:
- uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '**Deployed to production:** https://example.com',
});Rollback Pattern
jobs:
deploy:
runs-on: ubuntu-latest
outputs:
previous-sha: ${{ steps.get-prev.outputs.sha }}
steps:
- id: get-prev
run: echo "sha=$(git rev-parse HEAD~1)" >> $GITHUB_OUTPUT
- run: ./deploy.sh ${{ github.sha }}
rollback:
needs: deploy
if: failure()
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh ${{ needs.deploy.outputs.previous-sha }}
env:
ROLLBACK: 'true'Deploy Workflow Checklist
- Use environment protection rules for production (required reviewers + branch restriction).
- Use OIDC over static credentials whenever the cloud provider supports it.
- Set
concurrency.cancel-in-progress: falseto queue, not clobber, concurrent deploys. - Upload build artifacts before deploy; download in deploy job — don't rebuild.
- Always record the deployed SHA/version in a step output or annotation.
- Post deployment URLs to the PR for preview environments.
- Implement a rollback job triggered on
failure().