Skip to content

GUIDES

Production Runbook

Operational procedures, incident response, and troubleshooting guide for Aether deployments.

Table of Contents

  1. Overview
  2. Pre-Deployment Checklist
  3. Deployment Procedures
  4. Monitoring & Alerts
  5. Incident Response
  6. Common Issues
  7. Rollback Procedures
  8. Scaling Operations
  9. Backup & Recovery
  10. Security Incidents
  11. Performance Tuning
  12. Maintenance Windows

Overview

This runbook provides step-by-step procedures for managing Aether deployments in production environments.

On-Call Contact Information

Primary On-Call: Team rotation (see PagerDuty)
Escalation: Platform Team Lead
Emergency: CTO

Slack Channels:
- #aether-alerts
- #aether-incidents
- #platform-team

Critical Services

Service Priority RTO RPO Contact
API Gateway P0 5min 0 Platform Team
Payment Service P0 5min 0 Payments Team
User Service P1 15min 1min Identity Team
Product Service P1 15min 5min Product Team
Database P0 10min 1min Data Team

Pre-Deployment Checklist

Before Any Deployment

  • Create backup: aether backup -n pre-deploy-$(date +%Y%m%d)
  • Verify health checks pass
  • Review recent alerts and metrics
  • Check resource capacity
  • Notify stakeholders in #deployments
  • Ensure rollback plan is ready
  • Schedule deployment during low-traffic window
  • Have second team member available

Production Deployment Checklist

  • All tests pass in staging
  • Load testing completed
  • Security scan completed (no HIGH/CRITICAL)
  • Database migrations tested
  • Feature flags configured
  • Runbook updated with new procedures
  • Monitoring dashboards updated
  • Rollback tested in staging
  • Change request approved
  • Customer support team notified

Deployment Procedures

Standard Deployment

#!/bin/bash
# standard-deployment.sh

set -e

echo "Starting deployment of $SERVICE_NAME v$VERSION"

# 1. Pre-deployment checks
echo "Running pre-deployment checks..."
aether -s workloads/$SERVICE_NAME.yaml validate
kubectl get nodes
kubectl get pods -n production

# 2. Create backup
echo "Creating backup..."
BACKUP_NAME="pre-deploy-$SERVICE_NAME-$(date +%Y%m%d-%H%M%S)"
aether backup -n "$BACKUP_NAME" -d "Pre-deployment backup for $SERVICE_NAME v$VERSION"

# 3. Deploy with blue-green strategy
echo "Deploying $SERVICE_NAME v$VERSION..."
aether migrate $SERVICE_NAME kubernetes --strategy blue-green

# 4. Wait for deployment
echo "Waiting for deployment to stabilize..."
kubectl wait --for=condition=ready pod -l app=$SERVICE_NAME --timeout=300s

# 5. Run smoke tests
echo "Running smoke tests..."
./scripts/smoke-test-$SERVICE_NAME.sh

# 6. Verify health
echo "Checking service health..."
HEALTH_URL=$(kubectl get svc $SERVICE_NAME -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
curl -f https://$HEALTH_URL/health || {
    echo "Health check failed! Initiating rollback..."
    aether restore $(aether list-backups | grep $BACKUP_NAME | awk '{print $2}')
    exit 1
}

