家里 NAS 上跑了 12 个 Docker 服务,最长的一个连续运行 712 天没重启。
去年写过一篇 7 个服务的盘点,现在涨到 12 个。服务变多了,维护时间反而没增加。靠的是一开始把架构搭对。
12 个服务都跑什么
先列一下,给个参考,
| 服务 | 用途 | 端口 |
|---|---|---|
| Vaultwarden | 密码管理 | 8222 |
| Gitea | 私有 Git | 3000 |
| Nextcloud | 文件同步 | 443 |
| Mastodon | 社交 | 443 |
| Misskey | 联邦宇宙 | 443 |
| Jellyfin | 媒体中心 | 8096 |
| Home Assistant | 智能家居 | 8123 |
| Uptime Kuma | 监控 | 3001 |
| Trilium Notes | 笔记 | 8080 |
| AdGuard Home | DNS 广告过滤 | 53 |
| Photoprism | 照片管理 | 2342 |
| Watchtower | 自动更新 | - |
不是每个都天天用,但每个都有不可替代的场景。
核心原则一,网络隔离
所有服务在同一个 docker-compose.yml 里,但分了三个网络,
networks:
frontend: # 暴露给外网的:Gitea、Mastodon、Misskey
backend: # 内部服务:数据库、Redis、内部 API
isolated: # 完全隔离:Home Assistant、AdGuard
services:
gitea:
networks: [frontend, backend]
homeassistant:
networks: [isolated]
postgres:
networks: [backend]好处,即使某个服务被攻破,攻击者也没法直接访问数据库或智能家居。Postgres 这种敏感服务只挂在 backend 上,外部完全不可达。
核心原则二,数据分层
所有持久化数据统一放在 /data 下,按服务名分目录,
/data/
gitea/
nextcloud/
postgres/
mastodon/
...docker-compose.yml 里用相对路径挂载,
volumes:
- ./data/gitea:/data
- ./data/postgres:/var/lib/postgresql/data不用 Docker named volume,直接挂目录。原因,备份方便,tar -czf backup.tar.gz data/ 一条命令搞定;迁移方便,整个目录拷到新机器就能跑。
核心原则三,更新策略
12 个服务全开自动更新会出事,某个服务升了大版本,配置不兼容,直接挂掉。
我的策略是分三档,
自动更新(Watchtower 管)
- Vaultwarden、Uptime Kuma、Trilium Notes、Photoprism
- 这几个都是小项目,更新频率低,出问题影响小
手动更新,每月一次
- Gitea、Nextcloud、Jellyfin、Home Assistant
- 这些是核心服务,更新前先看 changelog,挑小版本升
不更新(锁版本)
- Mastodon、Misskey
- 联邦宇宙的协议更新频繁,跨版本升级容易出问题。锁在稳定版本,除非有安全漏洞否则不动
日常维护就这几件事
看日志,每天早上看一眼 Uptime Kuma 的状态面板,绿色就不管。某个服务红了,SSH 进去 docker logs --tail 50 <service> 看原因。
跑更新,每周日晚上,docker compose pull && docker compose up -d,10 分钟搞定。Watchtower 管的服务已经自动更新了,这步主要是手动更新那几个。
查磁盘,df -h 看一眼 /data 目录占了多少。Nextcloud 和 Jellyfin 是吃硬盘大户,超过 80% 就该清理了。
备份数据,每周一次,用 rclone 把 /data 同步到 Backblaze B2。Postgres 单独 pg_dump 导出,不信任文件级备份。
踩过的坑
PostgreSQL 大版本升级。 Debian 12 自带 Postgres 15,某个服务要求 16。直接升级把数据目录格式变了,所有依赖 Postgres 的服务全挂。解法,用 pg_upgrade 做数据迁移,或者新开一个 Postgres 16 容器,旧数据用 pg_dump | psql 导过去。
Docker 网络和宿主机防火墙冲突。 Docker 默认会修改 iptables 规则,绕过 ufw。你以为某个端口只对内网开放,实际上 Docker 把它暴露到了 0.0.0.0。解法,在 docker-compose.yml 里显式绑定 127.0.0.1:端口:端口,或者用 Docker 的自定义网络 + 外部反代。
Watchtower 更新搞挂服务。 Watchtower 默认每次都拉 latest 标签。某个服务从 v2 跳到 v3,配置文件不兼容,容器起来就崩。解法,给需要锁版本的服务加 label com.centurylinklabs.watchtower.enable=false,Watchtower 会跳过。
自托管不是折腾,是拿回控制权。12 个服务每周花 10 分钟维护,换来的是数据完全自主、没有订阅费、想怎么定制就怎么定制。关键是架构一开始就搭对,网络隔离、数据分层、更新分档,三件事做到位,剩下的就是享受。