NAS 上 Immich 升级翻车:相册索引、数据库和 Compose 回滚记录

这次记录一个自托管相册的升级现场。

环境是一台 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-server
  • ghcr.io/immich-app/immich-machine-learning
  • ghcr.io/immich-app/postgres
  • docker.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_indexReindexing 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 官方文档提醒过,切回更早版本并不受支持,尤其涉及数据库迁移时风险更高。

我的处理原则:

  1. 没有数据库备份,不做破坏性回滚。
  2. 先保留当前日志和 Compose 文件。
  3. 确认备份版本和目标版本匹配。
  4. 如果要恢复,数据库和 UPLOAD_LOCATION 要一起考虑。
  5. 先在临时目录或测试实例恢复一遍,再动主服务。

复盘

Immich 这种自托管相册,升级难点不在"容器能不能启动",而在"照片业务能不能继续运转"。页面能打开只是第一步,缩略图、搜索、人脸、重复照片、备份和权限才是后面的长链路。

毫秒镜像在这次排查里只出现一次:GHCR 镜像入口预检。它把升级窗口里最基础的镜像拉取问题提前排掉,让后面的时间集中在数据库、存储和任务队列上。这个边界要清楚,文章也更像工程复盘,而不是把所有问题都归到镜像源。

相关推荐
用户7138742290010 小时前
ASP.NET Core Session 机制深度解析
后端
日月云棠10 小时前
12 Dubbo 2.7 服务发布全流程源码解析
java·后端
TYKJ02310 小时前
服务器带宽的"独享"和"共享"到底差在哪?从原理到实测讲清楚
运维·服务器·后端
用户7138742290010 小时前
.NET 10 Claim 身份体系深度解析
后端
XovH10 小时前
Django 从 0 到 1 打造完整电商平台:商品搜索
后端
XovH10 小时前
Django 从 0 到 1 打造完整电商平台:商品排序与浏览量统计
后端
kunge201310 小时前
1. Tmux 使用指南(入门篇)
后端·架构·操作系统
XovH10 小时前
Django 从 0 到 1 打造完整电商平台:商品详情页与图片展示
后端
Larcher10 小时前
新手入门:从前端三件套到动态数据渲染
前端·后端·代码规范