
每次開新專案都在從零寫 docker-compose.yml?然後忘了加 healthcheck、忘了限制 log 大小、volume 名稱亂取到後來自己都搞不清楚哪個是哪個?這篇直接給你四套可以拿去用的模板。
先講結論
Docker Compose 的標準化不是限制彈性,是把「正確的預設」變成起點。本篇提供四套模板:Web App Stack、監控 Stack、日誌 Stack、開發環境。每套都附最佳實踐。直接 copy,改環境變數,docker compose up -d 就能跑。
模板 1:Web App Stack
最常見的組合:Nginx 反向代理 + App + PostgreSQL + Redis。大部分 Web 服務用這套就夠了。
# docker-compose.app.yml
version: "3.8"
services:
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- static_files:/var/www/static:ro
depends_on:
app:
condition: service_healthy
networks:
- frontend
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
app:
image: ${APP_IMAGE:-myapp:latest}
env_file:
- .env
environment:
- DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
- REDIS_URL=redis://cache:6379/0
volumes:
- static_files:/app/static
- upload_data:/app/uploads
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
networks:
- frontend
- backend
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
db:
image: postgres:16
environment:
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_NAME}
volumes:
- db_data:/var/lib/postgresql/data
networks:
- backend
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
volumes:
- redis_data:/data
networks:
- backend
restart: unless-stopped
command: ["redis-server", "--appendonly", "yes"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
static_files:
upload_data:
db_data:
redis_data:
networks:
frontend:
backend:這裡有幾個重點你可能會忽略:
healthcheck 是 depends_on 的靈魂。 沒有 healthcheck 的 depends_on 只保證「容器啟動了」,不保證「服務 ready 了」。DB 容器可能啟動了但還在 recovery,App 就去連然後 crash。
logging 一定要限制大小。 max-size: "10m" + max-file: "3" 表示單個 log 檔最多 10MB,最多保留 3 個。不設的話,跑幾個月 log 把磁碟打爆是常見的慘案。
network 分層。 Nginx 跟 App 在 frontend,App 跟 DB/Redis 在 backend。DB 不需要也不應該出現在 frontend network 裡。
模板 2:監控 Stack
Prometheus + Grafana + Node Exporter,三分鐘搭好一套基本的監控系統。
# docker-compose.monitoring.yml
version: "3.8"
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prom_data:/prometheus
ports:
- "9090:9090"
restart: unless-stopped
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
depends_on:
- prometheus
restart: unless-stopped
node_exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
restart: unless-stopped
volumes:
prom_data:
grafana_data:Prometheus 的 config 用 git 管理,不要手動改了之後忘記 commit。Grafana 的 admin 密碼用環境變數傳入,不要寫死在 compose 裡。Node Exporter 如果想拿到更準確的主機指標,可以用 network_mode: host。
模板 3:日誌 Stack(EFK)
Elasticsearch + Filebeat + Kibana。用來集中所有容器的 log。
# docker-compose.logging.yml
version: "3.8"
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms512m -Xmx512m
ports:
- "9200:9200"
volumes:
- es_data:/usr/share/elasticsearch/data
restart: unless-stopped
kibana:
image: docker.elastic.co/kibana/kibana:8.12.0
ports:
- "5601:5601"
depends_on:
- elasticsearch
restart: unless-stopped
filebeat:
image: docker.elastic.co/beats/filebeat:8.12.0
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
depends_on:
- elasticsearch
restart: unless-stopped
volumes:
es_data:Elasticsearch 的 JVM heap 不要給太大。512MB 在 dev/staging 夠用了,prod 再按需調整。Filebeat 只需要 read-only 權限就能收 Docker log,不要給它不必要的權限。
模板 4:本機開發環境
開發的時候不想裝 PostgreSQL、Redis、MinIO 到本機?全部丟 Docker 裡。
# docker-compose.dev.yml
version: "3.8"
services:
postgres:
image: postgres:16
ports:
- "5432:5432"
environment:
- POSTGRES_USER=dev
- POSTGRES_PASSWORD=dev
- POSTGRES_DB=dev_db
volumes:
- dev_db:/var/lib/postgresql/data
redis:
image: redis:7
ports:
- "6379:6379"
minio:
image: minio/minio
command: server /data --console-address ":9001"
ports:
- "9000:9000"
- "9001:9001"
environment:
- MINIO_ROOT_USER=minio
- MINIO_ROOT_PASSWORD=minio123
volumes:
- minio_data:/data
mailhog:
image: mailhog/mailhog
ports:
- "8025:8025"
volumes:
dev_db:
minio_data:這套不需要 healthcheck 也不需要 network 分層——它就是本機開發用的。密碼用 dev / minio123 這種,反正不會上 prod。如果你的 prod 密碼也是 dev,我們需要談談。
通用的 Checklist
不管用哪套模板,這些事都要做:
- 環境變數放
.env,不要硬編碼在 compose 裡 - 所有 service 加
restart: unless-stopped - 重要 service 加
healthcheck logging限制大小- Volume 命名要有意義(
db_data而不是volume1) - Network 分前後端
cp .env.example .env
docker compose -f docker-compose.app.yml up -d延伸閱讀
Docker Compose 的最高境界是:新人第一天 clone repo,改一下 .env,docker compose up,五分鐘就能開始開發。如果你的 onboarding 需要半天,那不是新人的問題,是你的 compose 的問題。