Docker and Docker Compose Rollback Guides
Platform-specific procedures and best practices for Docker deployments.
Table of Contents
- Docker Container Rollback
- Docker Compose Rollback
- Volume and Data Management
- Network Rollback
- Multi-Container Coordination
Docker Container Rollback
Basic Container Rollback
Scenario: Single container needs rollback to previous version
# 1. List available images
docker images | grep myapp
# myapp v1.2.4 abc123 2 hours ago 500MB
# myapp v1.2.3 def456 1 day ago 498MB
# myapp v1.2.2 ghi789 2 days ago 495MB
# 2. Stop current container
docker stop myapp-container
# 3. Remove current container (preserves volumes if using named volumes)
docker rm myapp-container
# 4. Start container with previous image
docker run -d \
--name myapp-container \
--network myapp-network \
-p 8080:8080 \
-v myapp-data:/data \
myapp:v1.2.3
# 5. Validate
docker ps | grep myapp
docker logs myapp-container --tail 50
Rollback Without Downtime (Blue-Green Pattern)
Run old and new versions simultaneously, switch traffic:
# Current state: v1.2.4 running on port 8080
# 1. Start old version on different port
docker run -d \
--name myapp-v1.2.3 \
--network myapp-network \
-p 8081:8080 \
myapp:v1.2.3
# 2. Validate old version works
curl http://localhost:8081/health
# 3. Update load balancer/reverse proxy to point to port 8081
# (nginx config change, docker network alias, etc.)
# 4. Stop new version
docker stop myapp-container
docker rm myapp-container
# 5. Rename old version to standard name
docker rename myapp-v1.2.3 myapp-container
# 6. Remap to standard port (if needed)
docker stop myapp-container
docker run -d \
--name myapp-container \
--network myapp-network \
-p 8080:8080 \
myapp:v1.2.3
Container Rollback with Volume Preservation
Ensure data persists across rollback:
# 1. Identify volumes in use
docker inspect myapp-container | grep -A 10 Mounts
# 2. Stop and remove container (volumes persist)
docker stop myapp-container
docker rm myapp-container
# 3. Start with previous image, same volumes
docker run -d \
--name myapp-container \
--network myapp-network \
-p 8080:8080 \
-v myapp-data:/data \
-v myapp-config:/etc/myapp \
myapp:v1.2.3
# 4. Verify volume data intact
docker exec myapp-container ls -la /data
Docker Compose Rollback
Full Stack Rollback
Rollback entire docker-compose application:
# 1. Backup current compose file
cp docker-compose.yml docker-compose.yml.backup
# 2. Restore previous compose file from git
git checkout HEAD~1 -- docker-compose.yml
# 3. Stop current services
docker-compose down
# 4. Start with previous configuration
docker-compose up -d
# 5. Validate all services
docker-compose ps
docker-compose logs --tail 50
Selective Service Rollback
Rollback specific service within compose stack:
# Current docker-compose.yml:
# services:
# app:
# image: myapp:v1.2.4
# db:
# image: postgres:14
# 1. Update only app service image version
sed -i 's/myapp:v1.2.4/myapp:v1.2.3/g' docker-compose.yml
# 2. Recreate only app service
docker-compose up -d --no-deps app
# 3. Validate
docker-compose ps app
docker-compose logs app --tail 50
# --no-deps prevents recreating linked services (db stays running)
Rollback with Environment Changes
Revert environment variables or config:
# 1. Restore previous .env file
git checkout HEAD~1 -- .env
# 2. Recreate services to pick up new env vars
docker-compose up -d --force-recreate
# Alternative: Update specific env var inline
docker-compose up -d \
-e APP_VERSION=v1.2.3 \
-e FEATURE_FLAG=false \
app
Pinned Version Rollback
If using image tags in compose file:
# docker-compose.yml
services:
app:
image: myapp:v1.2.4 # Change to v1.2.3
ports:
- "8080:8080"
db:
image: postgres:14
volumes:
- db-data:/var/lib/postgresql/data
# 1. Edit docker-compose.yml to change image tag
sed -i 's/myapp:v1.2.4/myapp:v1.2.3/g' docker-compose.yml
# 2. Pull previous image (if not cached)
docker-compose pull app
# 3. Recreate app service
docker-compose up -d app
# 4. Validate
docker-compose logs app --tail 50
Build-Based Rollback
If building from Dockerfile:
# docker-compose.yml
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
# 1. Checkout previous source code
git checkout HEAD~1
# 2. Rebuild and restart
docker-compose build app
docker-compose up -d app
# Or rebuild from specific git commit
git checkout abc123
docker-compose build app
docker-compose up -d app
Volume and Data Management
Volume Rollback Strategies
Strategy 1: Volume Snapshots (Manual Backup)
# Before risky changes, backup volume data
# 1. Stop services using volume
docker-compose stop app
# 2. Create backup
docker run --rm \
-v myapp-data:/source \
-v $(pwd)/backups:/backup \
alpine \
tar czf /backup/myapp-data-$(date +%Y%m%d_%H%M%S).tar.gz -C /source .
# 3. Restart services
docker-compose up -d app
# To restore:
# 1. Stop services
docker-compose stop app
# 2. Restore from backup
docker run --rm \
-v myapp-data:/target \
-v $(pwd)/backups:/backup \
alpine \
sh -c "rm -rf /target/* && tar xzf /backup/myapp-data-20260215_120000.tar.gz -C /target"
# 3. Start services
docker-compose up -d app
Strategy 2: Volume Cloning
# Clone volume before changes
# 1. Create new volume
docker volume create myapp-data-backup
# 2. Copy data to backup volume
docker run --rm \
-v myapp-data:/source:ro \
-v myapp-data-backup:/target \
alpine \
sh -c "cp -a /source/. /target/"
# If rollback needed:
# 1. Stop services
docker-compose stop app
# 2. Remove current volume
docker volume rm myapp-data
# 3. Clone backup back
docker volume create myapp-data
docker run --rm \
-v myapp-data-backup:/source:ro \
-v myapp-data:/target \
alpine \
sh -c "cp -a /source/. /target/"
# 4. Restart services
docker-compose up -d app
Strategy 3: Bind Mounts for Easy Backup
Use bind mounts for critical data:
# docker-compose.yml
services:
app:
volumes:
- ./data:/app/data # Bind mount for easy backup
# Rollback bind mount data
# 1. Stop services
docker-compose stop app
# 2. Restore from git or backup
cp -r data.backup/* data/
# or
git checkout HEAD~1 -- data/
# 3. Restart
docker-compose up -d app
Network Rollback
Network Configuration Changes
Rollback network settings:
# Current state: Using custom network with specific subnet
# 1. List current networks
docker network ls
docker network inspect myapp-network
# 2. Stop services
docker-compose down
# 3. Remove current network
docker network rm myapp-network
# 4. Recreate previous network configuration
docker network create \
--driver bridge \
--subnet 172.20.0.0/16 \
--gateway 172.20.0.1 \
myapp-network
# 5. Restart services
docker-compose up -d
# Services will reconnect to recreated network
Service Discovery Rollback
If using Docker DNS for service discovery:
# docker-compose.yml
services:
app:
networks:
default:
aliases:
- api-server # Changed alias
db:
networks:
default:
aliases:
- database
# Rollback network aliases
# 1. Restore previous docker-compose.yml
git checkout HEAD~1 -- docker-compose.yml
# 2. Recreate services with old aliases
docker-compose down
docker-compose up -d
# DNS names revert to previous configuration
Multi-Container Coordination
Coordinated Rollback (Database + Application)
Critical: Rollback in correct order:
# Scenario: App v1.2.4 + Database migration applied
# Need to rollback both
# 1. Stop application (prevent new requests)
docker-compose stop app
# 2. Rollback database migration
docker exec myapp-db alembic downgrade -1
# 3. Verify schema rollback
docker exec myapp-db psql -U user -d dbname -c "\d users"
# 4. Rollback application
sed -i 's/myapp:v1.2.4/myapp:v1.2.3/g' docker-compose.yml
docker-compose up -d app
# 5. Validate stack
docker-compose ps
docker-compose logs app --tail 50
docker-compose logs db --tail 50
Dependency-Aware Rollback
When services have dependencies:
# docker-compose.yml
services:
app:
depends_on:
- db
- cache
image: myapp:v1.2.4
db:
image: postgres:14
cache:
image: redis:7
# Rollback with dependency awareness
# 1. Identify dependency graph
# app depends on db, cache
# db and cache are independent
# 2. Rollback in reverse dependency order:
# First: app (depends on others)
docker-compose stop app
sed -i 's/myapp:v1.2.4/myapp:v1.2.3/g' docker-compose.yml
docker-compose up -d app
# Then: db and cache (if needed)
# Usually not needed unless they changed too
# 3. Validate
docker-compose ps
Health Check Integration
Use health checks to validate rollback:
# docker-compose.yml
services:
app:
image: myapp:v1.2.3
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
# After rollback, wait for health check
docker-compose up -d app
# Monitor health status
watch docker-compose ps
# app service should show "healthy" status
# If not healthy after start_period, rollback failed
Automated Rollback Scripts
Quick Rollback Script
#!/bin/bash
# quick-rollback.sh - Rollback to previous version
set -e
SERVICE_NAME=${1:-app}
PREVIOUS_VERSION=${2}
if [ -z "$PREVIOUS_VERSION" ]; then
echo "Usage: ./quick-rollback.sh <service-name> <previous-version>"
echo "Example: ./quick-rollback.sh app v1.2.3"
exit 1
fi
echo "Rolling back $SERVICE_NAME to $PREVIOUS_VERSION..."
# Stop service
docker-compose stop "$SERVICE_NAME"
# Update compose file
sed -i "s/${SERVICE_NAME}:.*/${SERVICE_NAME}:${PREVIOUS_VERSION}/g" docker-compose.yml
# Restart service
docker-compose up -d "$SERVICE_NAME"
# Show status
docker-compose ps "$SERVICE_NAME"
docker-compose logs "$SERVICE_NAME" --tail 20
echo "Rollback complete. Monitor logs: docker-compose logs -f $SERVICE_NAME"
Safe Rollback with Validation
#!/bin/bash
# safe-rollback.sh - Rollback with health checks
set -e
SERVICE_NAME=${1:-app}
PREVIOUS_VERSION=${2}
HEALTH_ENDPOINT=${3:-http://localhost:8080/health}
# Backup current state
docker-compose config > docker-compose-backup-$(date +%Y%m%d_%H%M%S).yml
# Rollback
docker-compose stop "$SERVICE_NAME"
sed -i "s/${SERVICE_NAME}:.*/${SERVICE_NAME}:${PREVIOUS_VERSION}/g" docker-compose.yml
docker-compose up -d "$SERVICE_NAME"
# Wait for service to start
echo "Waiting for service to start..."
sleep 10
# Health check
for i in {1..30}; do
if curl -sf "$HEALTH_ENDPOINT" > /dev/null; then
echo "✓ Service healthy"
docker-compose ps "$SERVICE_NAME"
exit 0
fi
echo "Waiting for health check... ($i/30)"
sleep 2
done
echo "✗ Health check failed after rollback"
echo "Service logs:"
docker-compose logs "$SERVICE_NAME" --tail 50
exit 1
Best Practices
- Tag images with versions - Use semantic versioning or git SHAs
- Keep previous images - Don't prune immediately after deploy
- Use named volumes - Easier to backup and restore than anonymous volumes
- Pin image versions - Don't use
latestin production - Test compose files -
docker-compose configvalidates syntax - Health checks are critical - Define healthchecks for all services
- Backup before changes - Especially volumes with critical data
- Document dependencies - Track which services depend on others
- Use .env files - Easier to version and rollback environment vars
- Practice rollbacks - Regular drills ensure procedures work