Skip to main content
/ DevOps

Docker Production Deployment: The Complete Swarm & Compose Guide for 2026

Sacha Roussakis-NotterSacha Roussakis-Notter
18 min read
Docker
PostgreSQL
Share

Production-ready Docker deployments with Swarm and Compose. Covers secrets management, health checks, networking, rolling updates, and the 42 best practices every DevOps engineer needs.

Why Docker for Production in 2026?

While Kubernetes dominates enterprise conversations, Docker Swarm and Compose remain the pragmatic choice for teams that don't need—or can't afford—Kubernetes complexity.

Swarm is still the easiest orchestrator to explain to ops teams that don't need Kubernetes-level complexity. Docker Compose is now production-capable with proper configuration.

flowchart

Production Container Deployment

Team and Scale?

Docker Compose

Docker Swarm

Kubernetes or K3s

Small team, single host - simple setup, docker compose up

Medium team, multi-host HA - built-in HA, native secrets

Large team, complex needs - full ecosystem, service mesh

Ctrl+scroll to zoom • Drag to pan46%

Docker Compose for Production

The 2026 Docker Compose Changes

Modern Docker Compose has evolved significantly:

ChangeOld WayNew Way (2026)
Version fieldversion: "3.8"Omit entirely
Commanddocker-composedocker compose
BuildOn-hostMulti-stage, CI/CD
SecretsEnvironment variablesDocker secrets or vaults
Health checksOptionalRequired

Production-Ready Compose Template

yaml
1# docker-compose.yml - No version field needed in 2026
2services:
3 web:
4 image: myapp/web:1.5.2
5 build:
6 context: .
7 dockerfile: Dockerfile
8 target: production
9 user: "1000:1000"
10 read_only: true
11 tmpfs:
12 - /tmp
13 - /var/run
14 deploy:
15 resources:
16 limits:
17 cpus: '1.0'
18 memory: 512M
19 reservations:
20 cpus: '0.25'
21 memory: 128M
22 healthcheck:
23 test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"]
24 interval: 30s
25 timeout: 10s
26 retries: 3
27 start_period: 40s
28 environment:
29 - NODE_ENV=production
30 env_file:
31 - .env.production
32 secrets:
33 - db_password
34 - jwt_secret
35 ports:
36 - "3000:3000"
37 networks:
38 - frontend
39 depends_on:
40 db:
41 condition: service_healthy
42 logging:
43 driver: "json-file"
44 options:
45 max-size: "10m"
46 max-file: "5"
47
48 db:
49 image: postgres:16-alpine
50 user: "999:999"
51 volumes:
52 - postgres_data:/var/lib/postgresql/data
53 environment:
54 POSTGRES_DB: myapp
55 POSTGRES_USER: myapp
56 secrets:
57 - source: db_password
58 target: /run/secrets/postgres_password
59 healthcheck:
60 test: ["CMD-SHELL", "pg_isready -U myapp"]
61 interval: 10s
62 timeout: 5s
63 retries: 5
64 networks:
65 - backend
66 deploy:
67 resources:
68 limits:
69 cpus: '2.0'
70 memory: 1G
71
72 redis:
73 image: redis:7-alpine
74 command: redis-server --appendonly yes --requirepass_file /run/secrets/redis_password
75 volumes:
76 - redis_data:/data
77 secrets:
78 - redis_password
79 healthcheck:
80 test: ["CMD", "redis-cli", "ping"]
81 interval: 10s
82 timeout: 5s
83 retries: 5
84 networks:
85 - backend
86 deploy:
87 resources:
88 limits:
89 cpus: '0.5'
90 memory: 256M
91
92networks:
93 frontend:
94 driver: bridge
95 backend:
96 driver: bridge
97 internal: true
98
99volumes:
100 postgres_data:
101 redis_data:
102
103secrets:
104 db_password:
105 file: ./secrets/db_password.txt
106 jwt_secret:
107 file: ./secrets/jwt_secret.txt
108 redis_password:
109 file: ./secrets/redis_password.txt

Health Check Patterns

Without health checks, Docker cannot detect if a containerized service is unhealthy.

flowchart

With Health Checks

Container runs

App crashes

Check fails

Restart

Recovers

No Health Checks

Container runs

App crashes

Still 'running'

Users see errors

Ctrl+scroll to zoom • Drag to pan47%

Health check examples by application type:

