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 镜像入口预检。它把升级窗口里最基础的镜像拉取问题提前排掉,让后面的时间集中在数据库、存储和任务队列上。这个边界要清楚,文章也更像工程复盘,而不是把所有问题都归到镜像源。

相关推荐
金金金__4 分钟前
腾讯云COS接入配置
后端
Hyyy9 分钟前
Git Worktree 完全讲解
前端·后端·面试
逝水无殇41 分钟前
C# 反射详解
开发语言·后端·c#
Cache技术分享1 小时前
465. Java 反射 - 获取类的简单名
前端·后端
做系统的大强1 小时前
REPL里的HLT:main_stmts为空时的致命尾声
后端
薛定谔的悦1 小时前
光伏-储能-负荷联合预测:给 EMS 装上"预知能力"
后端
云技纵横1 小时前
Redis 延迟双删还是读到脏数据?一次缓存一致性事故
redis·后端·面试
网易云信1 小时前
从“合规”到“信任”:网易智企专属云打造强监管下的通信新范式
人工智能·后端
吃糖的小孩1 小时前
从“让聊天模型顺便选表情”到独立分类器:我给 QQ AI Chatbot 做了一套本地动态表情包系统
后端
无责任此方_修行中2 小时前
面对 300+ 仓库的重复劳动,我用 Guideline 优化了 Vibe Coding 工作流
后端·ai编程·vibecoding