这次记录一个自托管相册的升级现场。
环境是一台 NAS 加一台小主机,Immich 用 Docker Compose 跑。升级前服务正常,照片上传、缩略图、人脸识别都没问题。升级后 docker compose up -d 没报错,页面也能打开,但新照片没有缩略图,重复照片任务也没动静。
一开始我也以为是 Immich 新版本的问题。后来按层拆,发现真正有用的顺序是:镜像版本先固定,数据库备份先确认,UPLOAD_LOCATION 再检查,最后才看 machine learning 和任务队列。
先把 Compose 现场留住
自托管服务升级前,我现在会先做三件事:
bash
cp docker-compose.yml docker-compose.yml.$(date +%Y%m%d-%H%M)
cp .env .env.$(date +%Y%m%d-%H%M)
docker compose ps
如果 .env 里用的是:
env
IMMICH_VERSION=v2
它的好处是跟随 v2 主版本,坏处是复盘时不够精确。维护窗口里我更倾向于先记下当前实际镜像:
bash
docker image ls | grep immich
docker compose config | sed -n '/image:/p'
镜像拉取只是一层,但这一层要先过
Immich 的 Compose 里不是一个镜像。常见会涉及:
ghcr.io/immich-app/immich-serverghcr.io/immich-app/immich-machine-learningghcr.io/immich-app/postgresdocker.io/valkey/valkey
NAS 网络环境如果访问 GHCR 不稳,就会在升级时制造很隐蔽的问题:server 镜像拉到了,machine learning 没拉到;database 镜像还是旧的;或者不同机器上镜像 tag 看起来一样,实际 digest 不一致。
所以我会先把镜像层单独验掉:
bash
docker pull ghcr.1ms.run/immich-app/immich-server:v2
docker pull ghcr.1ms.run/immich-app/immich-machine-learning:v2
docker pull ghcr.1ms.run/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
这里用毫秒镜像只是为了把 GHCR 镜像入口跑稳。它不解决挂载权限,也不解决数据库迁移。镜像层过了,后面继续排。
我的 Compose 里会这样写:
yaml
services:
immich-server:
image: ghcr.1ms.run/immich-app/immich-server:${IMMICH_VERSION:-v2}
volumes:
- ${UPLOAD_LOCATION}:/data
- /etc/localtime:/etc/localtime:ro
immich-machine-learning:
image: ghcr.1ms.run/immich-app/immich-machine-learning:${IMMICH_VERSION:-v2}
volumes:
- model-cache:/cache
database:
image: ghcr.1ms.run/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
volumes:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
数据库和照片目录要分开备份
自托管相册最容易犯的错,是以为"备份了数据库就等于备份了照片"。Immich 官方文档说得很明确:数据库里有路径和用户元数据,但数据库备份不包含照片和视频原文件。
我的升级前检查表现在是这样:
bash
grep -n "UPLOAD_LOCATION\\|DB_DATA_LOCATION\\|IMMICH_VERSION" .env
du -sh "$UPLOAD_LOCATION" "$DB_DATA_LOCATION"
ls -lah "$UPLOAD_LOCATION/backups" | tail
同时确认这些目录还在:
text
backups
encoded-video
library
profile
thumbs
upload
如果 NAS 外接盘、NFS、SMB、iSCSI 这类存储在升级前后路径变化,Immich 页面能打开也没用,后台任务会对着空目录或无权限目录工作。
索引卡住不一定是卡死
这次最容易误判的地方是数据库索引。
Immich 官方升级文档提到,VectorChord 相关迁移中,Reindexing clip_index、Reindexing face_index 在照片量大或硬件比较弱时可能持续一段时间。也就是说,看到这两行不代表一定挂了。
我会这样看:
bash
docker compose logs -f immich-server
docker compose logs -f database
如果日志只是停在 reindex,没有报错,就先看 CPU、磁盘 IO 和数据库容器状态:
bash
docker stats
docker compose ps database
如果出现连接失败、迁移失败、权限错误,再进入下一层。
存储权限比想象中常见
升级后缩略图不生成,我最先看 /data:
bash
docker compose exec immich-server sh -lc 'ls -lah /data | head'
docker compose exec immich-server sh -lc 'touch /data/.write-test && rm /data/.write-test'
如果这里写不进去,后面就不用排 ML 了。相册服务能打开,只说明 server 启动了,不说明它能写缩略图、转码文件和缓存。
常见问题有几个:
| 现象 | 常见原因 |
|---|---|
/data 为空 |
UPLOAD_LOCATION 相对路径变了 |
thumbs 不更新 |
目录权限不对 |
| 外部图库消失 | 挂载路径变化 |
| 新照片不处理 | job queue 卡住或无法写派生文件 |
| 人脸识别不动 | machine learning 容器异常 |
回滚不要只回滚容器
如果确实需要回滚,不要只把镜像 tag 改回去。Immich 官方文档提醒过,切回更早版本并不受支持,尤其涉及数据库迁移时风险更高。
我的处理原则:
- 没有数据库备份,不做破坏性回滚。
- 先保留当前日志和 Compose 文件。
- 确认备份版本和目标版本匹配。
- 如果要恢复,数据库和
UPLOAD_LOCATION要一起考虑。 - 先在临时目录或测试实例恢复一遍,再动主服务。
复盘
Immich 这种自托管相册,升级难点不在"容器能不能启动",而在"照片业务能不能继续运转"。页面能打开只是第一步,缩略图、搜索、人脸、重复照片、备份和权限才是后面的长链路。
毫秒镜像在这次排查里只出现一次:GHCR 镜像入口预检。它把升级窗口里最基础的镜像拉取问题提前排掉,让后面的时间集中在数据库、存储和任务队列上。这个边界要清楚,文章也更像工程复盘,而不是把所有问题都归到镜像源。