Skip to main content

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:

  1. Go to Settings > Environments
  2. Click New environment
  3. Name it production
  4. Under Deployment branches and secrets, select Restrict deployments to specific branches
  5. Choose main only (so develop or feature branches can't deploy to production)
  6. Under Required reviewers, add team members who can approve deployments
  7. 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 rules
  • workflow_dispatch: Allows manual triggering from the GitHub UI
  • inputs.version: Lets users specify which version to deploy
  • Checks out main only: 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:

  1. Build runs on every push to main, runs tests, and uploads the build artifact
  2. Deploy staging runs automatically after build and deploys to staging
  3. 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

RulePurposeWhen to Use
Required approvalHuman gate for risky deploymentsAlways for production
Restricted branchesLimit which branches can deployOnly main to production
Required checksMust pass before deployingEnsure tests + linting pass
Deployment historyAudit trail of all deploysCompliance requirement

Key Takeaways

  • Use GitHub's built-in environment feature to require approval for production deployments
  • Trigger production deploys with workflow_dispatch (manual trigger) rather than automatic push triggers
  • Always deploy from main branch only; use branch protections to ensure main is 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.

Further Reading