yaml
1# HTTP service
2healthcheck:
3 test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
4 interval: 30s
5 timeout: 10s
6 retries: 3
7
8# Database
9healthcheck:
10 test: ["CMD-SHELL", "pg_isready -U postgres"]
11 interval: 10s
12 timeout: 5s
13 retries: 5
14
15# Redis
16healthcheck:
17 test: ["CMD", "redis-cli", "ping"]
18 interval: 10s
19 timeout: 5s
20 retries: 5
21
22# Custom script
23healthcheck:
24 test: ["CMD", "/app/healthcheck.sh"]
25 interval: 30s
26 timeout: 10s
27 retries: 3
28 start_period: 60s

Docker Swarm for Production

Docker Swarm provides native clustering and orchestration with built-in secrets management and rolling updates.

Swarm Architecture

flowchart

Worker Nodes

Manager Nodes - Odd Number

Manager 1 - Raft Leader

Manager 2

Manager 3

Worker 1

Worker 2

Worker 3

Worker N

Load Balancer

Ctrl+scroll to zoom • Drag to pan43%

Setting Up a Swarm Cluster

bash
1# Initialize Swarm on first manager
2docker swarm init --advertise-addr 192.168.1.10
3
4# Output provides join tokens
5# To add a manager:
6docker swarm join-token manager
7
8# To add a worker:
9docker swarm join-token worker
10
11# Join additional managers (run on each manager node)
12docker swarm join --token SWMTKN-1-xxx 192.168.1.10:2377
13
14# Join workers (run on each worker node)
15docker swarm join --token SWMTKN-1-yyy 192.168.1.10:2377

Swarm Secrets Management

Docker Swarm secrets are encrypted at rest and in transit. Only containers explicitly granted permission can access the decrypted value.

flowchart

Yes

No

Create Secret

Encrypt with Swarm Key

Store in Raft DB

Service requests secret

Service authorized?

Decrypt and mount as tmpfs

Access denied

Ctrl+scroll to zoom • Drag to pan35%

Creating and using secrets:

bash
1# Create from stdin
2echo "my-super-secret-password" | docker secret create db_password -
3
4# Create from file
5docker secret create ssl_cert ./certs/server.crt
6docker secret create ssl_key ./certs/server.key
7
8# List secrets
9docker secret ls
10
11# Inspect secret metadata (not the value)
12docker secret inspect db_password

Using secrets in services:

yaml
1# docker-compose.yml for Swarm
2services:
3 api:
4 image: myapp/api:latest
5 secrets:
6 - db_password
7 - source: ssl_cert
8 target: /etc/ssl/certs/server.crt
9 mode: 0444
10 - source: ssl_key
11 target: /etc/ssl/private/server.key
12 uid: '1000'
13 gid: '1000'
14 mode: 0400
15
16secrets:
17 db_password:
18 external: true
19 ssl_cert:
20 external: true
21 ssl_key:
22 external: true

Reading secrets in your application:

typescript
1// Node.js example
2import { readFileSync } from 'fs';
3
4function getSecret(name: string): string {
5 try {
6 // Secrets are mounted at /run/secrets/
7 return readFileSync(`/run/secrets/${name}`, 'utf8').trim();
8 } catch (error) {
9 // Fallback to environment variable for development
10 const envValue = process.env[name.toUpperCase()];
11 if (!envValue) {
12 throw new Error(`Secret ${name} not found`);
13 }
14 return envValue;
15 }
16}
17
18const dbPassword = getSecret('db_password');

Production Stack Deployment