# 7. Monitor for 5 minutes
echo "Monitoring deployment..."
for i in {1..30}; do
    ERROR_RATE=$(curl -s http://prometheus:9090/api/v1/query?query=rate(http_requests_total{status=~"5.."}[1m]) | jq -r '.data.result[0].value[1]')
    if (( $(echo "$ERROR_RATE > 0.01" | bc -l) )); then
        echo "Error rate too high! Rolling back..."
        aether restore $(aether list-backups | grep $BACKUP_NAME | awk '{print $2}')
        exit 1
    fi
    sleep 10
done

echo "✅ Deployment successful!"
echo "Backup available: $BACKUP_NAME"

Emergency Hotfix Deployment

#!/bin/bash
# emergency-hotfix.sh

# Skip normal approval process for P0 incidents
# Still maintain safety checks

echo "🚨 EMERGENCY HOTFIX DEPLOYMENT"

# Quick validation
aether -s workloads/$SERVICE_NAME.yaml validate

# Deploy immediately
aether migrate $SERVICE_NAME kubernetes --strategy immediate

# Monitor closely
watch -n 5 kubectl get pods -l app=$SERVICE_NAME

Canary Deployment

#!/bin/bash
# canary-deployment.sh

# Deploy to 10% of traffic
yq eval '.scaling.minReplicas = 1' -i workloads/$SERVICE_NAME-canary.yaml
aether -s workloads/$SERVICE_NAME-canary.yaml run

# Monitor for 15 minutes
sleep 900

# Check error rate
ERROR_RATE=$(get-error-rate.sh $SERVICE_NAME-canary)
if [ $(echo "$ERROR_RATE < 0.01" | bc) -eq 1 ]; then
    # Promote canary
    aether -s workloads/$SERVICE_NAME.yaml run
    aether delete $SERVICE_NAME-canary
else
    # Rollback canary
    aether delete $SERVICE_NAME-canary
    echo "Canary failed - not promoting"
fi

Monitoring & Alerts

Key Metrics to Monitor

# Critical Metrics
- name: service_availability
  threshold: < 99.9%
  alert: P0

- name: error_rate
  threshold: > 1%
  alert: P1

- name: latency_p95
  threshold: > 500ms
  alert: P1

- name: latency_p99
  threshold: > 1000ms
  alert: P2

- name: cpu_utilization
  threshold: > 80%
  alert: P2

- name: memory_utilization
  threshold: > 85%
  alert: P2

- name: disk_utilization
  threshold: > 90%
  alert: P1

Dashboard URLs

Production Overview:   https://grafana.company.com/d/prod-overview
Service Health:        https://grafana.company.com/d/service-health
Infrastructure:        https://grafana.company.com/d/infrastructure
Database Performance:  https://grafana.company.com/d/database

Alert Response Times

Severity Response Time Resolution Time
P0 5 minutes 1 hour
P1 15 minutes 4 hours
P2 1 hour 24 hours
P3 4 hours 1 week

Incident Response

Incident Response Workflow

1. DETECT
   ├─ Alert fires
   ├─ Page on-call engineer
   └─ Create incident ticket

2. ASSESS
   ├─ Check monitoring dashboards
   ├─ Review recent deployments
   ├─ Check logs for errors
   └─ Determine severity

3. MITIGATE
   ├─ Apply immediate fix or rollback
   ├─ Update incident status
   └─ Communicate with stakeholders

4. RESOLVE
   ├─ Verify fix is effective
   ├─ Monitor for recurrence
   └─ Close incident

5. FOLLOW-UP
   ├─ Write post-mortem
   ├─ Implement preventive measures
   └─ Update runbook

P0 Incident Procedure

# STEP 1: Acknowledge incident
# - Page acknowledged in PagerDuty
# - Post in #aether-incidents
# - Start war room if needed

# STEP 2: Quick assessment
kubectl get pods -n production
kubectl get events --sort-by='.lastTimestamp' | tail -20
aether list

# STEP 3: Check recent changes
git log --since="1 hour ago" --oneline
kubectl rollout history deployment/$SERVICE_NAME

# STEP 4: Review metrics
# Open Grafana dashboards
# Check error rates, latency, throughput

# STEP 5: Immediate mitigation
# Option A: Rollback recent deployment
LAST_BACKUP=$(aether list-backups | head -2 | tail -1 | awk '{print $2}')
aether restore $LAST_BACKUP

# Option B: Scale up resources
kubectl scale deployment/$SERVICE_NAME --replicas=10

# Option C: Route traffic away
kubectl patch ingress $SERVICE_NAME -p '{"spec":{"rules":[]}}'

# STEP 6: Communicate
# Post updates every 15 minutes in #aether-incidents
# Update status page if customer-facing

# STEP 7: Verify resolution
curl -f https://api.company.com/health
# Check error rates return to normal

# STEP 8: Schedule post-mortem
# Create calendar invite within 24 hours

Common Issues

High Error Rate

Symptoms: - HTTP 5xx errors increasing - Error rate alerts firing - Customer complaints

Diagnosis:

# Check error logs
kubectl logs -n production -l app=$SERVICE_NAME --tail=100 | grep ERROR

# Check recent deployments
kubectl rollout history deployment/$SERVICE_NAME

# Check resource usage
kubectl top pods -n production -l app=$SERVICE_NAME

# Check dependencies
curl -f https://database:5432/health
curl -f https://redis:6379/health

Resolution:

# If recent deployment:
aether restore $(aether list-backups | head -2 | tail -1 | awk '{print $2}')

# If resource exhaustion:
kubectl scale deployment/$SERVICE_NAME --replicas=$(($(kubectl get deployment $SERVICE_NAME -o jsonpath='{.spec.replicas}') * 2))

# If dependency issue:
# Fix dependency first, then restart service
kubectl rollout restart deployment/$SERVICE_NAME

High Latency

Symptoms: - Response times >1s - Latency alerts firing - Slow page loads

Diagnosis:

# Check service latency
kubectl exec -it $POD_NAME -- curl http://localhost:5090/metrics | grep latency

# Check database performance
kubectl exec -it postgres-0 -- psql -U postgres -c "
  SELECT pid, now() - query_start AS duration, query
  FROM pg_stat_activity
  WHERE state = 'active'
  ORDER BY duration DESC
  LIMIT 10;"

# Check cache hit rate
kubectl exec -it redis-0 -- redis-cli INFO stats | grep hits

Resolution:

# Enable caching
kubectl set env deployment/$SERVICE_NAME CACHE_ENABLED=true

# Scale up
kubectl scale deployment/$SERVICE_NAME --replicas=10

# Optimize database queries
# (requires code changes)

# Add read replicas
aether -s workloads/database-replica.yaml run

Out of Memory (OOM)

Symptoms: - Pods restarting frequently - OOMKilled events - Memory alerts

Diagnosis:

# Check OOM events
kubectl get events -n production | grep OOMKilled

# Check memory usage
kubectl top pods -n production -l app=$SERVICE_NAME

# Check memory limits
kubectl get deployment $SERVICE_NAME -o yaml | grep -A 5 resources

Resolution:

# Increase memory limits
yq eval '.resources.limits.memory = "4Gi"' -i workloads/$SERVICE_NAME.yaml
aether -s workloads/$SERVICE_NAME.yaml run

# Or fix memory leak
# (requires code changes and redeployment)

# Temporary: Restart pods regularly
kubectl set env deployment/$SERVICE_NAME RESTART_INTERVAL=3600

Database Connection Pool Exhausted

Symptoms: - "Too many connections" errors - Database alerts - Slow queries

Diagnosis:

# Check active connections
kubectl exec -it postgres-0 -- psql -U postgres -c "
  SELECT count(*), state
  FROM pg_stat_activity
  GROUP BY state;"

# Check max connections
kubectl exec -it postgres-0 -- psql -U postgres -c "
  SHOW max_connections;"

Resolution:

# Increase max connections
kubectl exec -it postgres-0 -- psql -U postgres -c "
  ALTER SYSTEM SET max_connections = 200;
  SELECT pg_reload_conf();"

# Optimize connection pooling
kubectl set env deployment/$SERVICE_NAME DB_POOL_SIZE=20 DB_POOL_MAX_OVERFLOW=10

# Add connection pooler (PgBouncer)
aether -s workloads/pgbouncer.yaml run

Certificate Expiration

Symptoms: - SSL/TLS errors - Certificate warnings - Connection refused

Diagnosis:

# Check certificate expiration
kubectl get certificate -n production
kubectl describe certificate $CERT_NAME

# Manual check
echo | openssl s_client -servername api.company.com -connect api.company.com:443 2>/dev/null | openssl x509 -noout -dates

Resolution:

# Renew with cert-manager
kubectl delete certificate $CERT_NAME
kubectl apply -f certificates/$CERT_NAME.yaml

# Or manual renewal
# (depends on certificate provider)

# Verify renewal
kubectl get certificate $CERT_NAME -o yaml | grep notAfter

Rollback Procedures

Standard Rollback

#!/bin/bash
# rollback.sh

set -e

echo "🔄 Starting rollback of $SERVICE_NAME"

# 1. Identify backup to restore
aether list-backups
read -p "Enter backup name to restore: " BACKUP_NAME

# 2. Verify backup
aether list-backups | grep $BACKUP_NAME || {
    echo "Backup not found!"
    exit 1
}

# 3. Announce rollback
echo "Announcing rollback in #aether-incidents..."
# Post to Slack

# 4. Execute rollback
echo "Restoring from backup: $BACKUP_NAME"
aether restore ~/.aether/backups/$BACKUP_NAME.json

# 5. Verify rollback
echo "Verifying rollback..."
kubectl wait --for=condition=ready pod -l app=$SERVICE_NAME --timeout=300s

# 6. Run health checks
echo "Running health checks..."
./scripts/smoke-test-$SERVICE_NAME.sh

# 7. Monitor
echo "Monitoring for 5 minutes..."
sleep 300

# 8. Confirm success
echo "✅ Rollback completed successfully"
echo "Previous version restored from $BACKUP_NAME"

Emergency Rollback

# Fastest possible rollback
LAST_BACKUP=$(aether list-backups | head -2 | tail -1 | awk '{print $2}')
aether restore $LAST_BACKUP

Database Rollback

#!/bin/bash
# database-rollback.sh

# Stop application writes
kubectl scale deployment/$SERVICE_NAME --replicas=0

# Restore database backup
kubectl exec -it postgres-0 -- pg_restore \
  -U postgres \
  -d production \
  -c \
  /backups/database-$(date -d yesterday +%Y%m%d).dump

# Restart application
kubectl scale deployment/$SERVICE_NAME --replicas=3

Scaling Operations

Manual Scaling

# Scale up
kubectl scale deployment/$SERVICE_NAME --replicas=10

# Scale down (during low traffic)
kubectl scale deployment/$SERVICE_NAME --replicas=3

# Check scaling status
kubectl get hpa
kubectl top pods -n production

Auto-Scaling Tuning

# Adjust HPA targets
kubectl autoscale deployment/$SERVICE_NAME \
  --cpu-percent=70 \
  --min=3 \
  --max=20

# Check HPA status
kubectl describe hpa $SERVICE_NAME

# View scaling events
kubectl get events | grep HorizontalPodAutoscaler

Database Scaling

# Add read replica
aether -s workloads/database-replica.yaml run

# Update application to use replica for reads
kubectl set env deployment/$SERVICE_NAME \
  READ_REPLICA_HOST=postgres-replica.production.svc.cluster.local

# Verify replica lag
kubectl exec -it postgres-replica-0 -- psql -U postgres -c "
  SELECT now() - pg_last_xact_replay_timestamp() AS replication_lag;"

Backup & Recovery

Daily Backup Procedure

#!/bin/bash
# daily-backup.sh

DATE=$(date +%Y%m%d)

# Aether state backup
aether backup -n "daily-$DATE" -d "Automated daily backup"

# Database backup
kubectl exec -it postgres-0 -- pg_dump \
  -U postgres \
  -Fc \
  production > /backups/database-$DATE.dump

# Upload to S3
aws s3 cp /backups/database-$DATE.dump \
  s3://company-backups/aether/database-$DATE.dump

# Cleanup old backups (keep 30 days)
find /backups -name "*.dump" -mtime +30 -delete
aether list-backups | tail -n +32 | awk '{print $2}' | xargs -I {} rm {}

Disaster Recovery Test

#!/bin/bash
# dr-test.sh

# Monthly DR test procedure

# 1. Spin up DR environment
kubectl create namespace dr-test

# 2. Restore from backup
LATEST_BACKUP=$(aether list-backups | head -1 | awk '{print $2}')
aether restore $LATEST_BACKUP --namespace dr-test

# 3. Restore database
kubectl exec -it postgres-0 -n dr-test -- pg_restore \
  -U postgres \
  -d production \
  /backups/latest.dump

# 4. Run smoke tests
./scripts/smoke-test-dr.sh

# 5. Document results
echo "DR test completed: $(date)" >> dr-test-log.txt

# 6. Cleanup
kubectl delete namespace dr-test

Security Incidents

Suspected Breach Procedure

# 1. IMMEDIATE ACTIONS
# - Isolate affected systems
kubectl cordon $AFFECTED_NODE
kubectl drain $AFFECTED_NODE --ignore-daemonsets

# - Revoke credentials
kubectl delete secret $COMPROMISED_SECRET
# Rotate all API keys and passwords

# - Enable audit logging
kubectl apply -f audit-policy.yaml

# 2. INVESTIGATION
# - Collect logs
kubectl logs -n production --all-containers=true --since=24h > incident-logs.txt

# - Check for suspicious activity
grep "unauthorized" incident-logs.txt
grep "failed login" incident-logs.txt

# - Review access logs
kubectl get events --sort-by='.lastTimestamp' | tail -100

# 3. CONTAINMENT
# - Block suspicious IPs
kubectl apply -f network-policy-lockdown.yaml

# - Disable compromised accounts
kubectl delete serviceaccount $COMPROMISED_SA

# 4. RECOVERY
# - Deploy patched versions
aether -s workloads-patched/$SERVICE_NAME.yaml run

# - Verify integrity
./scripts/verify-checksums.sh

# 5. POST-INCIDENT
# - Write incident report
# - Update security policies
# - Schedule security audit

CVE Response

#!/bin/bash
# cve-response.sh

CVE_ID=$1
SEVERITY=$2

echo "Responding to $CVE_ID (Severity: $SEVERITY)"

# 1. Assess impact
trivy image --severity $SEVERITY ghcr.io/company/app:latest

# 2. Update base images
# (requires Dockerfile changes)

# 3. Rebuild images
docker build -t ghcr.io/company/app:patched .
docker push ghcr.io/company/app:patched

# 4. Deploy updates
yq eval '.image.tag = "patched"' -i workloads/*.yaml
for service in workloads/*.yaml; do
    aether -s "$service" run
done

# 5. Verify patches
trivy image ghcr.io/company/app:patched

Performance Tuning

Database Performance

# Analyze slow queries
kubectl exec -it postgres-0 -- psql -U postgres -c "
  SELECT query, calls, total_time, mean_time
  FROM pg_stat_statements
  ORDER BY mean_time DESC
  LIMIT 10;"

# Add indexes
kubectl exec -it postgres-0 -- psql -U postgres -c "
  CREATE INDEX CONCURRENTLY idx_users_email ON users(email);"

# Vacuum and analyze
kubectl exec -it postgres-0 -- psql -U postgres -c "
  VACUUM ANALYZE;"

Cache Optimization

# Check cache hit rate
CACHE_HIT_RATE=$(kubectl exec -it redis-0 -- redis-cli INFO stats | grep keyspace_hits | awk -F: '{print $2}')
echo "Cache hit rate: $CACHE_HIT_RATE%"

# Adjust cache size
kubectl set env deployment/redis MAXMEMORY=16gb

# Tune eviction policy
kubectl exec -it redis-0 -- redis-cli CONFIG SET maxmemory-policy allkeys-lru

Application Optimization

# Enable HTTP/2
kubectl annotate ingress $SERVICE_NAME \
  nginx.ingress.kubernetes.io/http2-push-preload=true

# Enable compression
kubectl annotate ingress $SERVICE_NAME \
  nginx.ingress.kubernetes.io/enable-compression=true

# Adjust worker processes
kubectl set env deployment/$SERVICE_NAME WORKERS=4

Maintenance Windows

Planned Maintenance Procedure

#!/bin/bash
# planned-maintenance.sh

# Typically scheduled for Sunday 2-4 AM

# 1. Pre-maintenance
echo "Starting pre-maintenance checks..."
aether backup -n "pre-maintenance-$(date +%Y%m%d)"
./scripts/smoke-test-all.sh

# 2. Announce maintenance
# Post in #announcements and update status page

# 3. Scale up for redundancy
kubectl scale deployment/api-gateway --replicas=5

# 4. Perform maintenance
# - Apply updates
# - Run migrations
# - Update configurations

# 5. Verify functionality
./scripts/smoke-test-all.sh

# 6. Monitor for issues
sleep 600  # 10 minutes

# 7. Scale back to normal
kubectl scale deployment/api-gateway --replicas=3

# 8. Complete maintenance
# Update status page
echo "Maintenance completed: $(date)"

Database Maintenance

# Vacuum full (requires downtime)
kubectl scale deployment/$SERVICE_NAME --replicas=0
kubectl exec -it postgres-0 -- psql -U postgres -c "VACUUM FULL;"
kubectl scale deployment/$SERVICE_NAME --replicas=3

# Reindex
kubectl exec -it postgres-0 -- psql -U postgres -c "REINDEX DATABASE production;"

# Update statistics
kubectl exec -it postgres-0 -- psql -U postgres -c "ANALYZE;"

Appendix

Useful Commands

# Quick health check all services
kubectl get pods -n production -o wide

# Check recent events
kubectl get events --sort-by='.lastTimestamp' | tail -20

# View resource usage
kubectl top nodes
kubectl top pods -n production

# Check ingress status
kubectl get ingress -n production

# View logs from all pods
kubectl logs -n production -l app=$SERVICE_NAME --all-containers=true

# Execute command in pod
kubectl exec -it $POD_NAME -- /bin/bash

# Port forward for debugging
kubectl port-forward svc/$SERVICE_NAME 8080:8080

# Copy files from pod
kubectl cp $POD_NAME:/path/to/file ./local-file

Contact Information

Platform Team: platform-team@company.com
Database Team: database-team@company.com
Security Team: security-team@company.com
DevOps Lead: devops-lead@company.com

Vendor Support:
- Cloud Provider: 1-800-XXX-XXXX
- Database: support@database.com
- Monitoring: support@monitoring.com

Change Log

Date Author Changes
2024-02-06 Platform Team Initial runbook creation
Added incident response procedures
Added rollback procedures

Support

For runbook questions or updates: - GitHub Issues: https://github.com/zyvorai/Aether/issues - Tag: runbook