Skip to content

自建 iCloud 完整方案:Immich + Cloudflare Tunnel 全家共享照片库(永久免费)

用一台Mac mini + Immich + Cloudflare Tunnel,搭一套全家共享、多账号多设备、永久免费的私人 iCloud:

  • 全家多账号自动备份,浏览器随时打开
  • 共享相册:孩子照片 / 旅行 / 聚会,全家都能加都能看
  • 给爷爷奶奶发链接,扫码即可看孙子照片,无需注册
  • 不开端口、不要公网 IP,数据全在自家电脑上
  • 服务器选择 + Docker 环境准备
  • 在 Cloudflare「联网」菜单创建 Tunnel
  • Docker Compose 部署 Immich + 初始化管理员和家庭成员账号
  • 配置 Cloudflare Access 邮箱鉴权(防陌生人扫到)
  • 移动端 App 安装、远程登录、自动备份
  • 共享相册 / Partner Sharing / Public Link 三种共享方式
  • 验证清单和常见错误排查

  • 一个 Cloudflare 账号
  • 一个已经托管到 Cloudflare 的域名,例如 example.com
  • Cloudflare Zero Trust 组织(到第六步配置 Access 时再开通即可,创建 Tunnel 不依赖 Zero Trust)
  • 每位家庭成员一个能收邮件的邮箱(爸 / 妈 / 老婆 / 老公 / 老人 各一个)

照片是不可再生的家庭资产,用一台专用、可靠、24 小时常开的设备做主机:

设备说明
Mac minimacOS Docker Desktop,体验最简单
台式机 + Linux性能足、硬盘大、Docker 部署稳定
群晖 / 飞牛 NASRAID 自带磁盘冗余,套件中心或 Docker 装 Immich

最低配置:4 核 CPU / 4G 内存 / 256G 系统盘 + 独立照片盘(NAS 务必 RAID 1 双盘镜像)。Immich 后台扫描和缩略图持续吃 CPU,低于此配置会跑得很慢。

请把下面示例里的域名和 IP 换成你自己的:

示例
域名example.com
Immich 外网域名photos.example.com
Immich 服务器局域网地址192.168.1.50
Immich Web 端口2283

Immich 真的能多人共享吗?提前把答案说清,避免做完才发现不对:

能力是否支持视频里怎么用
Admin 创建无限用户账号支持全家每人一个
每账号多设备登录支持iPhone + iPad + 电脑同时
每账号独立时间线和隐私支持老婆备份的不影响老公
Shared Album 共享相册(多人写)支持全家共享孩子照片
Partner Sharing 配偶单向共享整个库支持夫妻互看对方所有照片
Public Share Link 生成链接发外人支持给爷爷奶奶看孙子,无需注册

第一步:服务器选择和系统准备

Section titled “第一步:服务器选择和系统准备”

本手册以 Ubuntu 24.04 LTS 为示范,命令在 macOS / 其他 Linux 发行版基本一致。

1)确认 Docker 已安装

Terminal window
docker --version
docker compose version

如果没装:

Terminal window
# Ubuntu / Debian
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# 重新登录 shell 让组生效

2)给 Immich 准备一个独立的目录

例如在系统默认的“Pictures”目录下创建一个 “immich” 的文件夹,用于存储原始的图片文件

3)测试本机Immich端口未占用

Terminal window
sudo lsof -i :2283

无输出即可用。


⚠️ 创建 Tunnel 不需要先初始化 Zero Trust。第六步的 Access 邮箱鉴权才需要 Zero Trust,到时候再开通即可。

进入 Cloudflare Dashboard(dash.cloudflare.com),左侧栏「保护和连接」分组下:

联网(Networks)
→ Tunnels(隧道)
→ Create a tunnel(创建隧道)
home-photos-tunnel

我们用 Docker Compose 跑 cloudflared,主机上不需要装任何 cloudflared 二进制——选项只是用来定位那串 token。

页面下方「安装并运行」区有三个命令框:

Install cloudflared ← 安装二进制(用不上,跳过)
Install as service ← 装为系统服务(用不上,跳过)
Or, run tunnel (manual) ← 手动运行命令(含我们要的 token)

Install as serviceOr, run tunnel (manual) 任选一个,只复制命令里的 token 部分——也就是 eyJhIjoi... 这一长串 base64 字符(通常 200+ 字符):

Terminal window
# Install as service 长这样(Debian 选项):
sudo cloudflared service install eyJhIjoi**********...**********aC...
└─────── 这就是 token ───────┘
# Or, run tunnel (manual) 长这样:
cloudflared tunnel run --token eyJhIjoi**********...**********aC...
└─────── 这就是 token ───────┘
# 如果选了 Docker 环境,命令会长这样(一眼就能看到 token):
docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token eyJhIjoi**********...**********aC...

