用 Immich 搭建私人相册:VPS 部署、手机上传与异地备份

在Linux VPS上用官方Docker Compose部署Immich v3.2.4,配置Caddy HTTPS和手机照片上传,核对内存与存储需求,并为原片和数据库建立异地备份与恢复流程。

把应用部署到土耳其|BRNCHOST · 土耳其 VDS
云服务器,积分可续期|雨云 · 国内外节点 · 积分兑换权益
低价年付,大流量 VPS|RackNerd · SSD 存储 · 1Gbps 端口
香港轻量,搭个小站|晚安云 · 香港云服务器
香港 VPS,大带宽可选|野草云 · BGP 直连
大陆访问,精品线路|搬瓦工 · CN2 GIA / CTGNet 套餐
资料归档,交给 AI 整理|WorkBuddy · 本地文件处理
建站起步,先看应用镜像|腾讯云 · 轻量应用服务器
CN2 GIA,中国方向优化|DMIT · Premium 网络
双 ISP 原生住宅 IP|丽萨主机 · 美国 9929 精品线路
高频 CPU,多地部署|Evoxt · 云服务器 · 每周异地备份
京东云轻量云主机:129元/年,新人专享,限购1台

手机里有几万张照片,换机时复制一遍,电脑上再存一遍,最后往往留下好几个内容相近的文件夹。想按日期浏览、从手机随时找回原图,又不想把整理工作全部交给网盘,可以在自己的服务器上部署 Immich。它提供网页和手机应用,把照片、视频放进同一个相册系统,也能自动接收手机上传的新文件。

相册与文字博客的开销相差很大。视频会占满磁盘,首次导入会触发后台处理,手机后台上传也受系统限制。买一台只有几十 GB 硬盘的小 VPS,再把所有原片搬进去,通常很快就要考虑扩容。

这篇教程使用 Immich v3.2.4 的官方 Docker Compose 文件,在 Linux VPS 上部署,通过 Caddy 提供 HTTPS。先上传少量照片确认入口和存储,再接入手机,再把备份保存到另一台机器。已经有大容量 NAS 的读者,也可以把相同思路用在家里的设备上;服务器放在哪里,应当由存储容量和访问方式决定。

先算磁盘,再选服务器

Immich 的硬件要求列出的最低配置是 2 核 CPU、6GB 内存,建议配置为 4 核、8GB。只有 4GB 内存时,需要关闭机器学习功能,不能把完整默认部署的要求直接降到这个档位。v3 的 amd64 机器学习容器还要求 CPU 支持 x86-64-v2;较老的虚拟机要留意宿主机给出的 CPU 指令集。

选机时先看内存和磁盘,再比较价格。小规格机器适合学习容器,未必适合承载多年照片。已有服务器上如果还运行数据库、网站和其他服务,也需要给这些程序留下内存,不能按机器总内存来判断 Immich 是否够用。

磁盘预算可以从现有原片的体积开始。例如有 200GB 照片和视频,给原片预留 200GB 之后,还要考虑缩略图、转码文件、数据库、容器镜像与后续新增内容。官方说明生成内容平均可能增加约 10%—20% 的相册体积,具体取决于媒体组成。这一比例只是容量规划的参考,不能据此认定 240GB 磁盘一定够用。

更稳妥的办法是先整理现有文件总量,再计算未来一年预计增加多少。经常拍 4K 视频的人,增长速度可能远高于只存照片的人。备份磁盘另算:主机里留出一个名为 backup 的文件夹,并不会减少同一块硬盘损坏带来的损失。

数据库应放在本地 SSD 上,不能把 PostgreSQL 数据目录挂到网络共享。媒体目录可以采用适合自己的存储方案,但不要在不理解挂载方式时,把数据库与原片一起搬到远程盘。远程存储临时断开、权限变化和容量耗尽,都会成为额外的维护问题。

