Production-grade CI/CD with GitHub Actions, deployment strategies, caching, and release automation.
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node: [20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm test -- --coverage
- uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.node }}
path: coverage/
# Node modules — use setup-node cache (simplest)
- uses: actions/setup-node@v4
with: { node-version: 22, cache: npm }
# Docker layer caching
- uses: docker/build-push-action@v5
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
# Turborepo remote cache
- run: npx turbo build --cache-dir=.turbo
- uses: actions/cache@v4
with:
path: .turbo
key: turbo-${{ hashFiles('**/turbo.json') }}-${{ github.sha }}
restore-keys: turbo-${{ hashFiles('**/turbo.json') }}-
# Repository / org secrets (Settings → Secrets)
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
# Environment-scoped secrets (dev/staging/prod)
jobs:
deploy:
environment: production # requires approval + has own secrets
steps:
- run: deploy --token ${{ secrets.DEPLOY_TOKEN }}
# OIDC — no stored secrets (AWS, GCP, Azure)
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/deploy
aws-region: us-east-1
Rules: Never echo secrets. Use GITHUB_TOKEN where possible. Rotate credentials quarterly. Use OIDC over static keys.
# Build stage
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --production=false
COPY . .
RUN npm run build
# Production stage
FROM node:22-alpine
WORKDIR /app
RUN addgroup -g 1001 app && adduser -u 1001 -G app -s /bin/sh -D app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
COPY package.json .
USER app
EXPOSE 3000
CMD ["node", "dist/index.js"]
| Strategy | Downtime | Rollback Speed | Risk | Best For |
|---|---|---|---|---|
| Rolling | Zero | Minutes | Medium | Stateless services |
| Blue-Green | Zero | Instant (swap) | Low | Critical services |
| Canary | Zero | Fast | Lowest | High-traffic APIs |
| Recreate | Yes | Slow | High | Dev/staging only |
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to green
run: ./deploy.sh green
- name: Health check
run: curl -f https://green.app.com/health
- name: Swap traffic
run: ./swap-traffic.sh green
- name: Keep blue as rollback
run: echo "Blue is previous version — rollback with ./swap-traffic.sh blue"
# Trigger chain: push → dev → staging (auto) → prod (manual approval)
deploy-dev:
if: github.ref == 'refs/heads/main'
environment: dev
deploy-staging:
needs: deploy-dev
environment: staging
deploy-prod:
needs: deploy-staging
environment: production # Configure "Required reviewers" in GitHub
// .releaserc.json
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/github",
["@semantic-release/git", { "assets": ["CHANGELOG.md", "package.json"] }]
]
}
release:
runs-on: ubuntu-latest
permissions: { contents: write, packages: write }
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- run: npx semantic-release
env: { GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} }
npx changeset # developer adds changeset
npx changeset version # CI bumps versions
npx changeset publish # CI publishes packages
See references/changeset-action.yml for the GitHub Actions workflow.
# Kubernetes
kubectl rollout undo deployment/api
kubectl rollout status deployment/api
# Docker / ECS
aws ecs update-service --service api --task-definition api:PREVIOUS_REVISION
# Vercel / Netlify
vercel rollback # instant, previous deployment
Rollback checklist:
[](https://github.com/org/repo/actions/workflows/ci.yml)
[](https://codecov.io/gh/org/repo)
concurrency to cancel stale PR runspaths filter to skip irrelevant workflowsubuntu-latest (fastest) unless you need a specific OSSee references/workflow-templates/ for copy-paste starter workflows.