复制保存到密码管理器——后面 Docker Compose 的 .env 文件里会用到。关闭这个页面就找不回了(只能删除 Tunnel 重建)。

页面最底部「连接状态」会一直显示「正在等待您的隧道连接…」,这是正常的——因为我们还没有启动 cloudflared 容器。第三步起完 docker compose 后,这里会自动变成「Healthy」,再回头确认即可。


在服务器上选一个工作目录存放 Immich 的配置文件。本手册统一用 ~/immich/(即当前用户家目录下的 immich 文件夹):

Terminal window
mkdir -p ~/immich
cd ~/immich
pwd # 应输出类似 /home/yourname/immich,确认你已进入这个目录

⚠️ 后续所有 docker compose 命令都必须在这个目录下执行——docker compose 会自动读取当前目录的 docker-compose.yml.env。如果在其他目录运行命令会找不到配置。

💡 为什么选 ~/immich/

  • 当前用户家目录,普通用户也有读写权限,不必每次都 sudo
  • 路径短、好记
  • 数据库文件(./postgres/)会自动放在这个目录下,方便集中备份
  • 如果你想放别的位置(例如 /opt/immich//srv/immich/),随意——只要把后面所有 ~/immich 替换成你的实际路径,且确保当前用户对该目录有读写权限即可

~/immich/ 目录下新建 .env(注意:文件名以点开头,是隐藏文件)。可以用 nanovimvi 任意编辑器:

Terminal window
vim ~/immich/.env
# 或: nano ~/immich/.env

填入下面内容(Tunnel token 替换成第二步复制的):

# === 照片实际存储位置(重要)===
# 这是宿主机磁盘路径,所有照片、缩略图、转码视频都存这里
# 强烈建议指向独立的大容量存储盘,单独一份可以方便用 rsync 备份
UPLOAD_LOCATION=/mnt/photos/immich-library
# === PostgreSQL 数据库存储位置 ===
# 元数据(文件名、EXIF、相册关系、用户、共享)存这里
# 数据库挂了照片还在,但相册结构会丢,**务必单独定期备份这个目录**
DB_DATA_LOCATION=./postgres
TZ=Asia/Shanghai
# === Immich 版本 ===
# v2 = 锁定大版本号,自动获取 v2.x.x 最新补丁,不会跳到 v3 breaking change
# release = 永远最新(含 v3+),不推荐生产环境
# v2.7.5 = 锁定具体版本,最稳定但需手动升级
IMMICH_VERSION=v2
# === 数据库账号密码 ===
# ⚠️ 重要:DB_PASSWORD 只能包含字母和数字(A-Za-z0-9),
# 不能含特殊字符(! @ # $ % & * 等)和空格,
# 否则 PostgreSQL 容器初始化时会失败
DB_PASSWORD=改成一个强密码仅限字母数字
DB_USERNAME=postgres
DB_DATABASE_NAME=immich
# === Cloudflare Tunnel ===
TUNNEL_TOKEN=粘贴你刚才第二步复制的 Cloudflare Tunnel token

保存退出:nano 按 Ctrl+O 回车保存,Ctrl+X 退出;vim 按 Esc 然后输入 :wq 回车。

仍然在 ~/immich/ 目录下(与 .env 同目录)新建 docker-compose.yml

Terminal window
nano ~/immich/docker-compose.yml

填入下面内容(精简版,已剔除机器学习服务,已对齐 Immich v2.7.5 官方模板):

name: immich
services:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-v2}
volumes:
- ${UPLOAD_LOCATION}:/data
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ports:
- "2283:2283"
depends_on:
- redis
- database
restart: always
healthcheck:
disable: false
redis:
# ⚠️ 注意:Immich 已从 Redis 迁移到 Valkey(Redis 协议改 license 后的 BSD 分叉)
# 不要再用 docker.io/redis:6.2-alpine,会触发兼容性告警
container_name: immich_redis
image: docker.io/valkey/valkey:9
healthcheck:
test: redis-cli ping || exit 1
restart: always
database:
container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
POSTGRES_INITDB_ARGS: "--data-checksums"
volumes:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
shm_size: 128mb
restart: always
healthcheck:
disable: false
cloudflared:
container_name: cloudflared
image: cloudflare/cloudflared:latest
restart: unless-stopped
command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}

关键字段说明