yaml
1# stack.yml - Full production example
2services:
3 traefik:
4 image: traefik:v3.0
5 command:
6 - "--api.dashboard=true"
7 - "--providers.docker.swarmMode=true"
8 - "--providers.docker.exposedbydefault=false"
9 - "--entrypoints.web.address=:80"
10 - "--entrypoints.websecure.address=:443"
11 - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
12 - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
13 - "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
14 - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
15 ports:
16 - "80:80"
17 - "443:443"
18 volumes:
19 - /var/run/docker.sock:/var/run/docker.sock:ro
20 - traefik_certs:/letsencrypt
21 networks:
22 - traefik-public
23 deploy:
24 mode: global
25 placement:
26 constraints:
27 - node.role == manager
28 labels:
29 - "traefik.enable=true"
30 - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
31 - "traefik.http.routers.dashboard.service=api@internal"
32 - "traefik.http.routers.dashboard.middlewares=auth"
33 - "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$xxx"
34
35 api:
36 image: myapp/api:1.5.0
37 environment:
38 - NODE_ENV=production
39 - DATABASE_URL=postgresql://myapp@db:5432/myapp
40 secrets:
41 - db_password
42 - jwt_secret
43 networks:
44 - traefik-public
45 - backend
46 deploy:
47 replicas: 3
48 update_config:
49 parallelism: 1
50 delay: 30s
51 failure_action: rollback
52 monitor: 60s
53 order: start-first
54 rollback_config:
55 parallelism: 1
56 delay: 10s
57 restart_policy:
58 condition: on-failure
59 delay: 5s
60 max_attempts: 3
61 window: 120s
62 resources:
63 limits:
64 cpus: '1'
65 memory: 512M
66 reservations:
67 cpus: '0.25'
68 memory: 128M
69 labels:
70 - "traefik.enable=true"
71 - "traefik.http.routers.api.rule=Host(`api.example.com`)"
72 - "traefik.http.routers.api.entrypoints=websecure"
73 - "traefik.http.routers.api.tls.certresolver=letsencrypt"
74 - "traefik.http.services.api.loadbalancer.server.port=3000"
75 healthcheck:
76 test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"]
77 interval: 30s
78 timeout: 10s
79 retries: 3
80
81 db:
82 image: postgres:16-alpine
83 volumes:
84 - postgres_data:/var/lib/postgresql/data
85 environment:
86 POSTGRES_DB: myapp
87 POSTGRES_USER: myapp
88 POSTGRES_PASSWORD_FILE: /run/secrets/db_password
89 secrets:
90 - db_password
91 networks:
92 - backend
93 deploy:
94 replicas: 1
95 placement:
96 constraints:
97 - node.labels.db == true
98 resources:
99 limits:
100 cpus: '4'
101 memory: 4G
102
103networks:
104 traefik-public:
105 driver: overlay
106 attachable: true
107 backend:
108 driver: overlay
109 driver_opts:
110 encrypted: "true"
111 internal: true
112
113volumes:
114 traefik_certs:
115 postgres_data:
116
117secrets:
118 db_password:
119 external: true
120 jwt_secret:
121 external: true

Deploying and Managing Stacks

bash
1# Deploy stack
2docker stack deploy -c stack.yml myapp
3
4# List stacks
5docker stack ls
6
7# List services in stack
8docker stack services myapp
9
10# View service logs
11docker service logs myapp_api -f
12
13# Scale a service
14docker service scale myapp_api=5
15
16# Rolling update
17docker service update --image myapp/api:1.6.0 myapp_api
18
19# Rollback
20docker service rollback myapp_api
21
22# Remove stack
23docker stack rm myapp

Rolling Updates and Zero-Downtime Deployments

sequence
New Container 2New Container 1Old Container 2Old Container 1Load BalancerInitial State: 2 old containersStart new container 1Stop old container 1Start new container 2Stop old container 2Final State: 2 new containersTrafficTrafficHealth check passesTraffic startsTraffic continuesHealth check passesTrafficTraffic starts
Ctrl+scroll to zoom • Drag to pan35%

Update Configuration Options

yaml
1deploy:
2 update_config:
3 parallelism: 1 # Update one container at a time
4 delay: 30s # Wait 30s between updates
5 failure_action: rollback # Auto-rollback on failure
6 monitor: 60s # Monitor for 60s after update
7 max_failure_ratio: 0 # Any failure triggers rollback
8 order: start-first # Start new before stopping old
OptionRecommended ValuePurpose
parallelism1Update one at a time for safety
delay30sAllow service to stabilize
failure_actionrollbackAutomatic recovery
monitor60sCatch delayed failures
orderstart-firstZero-downtime deployment

Networking Best Practices

Network Architecture

flowchart

Private Encrypted Overlay

Public Overlay Network

Internet

Users

Traefik or HAProxy

Web Frontend

API Service

Background Workers

Database

Redis

Ctrl+scroll to zoom • Drag to pan40%

Network configuration:

yaml
1networks:
2 # Public-facing services
3 public:
4 driver: overlay
5 attachable: true
6
7 # Internal services - no external access
8 private:
9 driver: overlay
10 driver_opts:
11 encrypted: "true" # Encrypt traffic between nodes
12 internal: true # No external connectivity
13
14 # Database network - extra isolation
15 database:
16 driver: overlay
17 driver_opts:
18 encrypted: "true"
19 internal: true

The 42 Docker Production Best Practices

Image Optimization (1-10)

  • 1. Use multi-stage builds to reduce image size
  • 2. Pin base image versions (never use :latest)
  • 3. Use Alpine or distroless images when possible
  • 4. Order Dockerfile instructions by change frequency
  • 5. Combine RUN commands to reduce layers
  • 6. Use .dockerignore to exclude unnecessary files
  • 7. Scan images for vulnerabilities (Trivy, Docker Scout)
  • 8. Sign and verify images in production
  • 9. Use BuildKit for faster builds
  • 10. Cache dependencies separately from application code

