Skip to content
advanced Phase 8 · Docker Project

Build & Deploy

Implement Dockerfiles, Compose configs, and deploy the application.

2h
0 problems
Topic Progress 0%

Production Build Process

Production Build Process

Build Production Images

# Build frontend
docker build -t myregistry.com/taskmanager-frontend:1.0.0 ./frontend

# Build API
docker build -t myregistry.com/taskmanager-api:1.0.0 ./api

# Verify image sizes
docker images myregistry/*
# REPOSITORY                           TAG     SIZE
# myregistry.com/taskmanager-frontend   1.0.0   25MB
# myregistry.com/taskmanager-api        1.0.0   120MB

Push to Registry

# Log in to registry
docker login myregistry.com

# Push images
docker push myregistry.com/taskmanager-frontend:1.0.0
docker push myregistry.com/taskmanager-api:1.0.0

# Push with git SHA tag
git rev-parse --short HEAD
# abc1234
docker tag myregistry.com/taskmanager-api:1.0.0 myregistry.com/taskmanager-api:abc1234
docker push myregistry.com/taskmanager-api:abc1234

Build Scripts

#!/bin/bash
# scripts/build.sh
set -e

REGISTRY="myregistry.com"
VERSION=$(git rev-parse --short HEAD)

echo "Building images for version: $VERSION"

# Build frontend
docker build -t ${REGISTRY}/taskmanager-frontend:${VERSION} \
  -t ${REGISTRY}/taskmanager-frontend:latest \
  ./frontend

# Build API
docker build -t ${REGISTRY}/taskmanager-api:${VERSION} \
  -t ${REGISTRY}/taskmanager-api:latest \
  ./api

# Push
docker push ${REGISTRY}/taskmanager-frontend:${VERSION}
docker push ${REGISTRY}/taskmanager-frontend:latest
docker push ${REGISTRY}/taskmanager-api:${VERSION}
docker push ${REGISTRY}/taskmanager-api:latest

echo "Build complete!"

CI/CD with GitHub Actions

# .github/workflows/deploy.yml
name: Build and Deploy

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          registry: ${{ secrets.REGISTRY }}
          username: ${{ secrets.REGISTRY_USER }}
          password: ${{ secrets.REGISTRY_PASSWORD }}
      
      - name: Build and push frontend
        uses: docker/build-push-action@v5
        with:
          context: ./frontend
          push: true
          tags: ${{ secrets.REGISTRY }}/taskmanager-frontend:${{ github.sha }}
      
      - name: Build and push API
        uses: docker/build-push-action@v5
        with:
          context: ./api
          push: true
          tags: ${{ secrets.REGISTRY }}/taskmanager-api:${{ github.sha }}
      
      - name: Deploy to production
        run: |
          ssh deploy@server "cd /opt/taskmanager && \
            docker compose -f docker-compose.prod.yml pull && \
            docker compose -f docker-compose.prod.yml up -d"

Docker Compose Build

# Build all services
docker compose build

# Build with no cache
docker compose build --no-cache

# Build and push
docker compose build && docker compose push

# Use build args
docker compose build --build-arg NODE_VERSION=20

Production Deployment

Production Deployment

Server Setup

# SSH into production server
ssh deploy@production-server

# Install Docker
curl -fsSL https://get.docker.com | sh

# Create project directory
sudo mkdir -p /opt/taskmanager
cd /opt/taskmanager

# Copy compose files
scp docker-compose.prod.yml deploy@production-server:/opt/taskmanager/

# Copy .env
scp .env deploy@production-server:/opt/taskmanager/

# Create .env file
cat > .env <<EOF
POSTGRES_DB=taskmanager
POSTGRES_USER=taskadmin
POSTGRES_PASSWORD=secure_password_here
DATABASE_URL=postgresql://taskadmin:secure_password_here@db:5432/taskmanager
REDIS_URL=redis://redis:6379
EOF

Deploy with Compose

# Pull and start services
cd /opt/taskmanager
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d

# Check status
docker compose ps

# View logs
docker compose logs -f

# Verify health
curl http://localhost:4000/health
curl http://localhost:80

Rolling Updates

# docker-compose.prod.yml with rolling updates
services:
  api:
    deploy:
      replicas: 3
      update_config:
        parallelism: 1        # Update one at a time
        delay: 10s            # Wait 10s between updates
        failure_action: rollback
        order: start-first    # Start new before stopping old
      rollback_config:
        parallelism: 1
        delay: 5s
# Update with rolling deployment
docker compose -f docker-compose.prod.yml up -d --no-deps api

# Scale API
docker compose -f docker-compose.prod.yml up -d --scale api=3

Database Migrations

# Run migrations before deploying new code
docker compose -f docker-compose.prod.yml exec api node migrate.js

# Or as a one-off container
docker compose -f docker-compose.prod.yml run --rm api node migrate.js

SSL/TLS with Let's Encrypt

services:
  nginx:
    image: nginx:1.25
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - certbot_data:/etc/letsencrypt
      - certbot_www:/var/www/certbot
    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"

  certbot:
    image: certbot/certbot
    volumes:
      - certbot_data:/etc/letsencrypt
      - certbot_www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

volumes:
  certbot_data:
  certbot_www:

Backup Strategy

#!/bin/bash
# scripts/backup.sh
BACKUP_DIR="/backups/$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR

# Backup database
docker compose -f docker-compose.prod.yml exec -T db pg_dumpall -U taskadmin > $BACKUP_DIR/db.sql

# Backup volumes
docker run --rm \
  -v taskmanager_pgdata:/source:ro \
  -v $BACKUP_DIR:/backup \
  alpine tar czf /backup/pgdata.tar.gz -C /source .

# Backup images
docker save myregistry.com/taskmanager-api:latest | gzip > $BACKUP_DIR/api.tar.gz

# Clean old backups (keep 7 days)
find /backups -type d -mtime +7 -exec rm -rf {} +

echo "Backup complete: $BACKUP_DIR"

Monitoring

# Check container health
docker compose ps

# View resource usage
docker stats --no-stream

# Check logs for errors
docker compose logs --tail 100 api | grep -i error

# Health check endpoint
curl -s http://localhost:4000/health | jq

Production Troubleshooting

Production Troubleshooting

Common Production Issues

1. Container won't start:

# Check container status
docker compose ps -a

# Check logs
docker compose logs api

# Common: Missing environment variable
docker compose exec api env | grep DATABASE_URL

# Common: Permission issues
docker compose exec api ls -la /app

2. Health check failing:

# Test health endpoint manually
docker compose exec api wget -qO- http://localhost:4000/health

# Check if service is listening
docker compose exec api ss -tlnp

# Check resource usage
docker stats api

3. Database connection refused:

# Check database is running
docker compose ps db

# Check database health
docker compose exec db pg_isready -U taskadmin

# Test connection from API
docker compose exec api pg_isready -h db -p 5432

# Check database logs
docker compose logs db | tail -20

4. Out of memory:

# Check OOM kills
docker inspect api | jq '.[0].State.OOMKilled'

# Increase memory limit
docker compose -f docker-compose.prod.yml up -d --no-deps api

# Monitor memory usage
docker stats api --no-stream

5. Disk space:

# Check disk usage
docker system df

# Clean unused resources
docker system prune -a --volumes

# Check log sizes
du -sh /var/lib/docker/containers/*/

Deployment Checklist

# Pre-deployment
□ Run tests locally
□ Build and test images
□ Update .env with production values
□ Run database migrations
□ Backup existing data

# Deployment
□ Pull new images
□ Stop old containers
□ Start new containers
□ Verify health checks
□ Run smoke tests
□ Monitor logs for errors

# Post-deployment
□ Verify application works
□ Check performance metrics
□ Update documentation
□ Notify team

Rollback Procedure

# If deployment fails, rollback to previous version
cd /opt/taskmanager

# Edit docker-compose.prod.yml to use previous image tag
# Then:
docker compose -f docker-compose.prod.yml up -d

# Or restore from backup
bash scripts/restore.sh <backup_date>

# Verify rollback
curl http://localhost:4000/health
docker compose logs --tail 50 api