如果需要新购服务器,可以从雨云查看当前配置,优惠码为 KuZhuJi。选择时重点核对内存、磁盘扩容方式、出口带宽和流量规则;相册中的视频预览、原图下载与异地备份都会占用传输额度,单看 CPU 核数并不能决定是否合适。

建一个独立目录,保留官方组件

以下操作以具备管理员权限的 Linux 用户为例。服务器需安装 Docker Engine 与 Compose 插件,可以先查看版本:

docker version
docker compose version

这里使用带空格的 docker compose。安装 Docker 的具体步骤按发行版对应的官方说明处理,不混用旧的 docker-compose 包。还需要 curl 和 openssl,下载配置以及生成数据库密码会用到它们。

在自己的用户目录下创建独立目录,并下载同一个发行版中的两个文件:

mkdir -p ~/immich-app
cd ~/immich-app
curl -fL -o docker-compose.yml \
  https://github.com/immich-app/immich/releases/download/v3.2.4/docker-compose.yml
curl -fL -o .env \
  https://github.com/immich-app/immich/releases/download/v3.2.4/example.env
chmod 600 .env
openssl rand -hex 24

最后一条命令输出随机密码。保存到密码管理器后,把它填入 .env 的 DB_PASSWORD。不要保留默认值 postgres,也不要把 .env 放进公开 Git 仓库。

编辑 .env 中以下项目,其余数据库用户名和库名先保持官方默认值:

UPLOAD_LOCATION=./library
DB_DATA_LOCATION=./postgres
TZ=Asia/Shanghai
IMMICH_VERSION=v3.2.4
DB_PASSWORD=REPLACE_WITH_GENERATED_HEX_PASSWORD
DB_USERNAME=postgres
DB_DATABASE_NAME=immich

REPLACE_WITH_GENERATED_HEX_PASSWORD 是占位文字,必须换成刚才生成的密码。十六进制字符属于官方建议使用的字母与数字范围,避免特殊字符与 Compose 解析发生冲突。

这套官方 Compose 不只有相册服务。它还包括机器学习服务、缓存,以及带向量扩展的 PostgreSQL 镜像。新手部署建议整套保留,不要看到服务器上已有 PostgreSQL 就直接替换数据库组件。普通 PostgreSQL 镜像未必包含相册所需扩展,已有实例的版本、扩展和权限也要单独评估。

打开 docker-compose.yml,找到 immich-server 的 ports,把默认映射改为:

ports:
  - '127.0.0.1:2283:2283'

只改端口映射,保留服务的其他内容。这样相册端口只监听服务器本机,公网访问由反向代理负责。官方文件已经声明了媒体和数据库挂载,不必额外复制一份简化版 Compose,否则容易遗漏版本更新所需组件。

UPLOAD_LOCATION=./library 表示主机上的媒体根目录叫 library,它在容器里挂载到 /data。根目录名称与其内部的 library 子目录不是同一层级。以后备份整个媒体根目录,不要因为名称相同而只复制内部一个文件夹。

启动前检查配置结构,然后拉取镜像:

cd ~/immich-app
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps

config --quiet 不会把包含密码的完整配置打印出来。它能发现 YAML 或变量解析错误,但不能保证服务器硬件、网络和存储一定满足运行需要。若容器持续重启,先查看日志,处理错误后再配置域名:

docker compose logs --tail=100 immich-server database

管理员先在私下创建,再开放域名

Immich 的第一个注册用户是管理员。新站初始化期间不要把未注册的入口留给公网访问,先通过 SSH 隧道完成注册。

在自己的电脑上运行下面的命令,替换 SSH 用户和服务器地址;终端保持打开:

ssh -L 2283:127.0.0.1:2283 USER@SERVER_IP

然后打开本机浏览器访问 http://127.0.0.1:2283,按 Getting Started 提示创建管理员。若本机 2283 已被占用,可以把命令左侧端口改为 12283,对应访问 http://127.0.0.1:12283,右侧服务器端口仍为 2283。