Security (11-20)

  • 11. Never run containers as root
  • 12. Use read-only filesystems
  • 13. Drop all capabilities, add only what's needed
  • 14. Use secrets management (never environment variables for secrets)
  • 15. Limit resources (CPU, memory)
  • 16. Use internal networks for service-to-service communication
  • 17. Enable TLS for all network communication
  • 18. Regularly update base images
  • 19. Use security contexts and AppArmor/SELinux profiles
  • 20. Implement least-privilege principle

Health & Reliability (21-30)

  • 21. Add health checks to ALL services
  • 22. Use start_period for slow-starting services
  • 23. Implement graceful shutdown handlers
  • 24. Configure restart policies appropriately
  • 25. Set up proper logging drivers
  • 26. Use dependson with condition: servicehealthy
  • 27. Configure rolling updates with proper delays
  • 28. Set failure_action to rollback
  • 29. Monitor services with Prometheus/Grafana
  • 30. Set up alerting for container failures

Performance (31-37)

  • 31. Use tmpfs for temporary files
  • 32. Configure appropriate resource limits
  • 33. Use volume mounts for persistent data
  • 34. Optimize container startup time
  • 35. Use connection pooling for databases
  • 36. Configure proper logging (avoid stdout flooding)
  • 37. Use overlay2 storage driver

Operations (38-42)

  • 38. Tag images with git commit SHA and semantic version
  • 39. Implement blue-green or canary deployments
  • 40. Automate deployments with CI/CD
  • 41. Back up volumes and secrets regularly
  • 42. Document runbooks for common operations

Monitoring and Observability

Basic Monitoring Stack

yaml
1services:
2 prometheus:
3 image: prom/prometheus:latest
4 volumes:
5 - ./prometheus.yml:/etc/prometheus/prometheus.yml
6 - prometheus_data:/prometheus
7 command:
8 - '--config.file=/etc/prometheus/prometheus.yml'
9 - '--storage.tsdb.path=/prometheus'
10 networks:
11 - monitoring
12 deploy:
13 placement:
14 constraints:
15 - node.role == manager
16
17 grafana:
18 image: grafana/grafana:latest
19 volumes:
20 - grafana_data:/var/lib/grafana
21 environment:
22 - GF_SECURITY_ADMIN_PASSWORD_FILE=/run/secrets/grafana_password
23 secrets:
24 - grafana_password
25 networks:
26 - monitoring
27 - public
28
29 cadvisor:
30 image: gcr.io/cadvisor/cadvisor:latest
31 volumes:
32 - /:/rootfs:ro
33 - /var/run:/var/run:ro
34 - /sys:/sys:ro
35 - /var/lib/docker/:/var/lib/docker:ro
36 networks:
37 - monitoring
38 deploy:
39 mode: global
40
41volumes:
42 prometheus_data:
43 grafana_data:
44
45networks:
46 monitoring:
47 driver: overlay
48 internal: true

Key Metrics to Monitor

MetricAlert ThresholdAction
Container CPU> 80% for 5minScale or optimize
Container Memory> 85%Investigate leaks
Container Restarts> 3 in 5minCheck logs
Health Check Failures> 2 consecutiveAuto-restart
Disk Usage> 80%Cleanup or expand

When to Graduate to Kubernetes

Docker Swarm works great, but consider Kubernetes/K3s when:

flowchart

Consider Kubernetes When

You need advanced auto-scaling

You want GitOps workflows

You need service mesh capabilities

Your team has K8s expertise

You're running 50+ services

You need multi-cluster management

Ctrl+scroll to zoom • Drag to pan31%

Brisbane Docker Consulting

At Buun Group, we help Queensland businesses deploy containerized applications:

  • Docker architecture — design production-ready setups
  • Swarm clusters — multi-node HA deployments
  • Security hardening — secrets, networking, scanning
  • Migration — move from VMs to containers

We've deployed production Docker workloads across various scales. We know what works.

Topics

Docker production deployment 2026Docker Swarm guideDocker Compose productionDocker secrets managementcontainer orchestrationrolling updates DockerDocker health checksDocker best practices

Share this post

Share

Comments

Sign in to join the conversation

Login

No comments yet. Be the first to share your thoughts!

Found an issue with this article?

/ Let's Talk

Want to work with us?

Whether you need help with architecture, development, or technical consulting, our team is here to help bring your vision to life.