Compose is a practical deployment tool for a single host when you use immutable images, persistent storage, private service networks, health checks, restart policies, backups, and an explicit rollback procedure. This guide shows the deployment workflow rather than pretending Compose is a multi-node orchestrator.
1. Prepare the deployment directory
sudo install -d -m 0750 -o "$USER" -g docker /opt/myapp
cd /opt/myapp
umask 077
2. Define the base application
services:
web:
image: registry.example.com/myapp/web:1.4.2
environment:
APP_ENV: production
DATABASE_URL_FILE: /run/secrets/database_url
secrets:
- database_url
depends_on:
db:
condition: service_healthy
networks: [frontend, backend]
db:
image: postgres:17.5-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets: [db_password]
volumes:
- db-data:/var/lib/postgresql/data
networks: [backend]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 10s
timeout: 5s
retries: 10
networks:
frontend:
backend:
internal: true
volumes:
db-data:
secrets:
database_url:
file: ./secrets/database_url.txt
db_password:
file: ./secrets/db_password.txt
Save this as compose.yaml. Store secrets with mode 600 and never commit them.
3. Add production-only settings
services:
web:
ports:
- "127.0.0.1:8080:8080"
restart: always
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:true
cap_drop: [ALL]
db:
restart: always
Save that as compose.production.yaml. A host Nginx or another reverse proxy can terminate TLS and forward to 127.0.0.1:8080.
4. Render and deploy the exact model
docker login registry.example.com
docker compose -f compose.yaml -f compose.production.yaml config --quiet
docker compose -f compose.yaml -f compose.production.yaml config --images
docker compose -f compose.yaml -f compose.production.yaml pull
docker compose -f compose.yaml -f compose.production.yaml up -d --wait --wait-timeout 120
docker compose -f compose.yaml -f compose.production.yaml ps
5. Validate after deployment
curl -fsS http://127.0.0.1:8080/healthz
docker compose -f compose.yaml -f compose.production.yaml logs --tail 100
docker compose -f compose.yaml -f compose.production.yaml stats --no-stream
sudo ss -lntp | grep ':8080'
6. Back up before an update
mkdir -p backups
docker compose exec -T db pg_dump -U app -d app -Fc >
"backups/app-$(date -u +%Y%m%dT%H%M%SZ).dump"
ls -lh backups/
7. Deploy and roll back one service
Change the web tag from 1.4.2 to a tested immutable version, then:
docker compose -f compose.yaml -f compose.production.yaml pull web
docker compose -f compose.yaml -f compose.production.yaml up -d --no-deps --wait web
curl -fsS http://127.0.0.1:8080/healthz
If validation fails, restore the previous tag in compose.yaml and recreate only the web service:
docker compose -f compose.yaml -f compose.production.yaml up -d --no-deps web
docker compose -f compose.yaml -f compose.production.yaml logs --tail 100 web
8. Operate safely
docker compose -f compose.yaml -f compose.production.yaml ps
docker system df
journalctl -u docker --since today --no-pager
Do not use down --volumes in production. Monitor disk, backup restores, certificate renewal, container health, restart counts, and application-level transactions.
References: Docker documentation and the Server World topic index.