编程 LibrePhotos 部署:unified 单容器与 compose 拆分的取舍,以及 PG18 卷路径、CSRF 两个坑

2026-10-15 00:04:45

LibrePhotos 部署:unified 单容器与 compose 拆分的取舍,以及 PG18 卷路径、CSRF 两个坑

LibrePhotos 是自托管的开源照片管理服务,带自动人脸识别、物体检测和语义搜索。

  • 项目仓库:
  • 文档:
  • 单容器部署文档:
  • 标准安装文档:

仓库是 monorepo,合并了原先 5 个仓库:

  • apps/backend/:Django 5 API、机器学习 pipeline、后台任务
  • apps/frontend/:React 19 + Vite
  • apps/mobile/:Expo 移动端
  • apps/docs/:Docusaurus
  • deploy/:Dockerfile、Compose、proxy、K8s manifest

README 列出的安装方式:Single container(官方推荐)/ Docker Compose / Kubernetes(deploy/k8s)/ Unraid / Windows standalone。

系统要求:RAM 最小 4GB、推荐 8GB+;存储最小 10GB 另加照片库;CPU 2 核起、推荐 4 核+;OS 为任意支持 Docker 的系统或 Windows。人脸识别、场景分类、图像描述这些 ML 任务吃内存,8GB+ 是强烈推荐。

两种方式怎么选

unified 单容器是 README 的推荐路径:API 和前端跑在同一个容器里,数据库可以内置 SQLite,也可以接外部 PostgreSQL。文档里的 Option 2(外部 PG)标注的适用场景是云部署、已有 PG、生产。

标准 Compose 走 deploy/compose,多容器拆分,需要 x86 或 ARM64,建议 4GB RAM(ARM64 算力较弱会慢),至少 10GB 磁盘用于镜像、缩略图、数据库和 ML 模型。它的 docker-compose.yml 是仓库跟踪文件,会随版本新增设置,因此升级动作里 git pull 不能省。

不管走哪条路,有两个坑高频:Postgres 18 的卷路径,以及反代后面的 CSRF_TRUSTED_ORIGINS。

单容器部署(unified)

单容器同时提供 API 与前端,可选内置 SQLite 或外部 PostgreSQL。

Option 1 全合一(SQLite)

mkdir -p /home/yourusername/librephotos/{db,protected_media,logs}
sudo docker run -d \
--name librephotos \
--restart unless-stopped \
-p 3000:8001 \
-v /home/yourusername/librephotos/db:/db \
-v /home/yourusername/librephotos/protected_media:/protected_media \
-v /home/yourusername/librephotos/logs:/logs \
-v /path/to/your/photos:/data \
-e SERVE_FRONTEND=true \
-e DB_BACKEND=sqlite \
reallibrephotos/librephotos-unified:latest

访问 http://localhost:3000。

说明:

  • Secret key:不提供则自动生成,保存到 /protected_media/secret.key(1.3.0 及更早为 /logs/secret.key,新版本首次启动会迁移)。不要删。自带用 -e SECRET_KEY=...。
  • 数据库:SQLite 自动建在 /db。更新前会复制到 /db/backups,保留最新三份。
  • 照片目录:可只读挂载 -v /path/to/photos:/data:ro,但会禁用上传、删除照片、回写元数据(上传默认写到 /data 内的 uploads/)。上传默认开启,用 :ro 时同时传 -e ALLOW_UPLOAD=false。

Option 2 外部 PostgreSQL

已有 PostgreSQL 需创建数据库与属主用户。首次启动容器以该用户跑 manage.py migrate,需要能建表——PostgreSQL 15 起 GRANT ALL PRIVILEGES ON DATABASE 不再包含建表权限,应把 app 用户设为库 owner:

CREATE USER librephotos_user WITH PASSWORD 'your_secure_password';
CREATE DATABASE librephotos OWNER librephotos_user;

已存在库执行 ALTER DATABASE librephotos OWNER TO librephotos_user;。从 PG14 及更早迁移过来的库,还要 \c librephotos 后执行 GRANT ALL ON SCHEMA public TO librephotos_user;。

新建 PG 容器:先建共享网络,再起 PostgreSQL(用 POSTGRES_USER/POSTGRES_DB 建库时该用户即 owner):

sudo docker network create librephotos-net
sudo docker run -d --name librephotos-db --network librephotos-net --restart unless-stopped \
-e POSTGRES_DB=librephotos -e POSTGRES_USER=librephotos_user -e POSTGRES_PASSWORD=your_secure_password \
-v /home/yourusername/postgres_data:/var/lib/postgresql \
postgres:18

