自托管 Immich:搭建您自己的 Google Photos
在 VPS 上用官方 Docker Compose 自托管 Immich:服务器、机器学习搜索、Postgres 与 Redis、手机备份、HTTPS,以及安全的升级和备份。
您将搭建什么
Immich 是一款自托管的照片和视频备份服务,是 Google Photos 的一个真正替代品。它有一个手机 App,可以在后台上传您的相机胶卷,提供时间线、相册、人脸识别,以及机器学习搜索:无需您手动打任何标签,就能找到“海滩”或某个人的照片。您把它运行在您自己拥有的 VPS 上,原始文件留在您的磁盘上,没有人会扫描它们来向您推销东西。
安装过程就是从项目自带的 Docker Compose 文件启动四个容器。这一步只需要十分钟。本指南余下的内容才是真正麻烦的地方:机器学习容器在小机器上很吃内存,原始文件会飞快地占满磁盘,手机 App 拒绝连接纯 HTTP 的服务器,而且 Immich 发布破坏性变更的频率高到,一次草率的 docker compose pull 就可能让您的数据库无法启动。认真对待这四件事,Immich 会非常稳定。忽视它们,您就会赔上一个周末。
前置条件,以及一些老实话
- 内存:官方文档说最低 6 GB、推荐 8 GB,请把 4 GB 加上交换分区当作绝对下限。
immich-server和 Postgres 容器占用不大。immich-machine-learning容器才是耗内存的大户,它会把 CLIP 和人脸识别模型加载进内存来建立搜索索引,在一台 2 GB 的机器上,内核会直接把它杀掉。哪怕您有 4 GB,也请加上交换分区(swap)。 - 磁盘:按您整个图库的大小来规划,再留出余量。您的原始文件会被完整复制一份,此外 Immich 还会生成缩略图和预览图(大约再多占 10–20%)。一个 200 GB 的照片库需要一个 300 GB 的卷。相比之下 Postgres 很小。
- CPU:任何现代的 KVM VPS 都可以,但在 CPU 上跑机器学习很慢。对一次大批量导入做智能搜索索引,可能会在后台跑上好几个小时。这是正常的,它不需要 GPU。
- 一个指向该 VPS 的域名。手机 App 强烈倾向于连接一个 HTTPS 端点,您也需要在前面放一个反向代理。这套配置的形态,和一台 使用 Docker、TLS 和备份的自托管 Nextcloud 实例 是一样的,Immich 是那台文件服务器在照片方面的对应物。
- 已安装 Docker 和 Compose 插件:从 Docker 官方的 apt 仓库安装 Docker Engine 加上 Compose v2 插件,具体做法完全照 我们的 Docker Compose 基础指南 即可。
第 1 步:在做任何事之前先加交换分区
Immich 在小型 VPS 上最常见的故障,就是机器学习容器被 OOM(内存耗尽)杀掉。请先给内核留出一点喘息的空间。
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h现在 free -h 应该显示一行 Swap:,值为 4.0Gi。这不会让机器学习变快,但能防止容器在一台 4 GB 的机器上建索引建到一半就崩溃。
第 2 步:获取官方的 compose 和 env,用他们的,别用副本
Immich 把它各个服务的版本,以及关键的数据库镜像,都固定写在它随发布提供的文件里。不要把博客(包括本文)里的 compose 文件粘贴过来当作您的权威来源。请下载发布资产:
sudo mkdir -p /opt/immich && cd /opt/immich
sudo wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
sudo wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env这些文件来自打了标签的发布版本,所以镜像引用是匹配的。这个 compose 文件定义了四个服务,在您改动任何东西之前,先了解每个服务是什么会很有帮助:
immich-server(ghcr.io/immich-app/immich-server,容器immich_server):API 和 Web 界面,监听端口2283。它把您上传的内容挂载在/data。immich-machine-learning(ghcr.io/immich-app/immich-machine-learning,容器immich_machine_learning):CLIP 搜索和人脸识别。会把下载的模型缓存在一个model-cache卷里。这就是那个耗内存的服务。database(容器immich_postgres):带有 VectorChord 向量扩展的 Postgres,相似度搜索由它驱动。镜像标签在 compose 文件里就以摘要(digest)方式固定住了,例如ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:...。较早的搭建方式用的是pgvecto.rs,对它的支持在 Immich v3.0 中被移除,所以您今天安装的一定是 VectorChord。绝对不要手动编辑这个标签。redis(容器immich_redis):用于任务队列的 Valkey/Redis 实例。
第 3 步:配置 .env,决定您的照片和数据库存放在哪里
打开 .env,设置四样东西。标记那一行以下的内容保持原样。
# Where original uploads are stored on the host
UPLOAD_LOCATION=/opt/immich/library
# Where the Postgres data lives. NEVER put this on an NFS/network share.
DB_DATA_LOCATION=/opt/immich/postgres
# "v3" is a floating tag that tracks the latest v3.x. Pin a full tag like
# v3.0.2 instead — then you upgrade on purpose, not by surprise.
IMMICH_VERSION=v3.0.2
# Change this to a long random string. Letters and digits only.
DB_PASSWORD=REPLACE_WITH_A_LONG_RANDOM_STRING
# Set your timezone so timestamps and "on this day" line up
TZ=Europe/London
###################################################################################
DB_USERNAME=postgres
DB_DATABASE_NAME=immich有两条规则能帮您省去麻烦。UPLOAD_LOCATION 应该指向您的大磁盘:如果您以后要挂载一个数据卷,请从一开始就把它设成那个卷的挂载路径,因为事后再移动它,意味着要移动缩略图并更新资源路径。而 DB_DATA_LOCATION 必须放在本地磁盘上:Postgres 放在 NFS 或 SMB 共享上会损坏,文档里也明明白白这么写。如果 DB_PASSWORD 里只用字母和数字,就能避开一类连接字符串转义的 bug。
第 4 步:首次运行并创建管理员用户
cd /opt/immich
sudo docker compose up -d
sudo docker compose ps正确的结果是四个容器全部 running,并最终变为 healthy:
NAME STATUS
immich_machine_learning Up (healthy)
immich_postgres Up (healthy)
immich_redis Up (healthy)
immich_server Up (healthy)首次 up 会拉取好几个 GB 的镜像,所以请给它一些时间。用 sudo docker compose logs -f immich-server 观察进度;服务器准备就绪后,会记录一条它正在监听端口 2283 的日志。现在在浏览器里打开 http://YOUR_SERVER_IP:2283。首次访问会显示一个 Getting Started 向导,您创建的第一个账户就是管理员。请设置一个强密码;这个账户拥有服务器设置、用户管理,以及您稍后会用到的机器学习配置。
第 5 步:手机 App 与后台备份
从 App Store 或 Play Store 安装“Immich”。在登录界面,它会要求一个 Server Endpoint URL(服务器端点地址)。请输入包含协议方案在内的完整 URL,例如 https://photos.example.com(App 会自己在后面追加 /api)。用您刚创建的账户登录,然后打开 App 的 Backup 界面,选择要保护的相册(通常是相机和截图),并启用 Background backup(后台备份)。iOS 的后台备份会被操作系统限流,前台上传总是会执行,后台上传则在操作系统允许时才进行。
人们恰恰在这里卡住,所以在您和这个 App 较劲之前,先读一读第 6 步。
第 6 步:通过反向代理启用 HTTPS,以及完整 URL 规则
手机 App 非常需要 HTTPS。请在端口 2283 前面放一个反向代理,并在那里终结 TLS。如果您已经在运行好几个容器,为多个 Docker 应用提供自动 TLS 的 Traefik 是最整洁的选择,一段标签配置就能把 photos.example.com 路由到 immich-server 容器,并替您获取证书。如果您更喜欢 nginx,用 Certbot 和 nginx 配置 Let's Encrypt 这篇指南能帮您拿到证书,并写好一个 proxy_pass http://127.0.0.1:2283; 配置块。对 Immich 来说有一个代理设置很重要:调大上传大小限制,因为手机视频很大。在 nginx 里,就是在 server 块内写上 client_max_body_size 50000M;;默认的 1 MB 会用 413 Request Entity Too Large 拒绝视频上传。
App 强制执行的规则是:端点必须可达,而且实际上必须是 HTTPS。http:// 端点,或者一个省略了端口的直连 IP,正是“App 无法连接到服务器”问题的来源,下面会作为一种具名故障来讲。
第 7 步:外部库与上传的区别,以及如何导入已有的照片目录
照片进入 Immich 有两种方式,它们不是一回事。
- 上传(Uploads)是 Immich 拥有的资源。App 或网页上传器会把文件复制进
UPLOAD_LOCATION。Immich 可以重命名、移动和删除它们。 - 外部库(External libraries)是对已经放在您服务器某个文件夹里的文件所做的只读导入,比如一棵旧的
Pictures目录树、一份 NAS 导出。Immich 就地为它们建立索引并在时间线里显示,但绝不会修改或删除原始文件。
要导入一棵已有的目录树,请把它以只读方式挂载进服务器容器。编辑 docker-compose.yml,在 immich-server: 下面添加一个卷:
immich-server:
volumes:
- ${UPLOAD_LOCATION}:/data
- /etc/localtime:/etc/localtime:ro
- /srv/photos:/mnt/media/photos:ro:ro 保证 Immich 永远无法碰到原始文件。用 sudo docker compose up -d 重新创建容器,然后在 Web 界面里点您的头像 → Administration → External Libraries → Create Library,选择归属用户,在 Folders 下点 Add,并输入容器内路径 /mnt/media/photos,而不是主机路径 /srv/photos。点击 Scan。用主机路径而不是容器路径,是外部库最常见的头号错误;扫描会找不到任何东西,并报告零个资源。
第 8 步:Immich 所要求的升级纪律
这一部分决定了您的 Immich 是顺畅还是崩坏。Immich 发布很快,既不回移(backport)修复,也不支持降级。盲目跟随浮动的 v3 标签,最终会弄坏您的数据库。纪律如下:
- 固定一个版本。把
IMMICH_VERSION设成一个具体标签,比如v3.0.2,而不是那个总会拉取最新 v3.x 的浮动v3。 - 每一次升级前都要读发布说明。破坏性变更,尤其是数据库或向量扩展的变更,都会在那里点明。v3.0 发布就是最典型的例子:它彻底移除了 pgvecto.rs,所以任何还在用旧扩展的人,必须先完成 VectorChord 迁移(早在 v1.133 引入)才能往上升级。
- 先备份数据库(第 9 步)。永远都要,而当发布说明提到数据库时更要如此。
- 同时也取用新的 compose 文件。
IMMICH_VERSION只固定服务器和机器学习镜像。Postgres 镜像是以摘要方式固定在docker-compose.yml里面的,所以一个需要更新数据库扩展的版本,会随附一个新的 compose 文件。请重新下载两个发布资产,重新套用您的.env值,然后再升级。 - 在同一时间前后更新您的手机客户端。服务器只与它匹配的主版本通信,而 App 支持当前和上一个主版本。一个跑到了 App 前面的服务器,会在手机上显示
Your app major version is not compatible with the server!,直到您更新它为止,所以最稳妥的做法是先更新 App。
在您把新文件就位之后,实际的命令如下:
cd /opt/immich
sudo docker compose pull
sudo docker compose up -d
sudo docker image prune第 9 步:备份,一份数据库转储加上原始文件,而且要做测试
对 Immich 的一次备份包含两样东西,缺一不可。数据库保存相册结构、人脸、搜索索引,以及从资源到文件的映射。原始文件目录保存实际的照片。只恢复其中一样,您要么得到一堆没有组织的照片,要么得到一个指向缺失文件的空壳。
用 Postgres 容器内的 pg_dump 转储数据库,具体是 immich 这个数据库,而不是整个集群:
sudo docker exec -t immich_postgres pg_dump --clean --if-exists \
--dbname=immich --username=postgres | gzip > /opt/immich/immich-db-$(date +%F).sql.gz然后用 restic、rsync 或 borg 把 UPLOAD_LOCATION(整棵 /opt/immich/library 目录树,尤其是它的 library/、upload/ 和 profile/ 子文件夹)备份到另一台机器或对象存储。先做数据库、再做文件,这样转储就不会引用一张文件备份还没复制过去的照片。外部库请在它们真正的源头单独备份;Immich 并不拥有它们。
现在是人人都会跳过的部分:测试恢复。一次恢复必须针对一个全新的、服务器从未启动过的技术栈来跑,而且所用的 Postgres 镜像,其向量扩展必须与转储兼容,这正是您绝不能随意乱定 DB 镜像标签的原因。在一台采用相同 compose 和 .env 的临时机器上,清掉任何旧状态,只启动数据库,然后载入转储:
cd /opt/immich
sudo docker compose down -v
sudo docker compose pull
sudo docker compose create
sudo docker start immich_postgres
sleep 10
gunzip --stdout immich-db-2026-07-15.sql.gz |
sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" |
sudo docker exec -i immich_postgres psql --dbname=immich --username=postgres --single-transaction --set ON_ERROR_STOP=on
sudo docker compose up -d在 VectorChord 数据库上,对 search_path 的这段 sed 改写不是可选的,省掉它,恢复就会中途中止。当技术栈带着您的原始文件重新启动后,打开 Web 界面:如果您的照片和相册都在,您的备份就是有效的。如果您从未跑过这一步,那您拥有的就不是备份,而是一份指望。
故障模式,以及您会看到的字符串
机器学习容器被 OOM 杀掉。sudo docker compose logs immich-machine-learning 会突然中断,docker compose ps 显示它处于 Restarting,退出码是 137。sudo dmesg | grep -i oom 会证实这一点:Out of memory: Killed process ... (python3)。随后搜索和人脸任务就会卡住。原因是留给模型的内存太少。修复办法,按顺序:加交换分区(第 1 步);给 VPS 更多内存;或者,如果您实在做不到,就在 Administration → Settings → Machine Learning Settings 里关闭 Smart Search 和 Facial Recognition 来禁用机器学习,您保留备份和相册,只是失去按内容搜索的能力。从 compose 文件里移除 immich-machine-learning 服务也有同样的效果。
升级后 Postgres 拒绝启动。服务器日志会反复出现类似这样的一行:The database currently has VectorChord 0.5.3 activated, but the Postgres instance only has 0.4.2 available. This most likely means the extension was downgraded.,或者在较旧的技术栈上是 The pgvecto.rs extension is not available in this Postgres instance.。原因是数据库镜像的扩展版本,比您数据被升级到的版本还旧,这几乎总是来自手动编辑镜像标签,或者把一份较新的转储恢复到了较旧的镜像上。修复办法是使用匹配的 Postgres 镜像:取用与您数据库匹配的那个发布版本的 compose 文件,不要降级,并且只恢复到兼容的镜像上。
手机 App 无法连接到服务器。在您输入 URL 后,登录界面显示一个连接错误 / Server is not reachable。有三种原因:您在代理只提供 https:// 的地方输入了 http://;您直连了后端却省掉了端口,于是它尝试连的是 example.com(端口 443)而不是 example.com:2283;或者反向代理没有转发 /api。修复办法是输入完整的 https://photos.example.com URL,并先在手机浏览器里确认它能打开。如果浏览器能打开而 App 不行,那就是代理把路径剥掉了,或者证书是自签名的,App 会拒绝不受信任的证书。
导入途中磁盘用尽。上传开始失败,缩略图变空白,日志显示 ENOSPC: no space left on device,或者来自 Postgres 的 could not extend file ... No space left on device。df -h 显示 UPLOAD_LOCATION 所在的卷已 100%。这就是为什么要在导入大图库之前先规划好磁盘。恢复办法是挂载一个更大的卷,停掉技术栈,把 UPLOAD_LOCATION 移过去,更新 .env,然后重新启动;或者,如果您的服务商允许,直接扩容现有磁盘。Postgres 在填满时可能会卡死,所以在断定数据损坏之前,先清出空间并重启数据库容器。
FAQ
Immich 需要多少内存和磁盘?
Immich 的官方要求是内存最低 6 GB、推荐 8 GB;对一个小图库来说,4 GB 加交换分区是实际的下限,而且无论如何都要配置交换分区,因为机器学习容器才是那个会飙升的部分。磁盘方面,请按您完整图库的大小,再加上大约 10–20% 用于生成的缩略图和预览图来预算,并放在本地存储上,绝不要把 Postgres 数据目录放在网络共享上。如果您还在决定还要跑些什么,2026 年该自托管什么的指南 把 Immich 的占用和其他服务放在一起做了对比。
我能不用 GPU 运行 Immich 吗?
可以。机器学习容器在 CPU 上运行得很好,GPU 只是加快智能搜索的索引,以及在使用正确的镜像变体时加快视频转码。在 CPU 上,对一个大图库做初始索引可能会在后台跑上好几个小时,但它不会阻塞备份或浏览。如果您的机器小到根本跑不动机器学习,您可以在管理员设置里关闭 Smart Search 和 Facial Recognition,其余功能照常保留。
我该如何安全地升级 Immich?
把 IMMICH_VERSION 固定到一个具体标签,比如 v3.0.2,每次升级前都读发布说明,并先备份数据库。由于 Postgres 镜像是固定在 docker-compose.yml 里而不是由 IMMICH_VERSION 决定的,请从您的目标发布版本重新下载 compose 文件和 example.env 两者,重新套用您的值,然后运行 docker compose pull && docker compose up -d。绝不要放任版本无人看管地浮动,Immich 会发布破坏性变更,而且不支持降级。
我到底该备份什么?
两样东西,一起:immich 数据库的一份 pg_dump,以及整个 UPLOAD_LOCATION 原始文件目录。数据库保存相册、人脸和资源到文件的映射;目录保存实际的照片,而一次恢复两者都需要,外加一个向量扩展兼容的数据库镜像。先做数据库转储、再做文件复制,并且至少在一台临时机器上测试一次恢复,一份未经测试的备份算不上备份。
我该如何导入我已有的照片文件夹?
把该文件夹以只读方式作为一个额外的卷挂载进 immich-server 容器(例如 - /srv/photos:/mnt/media/photos:ro),重新创建容器,然后在 Administration → External Libraries 里创建一个库并添加容器内路径 /mnt/media/photos。Immich 会就地为这些文件建立索引,绝不会修改或删除它们。最常见的错误是输入了主机路径而不是容器路径,这会导致扫描什么都找不到。