TechLead
Lesson 16 of 18
5 min read
Docker & DevOps

Deploying with Docker

Deploy Docker containers to production using cloud platforms like AWS, Google Cloud, and DigitalOcean

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: always for production services
  • ✅ Set resource limits (CPU/memory)
  • ✅ Back up database volumes regularly

Continue Learning