Deployment Strategies
Docker simplifies deployment because your application runs the same way everywhere. There are several ways to deploy Docker containers to production, from simple VPS deployments to managed container services.
Deployment Options
- VPS + Docker Compose: Simple, affordable — DigitalOcean, Linode, Hetzner
- AWS ECS / Fargate: Managed containers on AWS without managing servers
- Google Cloud Run: Serverless containers that scale to zero
- Azure Container Apps: Managed containers on Azure
- Fly.io / Railway: Developer-friendly PaaS for Docker containers
VPS Deployment with Docker Compose
# 1. SSH into your server
ssh user@your-server.com
# 2. Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# 3. Clone your project
git clone https://github.com/user/myapp.git
cd myapp
# 4. Create .env with production values
cp .env.example .env
nano .env
# 5. Start the application
docker compose -f docker-compose.prod.yml up -d
# 6. Check status
docker compose ps
docker compose logs -f
Production Compose File
# docker-compose.prod.yml
services:
app:
image: ghcr.io/user/myapp:latest
ports:
- "3000:3000"
env_file: .env
restart: always
deploy:
resources:
limits:
memory: 512M
cpus: '1.0'
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 30s
retries: 3
db:
image: postgres:16-alpine
env_file: .env
volumes:
- pgdata:/var/lib/postgresql/data
restart: always
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- /etc/letsencrypt:/etc/letsencrypt:ro
depends_on:
- app
restart: always
volumes:
pgdata:
Google Cloud Run
# Build and push to Google Container Registry
gcloud builds submit --tag gcr.io/PROJECT_ID/myapp
# Deploy to Cloud Run
gcloud run deploy myapp \
--image gcr.io/PROJECT_ID/myapp \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--set-env-vars="NODE_ENV=production" \
--memory=512Mi \
--min-instances=0 \
--max-instances=10
AWS ECS with Fargate
# Push to ECR (Elastic Container Registry)
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin ACCOUNT.dkr.ecr.us-east-1.amazonaws.com
docker tag myapp:latest ACCOUNT.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
docker push ACCOUNT.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
# Deploy with ECS (using AWS Copilot CLI)
copilot init --app myapp --type "Load Balanced Web Service"
copilot deploy
Zero-Downtime Deployment
# Pull new image
docker compose -f docker-compose.prod.yml pull
# Recreate only changed services (zero-downtime)
docker compose -f docker-compose.prod.yml up -d --no-deps app
# Verify health
docker compose ps
curl -f http://localhost:3000/health
SSL/TLS with Nginx & Let's Encrypt
# nginx.conf
server {
listen 80;
server_name myapp.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name myapp.com;
ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;
location / {
proxy_pass http://app:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Deployment Checklist
- ✅ Use specific image tags (not
latest) - ✅ Configure health checks for all services
- ✅ Set up HTTPS with SSL certificates
- ✅ Use
restart: alwaysfor production services - ✅ Set resource limits (CPU/memory)
- ✅ Back up database volumes regularly