给家人使用时,在管理页面为每个人创建独立账号。共用一个管理员账号会把日常浏览和管理操作混在一起,也不方便区分各自上传的内容。

假设准备使用 photos.example.com,先把它解析到服务器,确认公网入口允许 80 和 443。已有 Caddy 时,在其配置中加入独立站点:

photos.example.com {
    reverse_proxy 127.0.0.1:2283
}

这个示例对应 Caddy 安装在主机上。如果 Caddy 自己运行在容器里,容器内的 127.0.0.1 指向 Caddy 容器,不能直接套用这个上游地址;需要按共享容器网络配置服务名。修改已有配置前备份文件,再验证与重载,配置路径以实际安装位置为准:

sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

Immich 要放在域名根路径,不能挂在 example.com/immich 下面。它的反向代理说明也要求上传大小、请求头以及 /.well-known/immich 路由正确。如果外面另有 CDN、网关或控制面板代理,每一层都可能限制视频上传,改好 Caddy 并不会自动解除其他平台的限制。

域名访问正常后,先从网页上传几张不敏感的照片和一个短视频。检查缩略图、原图下载和视频播放,观察磁盘落盘目录,再决定是否导入整个相册。初次整理大量媒体时会触发后台处理,不宜同时给一台配置紧张的服务器增加其他重任务。

手机上传先选相册,别急着删除原片

手机应用中填写自己的 HTTPS 地址,例如 https://photos.example.com,登录前面创建的账号。在备份页面选择需要上传的本地相册,先从相机照片开始,避免一开始把聊天应用缓存、下载目录和所有视频一起选中。

Immich 官方文档中的手机备份相册选择界面

图中是官方文档的相册选择示例。实际界面会随手机平台与版本调整;选择哪些本地相册,决定了第一批需要传输的文件范围。

开启备份后,应用会在打开或恢复到前台时上传,也会安排后台任务。默认只在 Wi-Fi 下上传,可以在设置中调整。手机备份文档说明了平台差异:Android 的电池优化可能阻止后台任务,iOS 则由系统决定后台调度时间,不能把它理解为拍完立即传完。

首次上传建议连上稳定 Wi-Fi,保持应用在前台一段时间,查看是否还有待上传项目。有上传失败时,先查手机授权、网络、代理限制和服务器剩余空间,重新开启备份并不能解决磁盘已满的问题。已有大量 iCloud 照片时,应用还可能先从 iCloud 取回文件,手机需要额外缓存空间,也会产生下载流量。

相册同步把手机的相册结构整理到服务端,是从手机到服务器的单向同步。它不会把手机相册后续的所有删除和移动都照搬过去。已有同名服务端相册时,照片可能合并进去;如果那个相册已共享,新加入的内容也可能随之共享,因此启用前先查看相册的分享状态。

更容易误会的是 Free Up Space,也就是释放手机空间。它会处理已经上传的本地媒体,让照片从手机原生图库移走。**如果启用了 iCloud 照片,本地删除还会同步到 iCloud 与同一账号下的其他设备。**不要把“Immich 已上传”和“iCloud 永远还留着一份”同时当成默认前提。

先完成独立备份,再使用清理功能;确认删除列表中没有需要继续留在手机上的文件。聊天应用可能依赖本地媒体,清理后聊天记录里的图片也可能无法正常显示。这些文件可以通过保留相册选项留在设备上。手机系统回收站中的内容仍占空间,要立即回收容量需另外清空,但清空前应再次确认保留副本。

给原片和数据库一起做备份

Immich 会自动生成数据库备份,但数据库备份只保存文件路径、账号和相册等元数据,不包含原始照片与视频。服务器损坏后,只有 SQL 文件无法还原照片;只有照片目录也无法完整重建原有相册系统。

按备份说明准备独立副本时,保留媒体根目录、数据库导出文件,以及部署配置。为了让数据库与媒体保持一致,可以暂停 immich-server 后备份,数据库容器仍然运行。下面是一组针对本篇默认路径的手动命令,需在 Bash 中运行;备份目录放在应用目录外,避免把归档文件自身反复打包。

