AtomLearnAtomLearn
DashboardGoalsPathAchievementsReviewSign In
DashboardCI/CDDeployment Pipelines
CI/CDNot Started

Deployment Pipelines

Automate deploys with staging gates, rollbacks, and zero-downtime strategies

0%
Knowledge0%
Learn & Drill
Fluency0%
Drill & Speed
Retention0%
Mastery & Review
Confidence0%
All modes
Practice

Free tier: read the explanation here. Upgrade to Pro for Drills, Speed challenges & Mastery badges.

Upgrade
Knowledge
Fluency
Retention

Knowledge Debt detected

You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.

Explanation

A deployment pipeline takes a verified build and moves it to production safely. The goal: get changes to users fast, without breaking what is already working.

Staging → Production gate (the most common pattern):

yaml jobs: test: runs-on: ubuntu-latest steps: [...] deploy-staging: needs: test environment: staging runs-on: ubuntu-latest steps: - run: ./deploy.sh staging deploy-prod: needs: deploy-staging environment: production # can require manual approval runs-on: ubuntu-latest steps: - run: ./deploy.sh production GitHub Environments can require a reviewer to approve the deploy-prod` job before it starts.

Docker-based deployment:

yaml jobs: build-push: runs-on: ubuntu-latest outputs: image-tag: ${{ steps.meta.outputs.tags }} steps: - uses: actions/checkout@v4 - uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USER }} password: ${{ secrets.DOCKERHUB_TOKEN }} - id: meta uses: docker/metadata-action@v5 with: images: myorg/myapp tags: type=sha - uses: docker/build-push-action@v5 with: push: true tags: ${{ steps.meta.outputs.tags }} deploy: needs: build-push runs-on: ubuntu-latest steps: - run: | ssh deploy@${{ secrets.SERVER }} \ "docker pull ${{ needs.build-push.outputs.image-tag }} && \ docker stop app || true && \ docker run -d --name app ${{ needs.build-push.outputs.image-tag }}"

Deployment strategies:

| Strategy | How it works | Downtime? | |----------|-------------|-----------| | Recreate | Stop old, start new | Yes (~seconds) | | Rolling | Replace instances one at a time | No | | Blue/Green | Run both versions, switch traffic | No | | Canary | Send 5% of traffic to new version | No |

Health check before full rollout:

bash for i in {1..10}; do STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://staging.myapp.com/health) if [ "$STATUS" = "200" ]; then echo "Healthy!"; exit 0; fi echo "Waiting... ($STATUS)"; sleep 10 done echo "Health check failed!" && exit 1

Examples

Deploy to Railway on merge to main

The `needs: test` gate ensures nothing deploys unless all tests pass. `environment: production` adds an optional human approval step.

# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20', cache: npm }
      - run: npm ci && npm test

  deploy:
    needs: test    # only runs if tests pass
    runs-on: ubuntu-latest
    environment: production

    steps:
      - uses: actions/checkout@v4

      - name: Deploy to Railway
        run: |
          npm install -g @railway/cli
          railway up --service myapp
        env:
          RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}

Blue/Green deploy with health check

If the health check fails, the old version keeps serving traffic — users see no interruption.

#!/bin/bash
# deploy.sh — blue/green swap
# nginx proxies to either "blue" (port 3000) or "green" (port 3001)
IMAGE=$1

CURRENT=$(cat /etc/nginx/current_color 2>/dev/null || echo "blue")
NEXT=$([ "$CURRENT" = "blue" ] && echo "green" || echo "blue")
PORT=$([ "$NEXT" = "blue" ] && echo "3000" || echo "3001")

echo "Deploying $IMAGE to $NEXT (port $PORT)..."

docker stop app-$NEXT 2>/dev/null || true
docker rm app-$NEXT 2>/dev/null || true
docker run -d --name app-$NEXT -p $PORT:3000 $IMAGE

# Health check new version before switching
sleep 5
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$PORT/health)
if [ "$STATUS" != "200" ]; then
  echo "Health check failed! Rolling back."
  docker stop app-$NEXT
  exit 1
fi

# Switch nginx to new version
sed -i "s/localhost:[0-9]*/localhost:$PORT/" /etc/nginx/sites-enabled/app
nginx -s reload
echo $NEXT > /etc/nginx/current_color

echo "Deployed! $NEXT is now live."

How well did you understand this?