字段作用不写会怎样
name: immichDocker Compose 项目名(影响容器网络名 immich_default不写默认用目录名,但官方模板写了,跟着写不会错
image: ...:${IMMICH_VERSION:-v2}锁定 Immich 大版本号release 会跳到 v3 breaking change
valkey/valkey:9Redis 兼容缓存老镜像 redis:6.2-alpine 会触发 Immich 启动告警
shm_size: 128mbPostgreSQL 共享内存默认 64MB,大库扫描时性能会下降 30-50%
healthcheck: disable: false启用容器内置健康检查docker compose ps 显示 running 而不是 healthy,故障排查更慢

关于机器学习服务的取舍

Immich 默认提供一个独立容器 immich-machine-learning,做四件事——人脸识别、CLIP 语义搜索(“找一下海边的狗”)、重复照片检测、OCR 文字识别。本手册默认精简版不启用,原因是:

  • 多数家庭用户的核心需求是「备份 + 共享」,不是 AI
  • 首次扫描几万张照片要 1-2 天,吃 CPU
  • 内存额外多吃 1-2 GB
  • 中文 CLIP 语义搜索效果一般

如果后期想用「重复照片检测」或「按内容搜索」,把下面这段加回 docker-compose.yml

immich-machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-v2}
volumes:
- model-cache:/cache
env_file:
- .env
restart: always
healthcheck:
disable: false
# 同时在文件最底部加(与 services 同级):
volumes:
model-cache:

支持 GPU 加速(NVIDIA CUDA / Apple Silicon Neural Engine / Intel OpenVINO),CPU 也能跑只是慢。详见 官方硬件加速文档

启动前用 ls -la ~/immich/ 确认两个文件都在 同一个目录

Terminal window
ls -la ~/immich/

预期输出(关键看后两行):

drwxr-xr-x 4 yourname yourname 128 Apr 30 12:00 .
drwxr-xr-x 30 yourname yourname 960 Apr 30 12:00 ..
-rw-r--r-- 1 yourname yourname 650 Apr 30 12:00 .env ← 第 3.2 步创建
-rw-r--r-- 1 yourname yourname 1450 Apr 30 12:00 docker-compose.yml ← 第 3.3 步创建

⚠️ 如果只看到 docker-compose.yml 没看到 .env:执行 ls ~/immich/ (不带 -a)会因为 .env 是隐藏文件(点开头)而看不见。-la 才能列出隐藏文件。

快速核对 .env 关键字段已填(可选但推荐):

Terminal window
# 确认 token 不是占位符
grep TUNNEL_TOKEN ~/immich/.env
# 期望:TUNNEL_TOKEN=eyJhIjoi...(一长串),不能是 "粘贴你刚才..."
# 确认密码已改且只含字母数字
grep DB_PASSWORD ~/immich/.env
# 期望:DB_PASSWORD=YourStrong123(不含 ! @ # $ 等特殊字符)

启动后还会自动生成第三个目录:

~/immich/
├── .env
├── docker-compose.yml
└── postgres/ ← docker compose 启动时自动创建(DB_DATA_LOCATION=./postgres)

确认在 ~/immich/ 目录下,启动所有容器:

Terminal window
cd ~/immich # 再次确认在正确目录
docker compose up -d

查看容器状态:

Terminal window
docker compose ps

期望全部 STATUS = Up ... (healthy)(healthcheck 启用后会显示 healthy 标记,可能需要 30-60 秒才转为 healthy):

NAME STATUS PORTS
cloudflared Up 30 seconds
immich_postgres Up 45 seconds (healthy)
immich_redis Up 45 seconds (healthy)
immich_server Up 30 seconds (healthy) 0.0.0.0:2283->2283/tcp

跟踪日志确认 cloudflared 已连上 Cloudflare:

Terminal window
docker compose logs -f cloudflared

看到 Registered tunnel connection 就是连上了,按 Ctrl+C 退出日志。

跟踪 Immich 服务器日志确认监听端口:

Terminal window
docker compose logs -f immich-server

看到 Listening on port 2283 即可,按 Ctrl+C 退出。

本机访问验证

