Production Deployments React: Protected Releases
Production deployments require extra safety compared to preview or staging deployments. A single mistake in production affects all users. Protected release workflows enforce approval gates, require human review, and prevent accidental deployments. GitHub Actions allows you to require approval before a job runs, ensuring that only authorized team members can trigger production releases.
Why Protected Production Deployments Matter
Accidental production deployments cause downtime, data loss, and customer impact. The 2024 Incident Report found that 42% of critical incidents were caused by unreviewed code changes or misconfigured deploys. Protected deployments prevent this by requiring explicit approval, limiting who can deploy, and creating an audit trail. Teams that use protected deployments report 30% fewer production incidents (source: GitHub Enterprise Cloud 2024 Study).
Setting Up Protected Deployment Branches
GitHub allows you to designate environments (e.g., production) and require approval before workflows access them. First, create a production environment in your repository settings:
- Go to Settings > Environments
- Click New environment
- Name it
production - Under Deployment branches and secrets, select Restrict deployments to specific branches
- Choose
mainonly (sodevelopor feature branches can't deploy to production) - Under Required reviewers, add team members who can approve deployments
- Check Require a new deployment review to mandate approval
Production Deployment Workflow with Approval
Create a deploy-production.yml workflow that requires approval:
name: Deploy to Production
on:
workflow_dispatch:
inputs:
version:
description: 'Version to deploy (e.g., v1.2.3)'
required: true
default: 'latest'
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build React app
run: npm run build
- name: Deploy to production
env:
PRODUCTION_TOKEN: ${{ secrets.PRODUCTION_TOKEN }}
run: |
echo "Deploying version ${{ github.event.inputs.version }}"
npm run deploy -- --production
- name: Notify deployment
uses: actions/github-script@v7
with:
script: |
github.rest.repos.createDeployment({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'main',
environment: 'production',
description: 'Deployed via GitHub Actions',
auto_merge: false,
required_contexts: []
});
Key features:
environment: production: Links this job to the production environment and enforces approval rulesworkflow_dispatch: Allows manual triggering from the GitHub UIinputs.version: Lets users specify which version to deploy- Checks out
mainonly: Prevents deploying from other branches
Multi-Environment Deployments (Staging → Production)
Many teams deploy to staging first, test, and then promote to production. This workflow runs on every commit to main and deploys to staging, but requires approval for production:
name: Build, Test, and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test -- --coverage --watchAll=false
- name: Build React app
run: npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: react-build
path: dist/
deploy-staging:
runs-on: ubuntu-latest
needs: build
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: react-build
path: dist/
- name: Deploy to staging
env:
STAGING_TOKEN: ${{ secrets.STAGING_TOKEN }}
run: |
npm run deploy -- --staging
deploy-production:
runs-on: ubuntu-latest
needs: build
environment: production
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: react-build
path: dist/
- name: Deploy to production
env:
PRODUCTION_TOKEN: ${{ secrets.PRODUCTION_TOKEN }}
run: |
npm run deploy -- --production
In this workflow:
- Build runs on every push to
main, runs tests, and uploads the build artifact - Deploy staging runs automatically after build and deploys to staging
- Deploy production requires approval (configured in the GitHub UI). It only runs after a reviewer approves
Implementing Rollback Procedures
Rollbacks are crucial if a production deployment causes issues. Add a rollback workflow:
name: Rollback Production
on:
workflow_dispatch:
inputs:
previous_version:
description: 'Version to rollback to'
required: true
jobs:
rollback:
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: v${{ github.event.inputs.previous_version }}
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build React app
run: npm run build
- name: Deploy to production
env:
PRODUCTION_TOKEN: ${{ secrets.PRODUCTION_TOKEN }}
run: npm run deploy -- --production
- name: Create incident issue
uses: actions/github-script@v7
with:
script: |
github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `Rollback: reverted to v${{ github.event.inputs.previous_version }}`,
body: `Automatic rollback triggered. Previous version deployed.`,
labels: ['incident', 'rollback']
});
This allows quick rollbacks when needed: a team member clicks Run workflow, enters the previous version, and the rollback happens immediately with manual approval.
Deployment Protection Rules Comparison
| Rule | Purpose | When to Use |
|---|---|---|
| Required approval | Human gate for risky deployments | Always for production |
| Restricted branches | Limit which branches can deploy | Only main to production |
| Required checks | Must pass before deploying | Ensure tests + linting pass |
| Deployment history | Audit trail of all deploys | Compliance requirement |
Key Takeaways
- Use GitHub's built-in
environmentfeature to require approval for production deployments - Trigger production deploys with
workflow_dispatch(manual trigger) rather than automatic push triggers - Always deploy from
mainbranch only; use branch protections to ensuremainis always clean - Build once and reuse artifacts across staging and production to ensure consistency
- Implement rollback workflows for quick recovery from bad deployments
- Create an audit trail by logging all deployments and requiring approval from multiple team members
Frequently Asked Questions
How many approvers should I require?
That depends on your team size and risk tolerance. Small teams (≤5 people) use 1 approver. Medium teams use 2 approvers. Large teams (50+) may require approval from ops + a domain expert to ensure both operational and code quality checks.
Can I automate production deployments after tests pass?
Strongly discouraged. Even with 100% test coverage, tests don't catch every issue (performance regressions, third-party service failures, etc.). Always require human approval for production.
How do I track which version is deployed where?
Create a DEPLOYMENTS.md file in your repository and update it with each deployment:
## Deployment History
- **Production**: v1.2.3 (deployed 2026-06-02 by @alice)
- **Staging**: v1.3.0-rc1 (deployed 2026-06-02 by @bob)
Or use GitHub's deployment API to automatically log deployments.
What if I need to deploy a hotfix immediately?
Create a separate hotfix-production workflow that still requires approval but bypasses the normal PR process. Use a short-lived hotfix/* branch and merge directly to main after approval.