
Docker Compose for Production Web Deployments
- AuthorLiam K.
- Date
- Time20 minutes
Goal
Deploy an app, a PostgreSQL database, and Nginx reverse proxy using a reproducible Docker Compose setup.
Step 1: Install Docker and Compose Plugin
bash
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USERStep 2: Create Project Layout
bash
mkdir -p ~/prod-stack/{app,nginx,db,logs}
cd ~/prod-stackStep 3: Add Environment File
bash
cat > .env <<'EOF'
APP_PORT=3000
POSTGRES_DB=appdb
POSTGRES_USER=appuser
POSTGRES_PASSWORD=replace_with_long_password
EOFStep 4: Create docker-compose.yml
yaml
cat > docker-compose.yml <<'EOF'
services:
app:
image: node:20-alpine
working_dir: /app
command: sh -c "npm ci && npm run start"
volumes:
- ./app:/app
env_file:
- .env
depends_on:
db:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 20s
timeout: 5s
retries: 5
db:
image: postgres:16-alpine
env_file:
- .env
volumes:
- pg_data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 10
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
app:
condition: service_healthy
restart: unless-stopped
volumes:
pg_data:
EOFStep 5: Add Nginx Proxy Config
nginx
cat > nginx/default.conf <<'EOF'
server {
listen 80;
server_name _;
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;
}
}
EOFStep 6: Start Stack and Validate
bash
docker compose pull
docker compose up -d
docker compose ps
docker compose logs -f --tail=100Step 7: Safe Update Procedure
bash
git pull
docker compose build --pull
docker compose up -d --remove-orphans
docker image prune -fStep 8: Backups for Persistent Data
bash
docker exec -t $(docker compose ps -q db) pg_dump -U appuser appdb > backup_$(date +%F).sql"Compose is excellent for small and medium production systems if you treat it like infrastructure code and keep updates disciplined."