浏览器打开(把 192.168.1.50 换成你 Immich 服务器的实际局域网 IP;本机访问也可用 http://localhost:2283):

http://192.168.1.50:2283

应能看到 Immich 的「Welcome / Getting Started」初始化页面。看到这个页面 = 第三步完成。


第四步:初始化 Immich 管理员 + 创建家庭账号

Section titled “第四步:初始化 Immich 管理员 + 创建家庭账号”

浏览器打开 http://192.168.1.50:2283

  1. 创建管理员账号——用你自己的主邮箱,密码强度要够
  2. 登录管理员后台

进入:

Administration
→ Users
→ Create User

为每位家庭成员创建账号,邮箱必须填本人真实邮箱(后面 Cloudflare Access 验证码会发到这里):

用户EmailStorage Quota(可选)
老婆wife@example.com不限
孩子kid@example.com100 GB
父亲dad@example.com50 GB
母亲mom@example.com50 GB

每个新账号会收到一个临时密码,让对方第一次登录后改密码。

必做:开启 Storage Template 让磁盘文件可读

Section titled “必做:开启 Storage Template 让磁盘文件可读”

Immich 默认把照片以 UUID 命名存盘(<asset-uuid>.jpg),万一数据库挂了,文件夹里全是无意义的 UUID。

强烈建议第一时间开启 Storage Template,让文件按”用户名 / 年 / 年-月 / 原文件名”组织。万一 Immich 哪天挂了,照片仍可当普通相册直接用。

这一步最大的价值:未来任何时候都可以直接 rsync 这个目录到外挂硬盘做冷备份,备份盘上看到的是按年月组织的相册,不是一堆 UUID。


第五步:在 Tunnel 里添加路由(Public Hostname)

Section titled “第五步:在 Tunnel 里添加路由(Public Hostname)”

回到 Cloudflare Dashboard 的「联网」菜单,点击进入第二步创建的 Tunnel:

联网(Networks)
→ Tunnels(隧道)
→ home-photos-tunnel ← 点进去

5.2 先确认 Tunnel 健康(验证第三步成功)

Section titled “5.2 先确认 Tunnel 健康(验证第三步成功)”

进入 Tunnel 详情页应看到「概览」tab,确认下面 4 个指标:

指标期望值含义
活动副本1cloudflared 容器已连上 Cloudflare 边缘
状态健康连接稳定
边缘位置显示就近数据中心(如 sjc06, sjc07... / nrt01... / lax02... 等)已建立 4 条 QUIC 长连接做高可用
副本表一行 linux_amd64 + 版本 2026.x.x容器版本正确
路由0(暂时是 0,本步骤就是来加这条路由的)

如果「活动副本 = 0」或状态「不健康」:第三步 cloudflared 容器没起来。回到服务器上 docker compose logs cloudflared 看错误(最常见是 TUNNEL_TOKEN 复制时漏了首尾字符,或包含了换行)。

进入路由配置有两个入口(任选其一):

  • A. 顶部点「路由」tab → 选 Create a route / 添加路由
  • B. 概览页底部「路由」可视化图里点「+ 添加路由」按钮

路由类型如果让选,选 「公开应用程序」(Public Application / Published Application)——而不是「私有主机名」「私有 CIDR」或「Workers VPC」。这三个是 Zero Trust WARP 客户端场景,本手册不用。

弹出「添加已发布应用程序」表单,按下表填写:

字段填写说明
子域(可选)photos你想用什么二级域名访问
域 *(必填)example.com从下拉菜单选你已托管到 CF 的域名
完整主机名(自动拼接,只读)photos.example.com上两项填完会自动显示
路径(可选)留空整个域名都给 Immich,不分路径。⚠️ 该字段是正则表达式,留空 = 匹配所有
服务 URL *(必填)http://immich-server:2283⚠️ 必须以 http:// 开头,不是 https://——见下方说明

⚠️ URL 必须含协议前缀(scheme)

表单的 placeholder 显示 https://localhost:8080 是 Cloudflare 给的通用示例(误导:不是要求 https):

  • 正确http://immich-server:2283(Immich 容器内部是裸 HTTP)
  • ❌ 错误:https://immich-server:2283(Immich 容器没装证书,TLS 握手失败 → 502 Bad Gateway)
  • ❌ 错误:immich-server:2283(必须有 scheme,否则保存报错)

外网访问时浏览器看到的 https://photos.example.com 那一段 https 是 Cloudflare 边缘自动加的,与 cloudflared → Immich 这段内部连接的协议无关,不冲突。

💡 为什么 URL 用 immich-server 而不是 localhost192.168.1.50

  • cloudflared 是个容器,它的 localhost 是容器内部,不是主机
  • 但 cloudflared 和 immich-server 在同一个 Docker Compose 项目里,Docker 自动建了内部网络,可以直接用 服务名 互相访问(即 immich-server
  • 如果你把 cloudflared 部署在另一台机器上(不和 immich-server 在同一个 compose),就要写 Immich 服务器的实际局域网 IP,例如 http://192.168.1.50:2283

📌 因为 URL 直接用了 http://(不走 TLS),TLS 验证开关不需要;HTTP/2 也是默认开启。直接点「添加路由」保存即可,不用展开任何高级设置。

仅当你的 URL 用了 https://(例如后端真的有 HTTPS 自签证书)才需要找「TLS 设置」分组打开「No TLS Verify」。本手册不涉及。

点「保存(Save)」。Cloudflare 会做两件事:

  1. 在 Tunnel 上加一条路由

  2. 自动给你 CF 域名加一条 DNS CNAME 记录:

    photos.example.com CNAME <tunnel-id>.cfargotunnel.com (代理)

通常 30 秒内 DNS 生效。回到 Tunnel 概览页应看到「路由 = 1」(不再是 0)。

初步可达性测试(不需要等 Access 配置,先确认链路通):

Terminal window
curl -I https://photos.example.com

期望返回 HTTP/2 302HTTP/2 200(302 跳转到 /auth/login 是正常的,说明已通到 Immich)。

如果返回 502 Bad Gateway:通常是「服务 URL」字段写错。常见错误(按出现频率):

  1. 写成 https://——Immich 容器没装证书,必须 http://(placeholder 显示 https 极易误导)
  2. 容器名拼错(应为 immich-server,不是 immich_server / immich
  3. 端口错或漏了 :2283
  4. 漏了协议前缀(直接写 immich-server:2283,必须有 scheme)

修复后回 5.3「服务 URL」字段改为 http://immich-server:2283 重新保存。 如果返回 530:DNS 还没生效,等 1-2 分钟。 如果 curl 直接连不上:DNS 还没传播完,等 1-2 分钟再试。


第六步:配置 Cloudflare Access 邮箱鉴权(必做,安全核心)

Section titled “第六步:配置 Cloudflare Access 邮箱鉴权(必做,安全核心)”

只把 Immich 放到 Tunnel 后面还不够——任何知道 photos.example.com 的人都能看到登录页。Tunnel URL 会被 Cloudflare 公开 cert log 收录,分分钟被扫描器扫到。

Access 给你加一道邮箱白名单:陌生人即使知道域名,也过不了第一关。

Dashboard 左侧栏点「Zero Trust」,第一次进入会看到欢迎页,点右上角「开始使用」按钮:

  1. 选择免费套餐 Free(个人用够用,最多 50 个用户)
  2. 设置 team domain,例如 yourfamily,完整地址变成 yourfamily.cloudflareaccess.com(这是后面用户登录验证页的域名,全家人会看到,起一个能记住的名字
  3. 绑定一张支付方式(Free 套餐不会扣款,仅用于身份验证;可选信用卡或 PayPal)
  4. 完成后会跳到「开始使用 Cloudflare Zero Trust」onboarding 引导向导(4 个大场景选择 + 底部「暂时跳过此步骤」)

点底部「暂时跳过此步骤」直接进入 Zero Trust 完整仪表板

进入:

Cloudflare Zero Trust
→ 访问控制(Access controls)
→ 应用程序(Applications)
→ 添加应用程序(Add an application)

6.4 选择应用程序类型:自托管和私有

Section titled “6.4 选择应用程序类型:自托管和私有”

弹出「添加应用程序」对话框,左侧有 4 个大类

直接点右下角「继续使用 自托管和私有」按钮进入下一步。

页面顶部「目标」section,下面有「公共主机名」一栏,三个字段:

字段填写说明
子域photos留空意味着保护根域 example.com;本期填 photos
域(必需)example.com从下拉菜单选你已托管到 CF 的域名
路径留空整个域名都给 Immich,不分路径保护

6.6(跳过)允许浏览器 RDP/SSH/VNC 访问

Section titled “6.6(跳过)允许浏览器 RDP/SSH/VNC 访问”

继续向下滚动到「Access 策略」section,初始状态显示「未添加策略」。

6.7.1 加第一条策略:家庭成员邮箱白名单(Allow)
Section titled “6.7.1 加第一条策略:家庭成员邮箱白名单(Allow)”

点「添加策略」按钮,按下表填写:

字段填写
策略名称Family members only
操作(Action)Allow(允许)
会话持续时间与应用程序会话超时相同(默认即可,全局在 6.10 配置)
配置规则(Include)电子邮件(Emails)
Value第四步在 Immich 里创建的所有家庭成员邮箱,逐一添加(如 wife@example.comkid@example.comdad@example.com 等)

确认这条策略。

6.7.2 不在本应用加 Bypass 策略(架构变化,移到 6.13 独立应用)
Section titled “6.7.2 不在本应用加 Bypass 策略(架构变化,移到 6.13 独立应用)”

⚠️ 关键架构说明:策略层没有路径字段——路径在**目标(公共主机名)**层面配置。

单个 Application 无法实现”全站需邮箱 + /api/* 放行”。Cloudflare 推荐的架构是建两个独立 Application 共存于同一域名,按”更具体路径优先”匹配:

应用 1(本步骤建应用 2(6.13 步骤再建
名称:Family Photos名称:Family Photos - API Bypass
目标:photos.example.com,路径留空目标:photos.example.com,路径 = api/*
策略:6.7.1 的 Allow + 家庭邮箱策略:Bypass + 所有人
命中:浏览器访问首页/相册 → 走邮箱验证命中:移动 App API 调用 → 直接放行

CF 按”更具体路径优先”匹配——/api/... 落入应用 2,其他路径落入应用 1。

本步骤只加 6.7.1 那一条 Allow 策略,不要在本应用里加 Bypass。Bypass 在 6.13 单独建应用 2 解决。

⚠️ 不要在本应用里加 Bypass 策略:策略层无路径字段,加 Bypass + 所有人会让 CF 对整个 photos.example.com 全部路径都绕过 Access——任何人都能直接进 Immich 登录页,邮箱保护彻底失效。

6.8(可选)用「策略测试器」预先验证

Section titled “6.8(可选)用「策略测试器」预先验证”

页面右上角的「身份 / MFA」切换:保持「身份」即可。

6.10 填「详细信息」section(页面最底部)

Section titled “6.10 填「详细信息」section(页面最底部)”

继续滚动到页面最底部详细信息」section:

字段填写说明
名称(必需)Family Photos应用名,会显示在登录页和 App Launcher 上给家庭成员看
会话持续时间(必需)从默认 24 hours 改为 1 month⚠️ 默认 24 小时对家庭场景太短(每天都要重验证);改为 1 月后家人 30 天内不用反复输验证码

💡 字段提示「策略和全局会话持续时间会覆盖应用程序会话持续时间」——意思是 6.7.1 策略级别和系统全局级别的 Session 设置,会覆盖这里的应用级 Session。我们 6.7.1 用了「与应用程序会话超时相同」= 跟随这里的 1 month。

页面最底部点「保存」(Save)按钮,应用 1 创建完成。回到「应用程序」列表页应能看到 Family Photos 一行,状态正常。

6.13 创建应用 2:Family Photos - API Bypass(移动 App 必需)

Section titled “6.13 创建应用 2:Family Photos - API Bypass(移动 App 必需)”

回到「访问控制 → 应用程序」列表页,再点「添加应用程序」,按下面流程复用 6.4 路径:

与 6.4 相同:选「自托管和私有」→「继续使用 自托管和私有」。

6.13.2 配置目标(关键差异:填路径)
Section titled “6.13.2 配置目标(关键差异:填路径)”

目标 → 公共主机名」section:

字段填写说明
子域photos与应用 1 完全相同
example.com与应用 1 完全相同
路径api/*⚠️ 这是与应用 1 的唯一区别——只匹配 API 路径

⚠️ 路径语法说明

Application Path 字段使用 glob 通配符(*),不是正则表达式

正确写法:

  • api/*(推荐)—— 标准 glob,匹配 /api/ 后所有路径
  • /api/* —— 加斜杠也行

故障验证(用最稳定的 ping 端点,避免 API 重命名问题):

Terminal window
curl -I https://photos.example.com/api/server/ping
# 错误状态:HTTP/2 302,location 跳转到 *.cloudflareaccess.com → Bypass 路径写错(最常见是写成了 regex)
# 正确状态:HTTP/2 200 + 含 "pong" 的 JSON → Bypass 已生效
#
# 提示:如果返回 404,说明 Bypass 已生效但 Immich 版本端点路径不同——
# 换用浏览器访问 https://photos.example.com/ 测邮箱验证流程是否正常即可。
6.13.3 跳过 RDP/SSH/VNC、跳过身份验证调整、加 Bypass 策略
Section titled “6.13.3 跳过 RDP/SSH/VNC、跳过身份验证调整、加 Bypass 策略”
  • RDP/SSH/VNC section:跳过(与 6.6 相同)
  • 「Access 策略」section 点「添加策略」:
字段填写
策略名称API bypass for mobile apps
操作(Action)Bypass(绕过)
配置规则(Include)所有人(Everyone)

💡 Bypass 策略只配身份层规则(包含=所有人/IP/Country/Group),没有路径字段——路径已经在 6.13.2 目标里限定为 api/*,不会泛滥。

「身份验证」section:保持默认即可(Bypass 不走身份验证,无关紧要)。

页面底部「详细信息」section:

字段填写
名称Family Photos - API Bypass
会话持续时间默认即可(Bypass 不依赖 session)

页面最底部「保存」。

回到应用程序列表,应能看到 2 行

Family Photos photos.example.com Allow
Family Photos - API Bypass photos.example.com (api/*) Bypass

外网(不要在自家 Wi-Fi 下——CF 可能因 IP 缓存让本地放行;手机切 4G 或换台电脑)访问 https://photos.example.com预期看到 Cloudflare 的邮箱验证页(域名是 yourfamily.cloudflareaccess.com/...):

Sign in to Family Photos
Enter your email below to receive a one-time code.
[输入邮箱框]
[Send me a code]

输入第四步在 Immich 里创建的家庭成员邮箱 → 收到 6 位验证码 → 输入后跳转到 Immich 登录页 = 应用 1 工作正常

命令行验证两个应用各自生效:

Terminal window
# ① 验证应用 1(Allow):根路径应跳转到 cloudflareaccess.com
curl -I https://photos.example.com
# 期望:HTTP/2 302
# 期望 location 头含 yourfamily.cloudflareaccess.com
# ② 验证应用 2(Bypass):/api 路径应直接打到 Immich,不被 Access 拦截
curl -I https://photos.example.com/api/server/version
# 期望:HTTP/2 200 或 401(Immich 自身处理)
# 不应:302 跳转 cloudflareaccess(说明 Bypass 没生效,App 会登录失败)

故障排查

症状原因修复
根路径直接看到 Immich 登录页(没邮箱验证)应用 1 的 Allow 策略没生效6.5 公共主机名是否与第五步 Tunnel 路由完全一致
/api/server/version 返回 302 跳 cloudflareaccess应用 2 的 Bypass 策略没生效6.13.2 路径必须用 glob api/*(不是 regex ^/api/.*);列表页是否真有 2 行应用
/api/server/version 返回 404Bypass 已生效(请求到了 Immich),但端点路径在你的 Immich 版本里不存在这是好消息!换用 /api/server/ping(v1.113+ 通用)或浏览器访问 https://photos.example.com/ 测邮箱验证
/api/... 返回 200,根路径也返回 200(不跳验证)Bypass 策略覆盖范围太大,把根路径也绕过了6.13.2 路径写成了 * 或留空——改回 api/*
邮箱验证页显示但收不到验证码邮箱不在 Allow 列表 / 进了垃圾箱6.7.1 检查 Value 列表 + 检查垃圾邮件箱 + 把 notify.cloudflare.com 加邮箱白名单
保存报错或登录页 500顶部「Access 服务降级」告警等 1-2 小时,看 CF 状态页 恢复后重试

关于 Immich API 端点变化:v1.113.0 起 /api/server-info/* 全部重命名为 /api/server/*。如果你看到的是 v1.112 或更老版本,验证命令应换回 /api/server-info/version。最稳定的健康检查端点是 /api/server/ping,所有现代版本都支持。


第七步:移动端 App 安装 + 远程登录

Section titled “第七步:移动端 App 安装 + 远程登录”

App Store / Google Play 搜索 Immich,认准开源标识。

家庭每个成员在自己手机上:

  1. 打开 Immich App
  2. Server Endpoint URL 填:
https://photos.example.com/api
  1. App 直接跳转到 Immich 用户名 / 密码登录页
  2. 输入管理员在第四步发给你的账号和临时密码
  3. 首次登录后改密码

触发备份的方式

  1. 前台备份:打开 Immich App,备份进度条自动出现在顶部,App 保持前台运行即持续上传
  2. 后台备份(iOS 系统限制较多):需要额外开启
iPhone 设置
→ 通用
→ 后台 App 刷新
→ Immich = 开启

第九步:设置共享方式(三种选一种或组合用)

Section titled “第九步:设置共享方式(三种选一种或组合用)”

家庭照片共享在 Immich 里有三种姿势,按需选用:

共享方式 A:Shared Album 共享相册(推荐主用)

Section titled “共享方式 A:Shared Album 共享相册(推荐主用)”

最适合”全家相册”场景。

操作(任意账号都能创建):

相册 → 新建相册 → 命名"我家相册 2026"
→ 添加照片
→ Share → 邀请用户 → 选家庭成员
→ 给他们写权限(可上传)

效果:

  • 全家人都能往这个相册加照片
  • 所有人都能看
  • 适合:孩子成长、旅行、聚会、节日

共享方式 B:Partner Sharing 配偶共享(夫妻向)

Section titled “共享方式 B:Partner Sharing 配偶共享(夫妻向)”

适合”我老婆所有照片我都能看,反之亦然”。

Partner Sharing 需通过浏览器 Web 端配置,iOS App 的设置页没有此入口。

操作(浏览器打开 https://photos.example.com):

右上角头像 → 账号设置
→ 好友共享
→ 添加协作者 → 选另一半的账号
→ 保存

配置后,另一半打开 App 会收到”邀请”通知,接受后双方照片互可见。

在 iOS App 查看对方照片:

底部 资源库(Library)→ 伙伴照片(Partner Photos)

效果:

  • 单向只读(双方各自添加对方 = 双向可见)
  • 对方整个时间线在”资源库 → 伙伴照片”里可见
  • 不会混入你自己的主时间线(各自独立)
Section titled “共享方式 C:Public Share Link 公开链接(给爷爷奶奶用)”

适合”老人不想注册账号,扫码就能看孙子最新照片”。

操作:

打开任意相册 / 选一组照片
→ Share → Create Link
→ 设置过期时间(建议 30 天)
→ 设置密码(可选)
→ 复制链接

家里 Wi-Fi 下(建议先在家里测一遍):

浏览器访问 https://photos.example.com

应该:邮箱验证 → 进入 Immich 登录页 → 输入密码 → 看到自己的相册。

外网验证(关掉家里 Wi-Fi,手机切 4G/5G):

重复上面流程,确认在外面也能进。

移动 App 远程上传验证

  1. 出门拍一张照片
  2. 等 1-2 分钟(Wi-Fi 状态下应秒传)
  3. 在浏览器 photos.example.com 看是否出现

如能出现,说明全套链路打通。


阶段效果全家可达成的能力
初始(裸 iPhone + iCloud 50G)iCloud 月费 + 容量焦虑 + 无法共池各拍各的,分享靠微信压缩图
完成第三步(Immich 已部署)局域网内可访问在家电脑能看,外面不能
完成第六步(CF Tunnel + Access)外网域名可访问 + 邮箱白名单任何设备浏览器都能看,陌生人扫不到
完成第七、八步(移动端备份)全家自动备份拍照就传到家里,iOS 受系统限制需偶尔手动触发
完成第九步(共享)共享相册 + Public Link全家共池 + 给爷爷奶奶发链接

实际上传速度 / 后台稳定性 / 大库扫描时间因家庭宽带、服务器配置和 iOS 行为而异,建议搭好后实测一周再决定是否取消 iCloud 付费。强烈建议保留 iCloud 50G 免费档作为系统级兜底,Immich 作为永久镜像和家庭共享中枢。


原因:Cloudflare Tunnel 找不到 Immich 容器。

排查

Terminal window
# 在服务器上测本地是否通
curl -I http://localhost:2283
# 看 cloudflared 日志
docker compose logs cloudflared | tail -50

常见错误:

错误修复
「服务 URL」写成 https://immich-server:2283(placeholder 是 https 极易误导)改成 http://immich-server:2283
漏了协议前缀,写成 immich-server:2283必须含 scheme:http://immich-server:2283
容器名写错必须和 docker-compose.yml 里 service 名一致:immich-server(不是 immich_serverimmich
cloudflared 和 immich-server 不在同一 compose 项目用局域网 IP:http://192.168.1.50:2283

原因

  • 邮箱不在 Access Policy 的 Include 列表
  • 邮件被识别为垃圾邮件
  • 企业邮箱拦截 noreply@notify.cloudflare.com

解决

  • 检查 Cloudflare Zero Trust → Access → Applications → 你的 App → Policies → Include Emails 列表
  • 检查垃圾邮件箱
  • notify.cloudflare.com 加入邮箱白名单

原因:通常是 Server Endpoint URL 填错。

正确格式

https://photos.example.com/api

注意

  • 必须是 https://,不是 http://
  • 末尾必须是 /api,不是裸域名
  • 不能有结尾斜杠

如果还是不行:

Terminal window
# 检查 Bypass 策略是否生效(用 ping 端点最稳定,所有 Immich 版本都支持)
curl -I https://photos.example.com/api/server/ping
# ✅ 200 + JSON → Bypass 工作 + Immich 响应正常
# ✅ 404 → Bypass 工作(请求到了 Immich),只是该版本端点不同;浏览器测试即可
# ❌ 302 → cloudflareaccess.com → Bypass 没生效,回 6.13 检查路径字段是 glob `api/*` 不是 regex

如果返回 302 跳转到 *.cloudflareaccess.com,说明 Bypass 策略没生效,回去第 6.13 步检查独立的 Family Photos - API Bypass 应用是否真的存在,且其目标路径为 glob api/*必须有 2 个独立应用才行——单一应用加 Bypass 策略带路径的方式不可用。

可能原因和优化

原因优化
家庭宽带上行小(电信常见 30Mbps)给 Immich 配独立时段(午夜上传)
服务器 CPU 卡在缩略图生成暂停后台 ML 任务 → 上传完再开
同时多人上传抢带宽错峰上传
Cloudflare 中转远一般家庭可以忽略,移动端 App 直传速度通常稳定

原因

  • Mac 自动睡眠 / Windows 进入待机
  • 笔记本合盖默认睡眠

解决(按系统)

系统命令 / 设置
macOScaffeinate -dimsu 或装 Amphetamine(免费)
Linux 桌面系统设置 → 电源 → 永不待机
Windows控制面板 → 电源选项 → “高性能”
笔记本合盖macOS:sudo pmset -c disablesleep 1;Windows:电源选项 → 合盖时不操作

最稳的方案是专门买一台便宜 N100 Mini PC 当家庭服务器(人民币 800-1500),24 小时通电不睡眠,全年电费几十块。

参见第九步。最常用的是:

  • 全家共享相册 = Shared Album(多人写)
  • 夫妻互看全部 = Partner Sharing(单向,双方各设置一次=互看)
  • 给老人/朋友分享 = Public Share Link(无需账号)

不建议把 Immich 当电影库用。Immich 是相册(按日期组织、EXIF 元数据),不是影音媒体中心。


⚠️ 免责声明:本教程仅供技术学习与家庭自托管交流,请遵守您所在国家/地区的相关法律法规。Immich 不应作为唯一备份方案——官方明确建议配合 iCloud / 外挂硬盘 / 第三方云任一种作双备份,避免版本升级或硬件故障导致数据丢失。