Deployment¶
Deploy applications safely and reliably.
Deployment Strategies¶
Rolling Deployment¶
Update instances gradually, one at a time.
Time →
┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
│v1 │ │v1 │ │v1 │ → │v2 │ │v1 │ │v1 │
└───┘ └───┘ └───┘ └───┘ └───┘ └───┘
→ │v2 │ │v2 │ │v1 │
→ │v2 │ │v2 │ │v2 │
Pros: Zero downtime, gradual rollout Cons: Mixed versions during deployment
Blue-Green Deployment¶
Switch traffic between two identical environments.
┌─────────────┐
│ Load │
│ Balancer │
└──────┬──────┘
│
┌──────────┴──────────┐
│ │
┌───┴───┐ ┌───┴───┐
│ Blue │ │ Green │
│ v1 │ ← active │ v2 │ ← standby
└───────┘ └───────┘
Pros: Instant rollback, full testing before switch Cons: Double infrastructure cost
Canary Deployment¶
Route small percentage of traffic to new version.
100% traffic
│
┌─────┴─────┐
│ Router │
└─────┬─────┘
│
┌─────────────┼─────────────┐
│ │ │
95% ↓ 5% ↓ 0% ↓
┌─────┐ ┌─────┐ ┌─────┐
│ v1 │ │ v2 │ │ v2 │
└─────┘ └─────┘ └─────┘
stable canary scaling
Pros: Minimize blast radius, real traffic testing Cons: Complex routing, monitoring required
Kubernetes Deployment¶
Rolling Update¶
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Extra pod during update
maxUnavailable: 0 # No downtime
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: api:v2
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
GitHub Actions Deployment¶
deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: production
url: https://api.example.com
steps:
- uses: actions/checkout@v4
- name: Configure kubectl
uses: azure/k8s-set-context@v3
with:
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/api \
api=ghcr.io/${{ github.repository }}:${{ github.sha }}
kubectl rollout status deployment/api --timeout=5m
Rollback¶
rollback:
runs-on: ubuntu-latest
environment: production
steps:
- name: Rollback deployment
run: kubectl rollout undo deployment/api
Docker Compose Deployment¶
Deploy Script¶
#!/bin/bash
# deploy.sh
set -e
COMPOSE_FILE="docker-compose.prod.yml"
IMAGE_TAG="${1:-latest}"
echo "Deploying version: $IMAGE_TAG"
# Pull new images
docker compose -f $COMPOSE_FILE pull
# Deploy with zero downtime
docker compose -f $COMPOSE_FILE up -d --no-deps --scale api=2 api
# Wait for health check
sleep 10
# Remove old containers
docker compose -f $COMPOSE_FILE up -d --no-deps --scale api=1 api
echo "Deployment complete"
GitHub Actions¶
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/app
git pull
docker compose pull
docker compose up -d
Cloud Platform Deployments¶
AWS ECS¶
deploy-ecs:
runs-on: ubuntu-latest
needs: build
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy to ECS
run: |
aws ecs update-service \
--cluster production \
--service api \
--force-new-deployment
Google Cloud Run¶
deploy-cloudrun:
runs-on: ubuntu-latest
needs: build
steps:
- uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS }}
- uses: google-github-actions/deploy-cloudrun@v2
with:
service: api
image: gcr.io/${{ secrets.GCP_PROJECT }}/api:${{ github.sha }}
region: us-central1
Vercel¶
deploy-vercel:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
Database Migrations¶
Run Migrations Before Deploy¶
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Run migrations
run: |
alembic upgrade head
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
- name: Deploy application
run: ./deploy.sh
Migration Safety Checks¶
- name: Check migration safety
run: |
# Check for destructive migrations
if alembic history -r head:-1 | grep -i "drop\|delete"; then
echo "WARNING: Destructive migration detected!"
exit 1
fi
Health Checks¶
Readiness vs Liveness¶
# health.py
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/health/live")
async def liveness():
"""Is the process running?"""
return {"status": "alive"}
@app.get("/health/ready")
async def readiness():
"""Is the app ready to serve traffic?"""
# Check dependencies
try:
await check_database()
await check_redis()
return {"status": "ready"}
except Exception as e:
return Response(
content={"status": "not ready", "error": str(e)},
status_code=503
)
Wait for Healthy¶
- name: Deploy
run: kubectl apply -f deployment.yaml
- name: Wait for rollout
run: |
kubectl rollout status deployment/api --timeout=5m
# Additional health check
for i in {1..30}; do
if curl -s https://api.example.com/health/ready | grep -q "ready"; then
echo "Deployment healthy"
exit 0
fi
sleep 10
done
echo "Deployment unhealthy"
exit 1
Rollback Procedures¶
Automatic Rollback on Failure¶
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy
id: deploy
run: |
kubectl set image deployment/api api=myapp:${{ github.sha }}
kubectl rollout status deployment/api --timeout=5m
- name: Rollback on failure
if: failure() && steps.deploy.outcome == 'failure'
run: |
kubectl rollout undo deployment/api
echo "Deployment failed, rolled back to previous version"
Manual Rollback Workflow¶
name: Rollback
on:
workflow_dispatch:
inputs:
environment:
description: 'Environment to rollback'
required: true
type: choice
options:
- staging
- production
version:
description: 'Version to rollback to (leave empty for previous)'
required: false
jobs:
rollback:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- name: Rollback
run: |
if [ -n "${{ inputs.version }}" ]; then
kubectl set image deployment/api api=myapp:${{ inputs.version }}
else
kubectl rollout undo deployment/api
fi
Deployment Notifications¶
Slack Notification¶
- name: Notify Slack
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Deployment to ${{ inputs.environment }}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Deployment Complete* :rocket:\n*Environment:* ${{ inputs.environment }}\n*Version:* ${{ github.sha }}\n*Author:* ${{ github.actor }}"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
GitHub Deployment Status¶
- name: Create deployment
uses: chrnorm/deployment-action@v2
id: deployment
with:
token: ${{ secrets.GITHUB_TOKEN }}
environment: production
- name: Deploy
run: ./deploy.sh
- name: Update deployment status
uses: chrnorm/deployment-status@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
state: ${{ job.status }}
environment-url: https://api.example.com
Complete Deployment Workflow¶
name: Deploy
on:
push:
branches: [main]
workflow_dispatch:
inputs:
environment:
type: choice
options: [staging, production]
default: staging
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to staging
run: ./deploy.sh staging
- name: Run smoke tests
run: ./smoke-tests.sh https://staging.example.com
deploy-production:
needs: deploy-staging
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to production
run: ./deploy.sh production
- name: Verify deployment
run: ./smoke-tests.sh https://example.com
- name: Rollback on failure
if: failure()
run: ./rollback.sh production
Best Practices Summary¶
| Practice | Benefit |
|---|---|
| Use environments | Control access, require approval |
| Health checks | Ensure deployment success |
| Rollback automation | Quick recovery |
| Canary releases | Reduce blast radius |
| Run migrations first | Database consistency |
| Notify on deploy | Team awareness |
| Smoke tests | Verify functionality |
| Immutable deploys | Reproducibility |