compose 文件写三年,我总结的 12 条实践
我的 Home Lab 里有 9 个 compose 项目,最老的跑了四年。这些年改下来,形成了一套固定写法。
不是「最佳实践」这种大词,就是我自己觉得省事的东西。
1. 一个项目一个目录,一个 .env
~/stacks/gitea/
├─ compose.yaml
├─ .env
└─ data/
.env 里放所有可变的东西:端口、路径、密码、镜像版本。这样换机器时只改 .env。
2. 镜像必须固定版本,不能用 latest
image: gitea/gitea:1.22.3 # 不是 :latest
用 latest 的代价:某天自动拉了个新版本,数据库迁移了,回不去了。
升级应该是显式动作:改版本号 → docker compose pull → up -d → 看日志 → 有问题改回来。
3. restart: unless-stopped
restart: unless-stopped
不用 always:always 在 docker stop 之后重启策略仍然生效(手动停了会被拉起来),而 unless-stopped 尊重你的手动停止。
on-failure 也不错,但如果程序启动就崩,会无限重启刷日志。配合 restart: unless-stopped + 健康检查更好。
4. 端口映射只绑内网地址
ports:
- "127.0.0.1:3000:3000" # 只本机可访问,前面用反代
# 不要写 "3000:3000" # 等于 0.0.0.0:3000,公网直接扫得到
这一条能挡掉大量「我的服务被扫到了」的问题。公网访问统一走反向代理。
5. 数据用相对路径的绑定挂载
volumes:
- ./data:/data
不用绝对路径,这样整个目录可以打包搬走。命名卷也好,但备份时要多一步 docker run --rm -v vol:/data alpine tar,绑定挂载直接 rsync 就行。
6. 显式声明时区和用户
environment:
- TZ=Asia/Shanghai
user: "1000:1000"
第二行看情况,有些镜像不支持非 root,会报权限错。但凡是支持 PUID/PGID 的镜像(linuxserver 系),一定要设。
7. 健康检查,不要只看「容器在跑」
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 40s
start_period 很重要——不加的话,启动慢的服务会被判定为不健康然后被反复重启。
8. 日志必须限制大小
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
不加这个,一个话多的容器几个月能把磁盘写满。我有一次就是 nginx 容器的 access log 写了 40G。
9. 用 depends_on 加健康条件,不要只写名字
depends_on:
db:
condition: service_healthy
只写 depends_on: [db] 只能保证启动顺序,不保证 db 已经能接受连接。加了 condition 才是真的等。
10. 网络显式命名,别用默认 bridge
networks:
backend:
name: stack_backend
services:
web:
networks: [backend]
ports: ["127.0.0.1:3000:3000"]
db:
networks: [backend]
好处:容器名可以当 DNS 用;不同项目的网络隔离;排查时 docker network inspect 清楚。
11. 敏感信息不要写进 compose
environment:
- DB_PASSWORD=${DB_PASSWORD} # 从 .env 读
.env 加进 .gitignore,仓库里只放 .env.example。
更进一步用 secrets,但家庭场景 .env + 文件权限 600 已经够了。
12. 保留一份 compose.yaml.bak 的习惯
改之前先 cp compose.yaml compose.yaml.bak。
Docker 的坑在于:写错一个缩进,up -d 会把原来的容器重建掉。有备份就能立刻回滚。
真正该做的是 git,但我发现自己在小改动时经常懒得 commit,所以物理备份更实际。
一份模板
把我常用的骨架整理成一个:
services:
app:
image: vendor/app:1.2.3
container_name: app
restart: unless-stopped
user: "1000:1000"
environment:
- TZ=Asia/Shanghai
- DB_PASSWORD=${DB_PASSWORD}
volumes:
- ./data:/app/data
ports:
- "127.0.0.1:8080:8080"
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/healthz"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks: [backend]
db:
image: postgres:16.4
restart: unless-stopped
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
- TZ=Asia/Shanghai
volumes:
- ./db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
networks: [backend]
networks:
backend:
一条没做到的
我至今没有把 9 个项目全部纳入 CI 做配置校验。理想情况下应该有个 docker compose config 的 pre-commit 检查,防止缩进写错。
这条先记着,等哪天真的被缩进坑一次大的,大概就会做了。
评论 (0)
还没有评论,来说两句吧~
发表评论