cd ~/immich-app
umask 077
IMMICH_BACKUP_DIR="$HOME/immich-backups/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$IMMICH_BACKUP_DIR"
set -o pipefail
docker compose stop immich-server

导出数据库。数据库用户名和库名对应前面的默认配置,修改过配置的人需要一起替换:

docker compose exec -T database \
  pg_dump --clean --if-exists --dbname=immich --username=postgres \
  | gzip > "$IMMICH_BACKUP_DIR/database.sql.gz"

这条管道必须正常退出,不能忽略错误继续做下一步。媒体归档针对 ./library 这个根目录;如果改过 UPLOAD_LOCATION,请修改归档路径:

tar -czf "$IMMICH_BACKUP_DIR/media.tar.gz" -C "$HOME/immich-app" library
cp .env docker-compose.yml "$IMMICH_BACKUP_DIR/"
gzip -t "$IMMICH_BACKUP_DIR/database.sql.gz"
tar -tzf "$IMMICH_BACKUP_DIR/media.tar.gz" > /dev/null
docker compose start immich-server

大相册归档可能耗时很长,需要预留停机时间和归档空间。若媒体文件由容器以其他用户身份创建,当前用户可能无法读取;归档遇到权限错误时,确认路径后用管理员权限读取,不要为此把整个媒体目录改成公开可写。中间任何一步失败都要处理错误,并在结束维护时启动 immich-server;不要把半个归档当成成功备份。压缩文件结构检查只能发现部分损坏,最终仍需要恢复到独立目录验证。

以上目录仍在同一台 VPS 上。下一步把完整备份复制到另一台机器或其他独立存储,限制读取权限,因为 .env 包含数据库密码,媒体文件可能含位置等私人信息。定期留多个日期的副本,不能只保留一个会被下一轮覆盖的文件。照片很多时,可以改用支持增量的备份工具,但数据库与媒体的一致性要求不变。

直接复制正在运行的 postgres 数据目录,不等于这里的数据库导出。不要把两种方式混在一个未经验证的恢复流程里。本篇采用逻辑导出与完整媒体归档,恢复时也应按相应路径处理。

恢复到独立目录,确认照片真的能打开

可以先用一台独立测试机器或另一个隔离环境完成恢复,不要拿唯一的线上实例试着覆盖。准备与备份相同版本的 Compose 和 .env,还原媒体根目录,保留原来的容器挂载关系,然后按官方的新安装恢复流程操作。

Immich 官方文档中的数据库恢复页面

图中是官方文档保留的恢复设置示例,截图中的实例版本为 v2.4.1,用于说明备份选择入口,并非本篇部署实例。恢复会覆盖当前数据库,不能把它当作普通导入文件的按钮。

新实例中,把备份对应的媒体目录放回新的 UPLOAD_LOCATION,包括其中的 backups、upload、library、profile、thumbs 与 encoded-video 等已有内容。启动服务后,在欢迎页面选择 Restore from backup,检查存储目录状态,再选择或上传数据库 .sql.gz 备份。使用外部图库的人,还要恢复相同的容器内挂载路径。

完成后登录账号,检查几张不同时间的原图、一个较大视频、相册内容与原图下载。也要查看新实例的任务和日志,确认没有大量缺失文件。新恢复实例暂时不要连接正在上传的手机,避免在检查期间生成新的变更。

升级前同样要备份。固定版本的好处是能明确记录这套数据对应哪一版服务,但 Immich 不支持直接降级,不能把镜像标签改回旧版本就认定已经回退。需要恢复时,使用升级前的完整数据和匹配版本,避免旧程序读取已经迁移过的数据库。

手机端能看到照片、另一台机器上也有可恢复的备份之后,再逐步导入剩下的相册。每天新增多少、磁盘还剩多少、备份何时成功,这几项需要长期留意;相册存储会随着拍摄继续增长,初次部署时留出的空间并不会一直够用。