PostgreSQL 18 是 Compose 使用的版本。从 18 起镜像把数据放在 /var/lib/postgresql 而不是 /var/lib/postgresql/data。若此前用 postgres:17 按 /var/lib/postgresql/data 挂载启动过,要用旧 tag 与旧挂载重建:官方镜像不会升级已有数据目录,直接用 postgres:18 指向它会得到空库。

LibrePhotos 容器:

sudo docker run -d --name librephotos --network librephotos-net --restart unless-stopped \
-p 3000:8001 \
-v /home/yourusername/librephotos/protected_media:/protected_media \
-v /home/yourusername/librephotos/logs:/logs \
-v /path/to/your/photos:/data \
-e SERVE_FRONTEND=true -e DB_BACKEND=postgresql \
-e DB_NAME=librephotos -e DB_USER=librephotos_user -e DB_PASS=your_secure_password \
-e DB_HOST=librephotos-db -e DB_PORT=5432 \
reallibrephotos/librephotos-unified:latest

DB_HOST:兄弟容器用容器名 librephotos-db;DB_HOST=localhost 不行,容器内指向自己。用已有 PG 服务器则去掉 --network librephotos-net 并设 DB_HOST=;若 PG 在 Docker 宿主机上,加 --add-host=host.docker.internal:host-gateway 且设 DB_HOST=host.docker.internal,并让 PG 监听所有接口、pg_hba.conf 匹配。

放在 HTTPS 反代后面

Traefik、Caddy 或云 LB 之后:

  • 加 -e CSRF_TRUSTED_ORIGINS=https://photos.example.com(多来源逗号分隔,必须带 scheme 且与浏览器实际地址完全一致)。unified 镜像默认可信来源为空,不加则 /api/django-admin/ 登录会被 CSRF 拒绝;照片应用本身不受影响(用 JWT,接口豁免 CSRF)。
  • 加 -e NUM_PROXIES=1,让登录限流按代理透传的客户端地址计数,而不是把所有人当成代理。

标准 Docker Compose 安装

git clone https://github.com/LibrePhotos/librephotos.git
cd librephotos/deploy/compose
cp librephotos.env .env   # 必须重命名为 .env
nano .env                 # 改照片目录等必需项
docker compose up -d

几分钟后访问 localhost:3000。Postgres v18+ 的卷要挂到 /var/lib/postgresql 而非 /var/lib/postgresql/data。

更新:

git pull            # 同时更新 docker-compose.yml
docker compose down
docker compose pull # 只刷镜像
docker compose up -d

注意:

  • docker compose pull 只拉新镜像;docker-compose.yml 是仓库跟踪文件,会随版本新增设置(近期加了把 workerConcurrency、frontendBaseUrl 从 .env 透传给 backend 的行,并改了 Postgres v18+ 的卷路径)。若你的 docker-compose.yml 早于这些改动,光在 .env 里设对应变量无效,必须先 git pull。
  • .env 不被 git 跟踪,git pull 不动你的设置;更新后和 librephotos.env 对比补上新变量。
  • 自己改过 docker-compose.yml(如用 GPU 镜像、设资源限制):git pull 可能拒绝或冲突,先 git stash,pull 后再 git stash pop,再在更新后的文件上重贴改动。
  • 从 2025w44 或更早来:1.1.0 之后的版本无法直接升级 2026w10 之前的数据库,先过 1.1.0。
  • 从 1.2.1 或更早来:更新后跑一次缩略图清理(去掉旧缩略图里的 GPS)。

首次启动有向导:建管理员账号、站点设置(上传、注册)、照片扫描目录。

技术栈

  • Backend:Django 5 + DRF、PostgreSQL、Django-Q2、LibRaw/rawpy、FFmpeg、ExifTool
  • ML:InsightFace(人脸检测)、scikit-learn + hdbscan(人脸聚类)、LFM2.5-VL 450M ONNX(图像描述)、OpenCLIP ViT-B/32 DataComp-XL MIT(打标/语义搜索/相似照片)、FAISS;ML runtime 全部用 ONNX Runtime,无 PyTorch;geopy 反地理编码
  • 前端:React 19 + TypeScript、Vite、Mantine、TanStack Router/Query、MapLibre GL、i18next
  • 部署:Docker & Docker Compose、反代 Nginx

API 文档:启动后 http://localhost:3000/api/swagger 与 /api/redoc。

推荐文章

程序员茄子在线接单