Quadlet 实战:Immich
官方 docker-compose 文件已经改动,建议仅参考流程
如果你还不熟悉 Quadlet 的基本概念,建议先阅读 Podman Quadlet 入门。
本文以部署 Immich 这个自托管照片和视频备份方案为例,演示如何使用 podlet 将 docker-compose.yml 转换为 Quadlet 配置,并最终交给 Systemd 托管。
1. 获取并调整 docker-compose.yml
先从 Immich 的 GitHub Release 页面获取最新的 docker-compose.yml:
wget https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml转换前需要先做几处适配:
- 移除
container_name:Quadlet 会根据单元文件名自动命名容器。 - 替换环境变量引用:将
${IMMICH_VERSION}等变量替换为具体值。 - 调整服务间通信:同一 Pod 内的容器通过
127.0.0.1通信,因此DB_HOSTNAME和REDIS_HOSTNAME需要设为127.0.0.1。
修改后的 docker-compose.yml 示例:
name: immich
services:
server:
image: ghcr.io/immich-app/immich-server:release
volumes:
- ./server/upload:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
ports:
- "2283:2283"
depends_on:
- redis
- database
environment:
DB_HOSTNAME: 127.0.0.1
REDIS_HOSTNAME: 127.0.0.1
restart: always
healthcheck:
disable: false
machine-learning:
image: ghcr.io/immich-app/immich-machine-learning:release
volumes:
- model-cache:/cache
restart: always
healthcheck:
disable: false
redis:
image: docker.io/valkey/valkey:9
healthcheck:
test: redis-cli ping || exit 1
restart: always
database:
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: immich
POSTGRES_INITDB_ARGS: "--data-checksums"
volumes:
- ./database:/var/lib/postgresql/data
restart: always
volumes:
model-cache:2. 使用 podlet 生成 Quadlet 文件
podlet 是一个用 Rust 编写的 CLI 工具,由维护 Podman 的 containers 组织维护。它可以从 docker-compose.yml、docker run 命令或现有的 Podman 对象生成 Quadlet 文件,是 Compose 迁移里非常省事的工具。
用 podlet 自动转换时,下面几个参数比较常用:
-i:在生成的单元文件中增加[Install]部分。-a:将相对路径转换为绝对路径,因为 Systemd 单元文件不识别相对路径。-f:直接写入对应的单元文件,而不是输出到标准输出。
podlet -i -a -f compose --pod为便于阅读,下面把生成的多个文件内容合并展示:
# immich-server.container
[Unit]
Requires=immich-redis.service immich-database.service
After=immich-redis.service immich-database.service
[Container]
Environment=DB_HOSTNAME=127.0.0.1 REDIS_HOSTNAME=127.0.0.1
Image=ghcr.io/immich-app/immich-server:release
Pod=immich.pod
Volume=/path/to/server/upload:/usr/src/app/upload
Volume=/etc/localtime:/etc/localtime:ro
[Service]
Restart=always
[Install]
WantedBy=default.target
---
# immich-machine-learning.container
[Container]
Image=ghcr.io/immich-app/immich-machine-learning:release
Pod=immich.pod
Volume=model-cache:/cache
[Service]
Restart=always
[Install]
WantedBy=default.target
---
# immich-redis.container
[Container]
HealthCmd=redis-cli ping || exit 1
Image=docker.io/valkey/valkey:9
Pod=immich.pod
[Service]
Restart=always
[Install]
WantedBy=default.target
---
# immich-database.container
[Container]
Environment=POSTGRES_PASSWORD=postgres POSTGRES_USER=postgres POSTGRES_DB=immich POSTGRES_INITDB_ARGS=--data-checksums
Image=ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
Pod=immich.pod
Volume=/path/to/database:/var/lib/postgresql/data
[Service]
Restart=always
[Install]
WantedBy=default.target
---
# immich.pod
[Pod]
PublishPort=2283:2283
[Install]
WantedBy=default.target目前 podlet 还有一个小 bug:不会自动为命名卷生成对应的 Quadlet 配置文件,这里需要手动补一个:
# immich-model-cache.volume
[Volume]然后再把 immich-machine-learning.container 文件中的 model-cache:/cache 改成 immich-model-cache.volume:/cache。
3. 部署和管理服务
-
保存文件:将上述文件放到
~/.config/containers/systemd/。 -
重载 Systemd:
systemctl --user daemon-reload -
创建目录:绑定挂载用到的目录需要提前手动创建,否则容器启动会失败。
-
启动 Pod:Systemd 会根据依赖自动启动所有容器。
systemctl --user start immich-pod -
检查状态:
systemctl --user status immich-*
至此,Immich 就已经通过 Quadlet + Systemd 运行起来了。之后你可以用 systemctl --user start/stop/restart 分别控制单个容器。
不过在实际部署时,通常还会遇到一些细节问题,下面继续处理。
4. 解决常见问题
I. 数据库容器挂载目录所有权问题
现象:启动 database 后,挂载目录下的文件所有者会显示为一个异常的高位 UID(例如 100998),宿主机用户无法直接访问这些文件。
原因:Rootless Podman 默认使用用户命名空间映射,容器内的 UID 会映射到宿主机的 subuid 范围。Postgres 容器默认以 UID 999 运行,所以在宿主机上看到的就会变成一个高位 UID,文件所有权也不再对应当前用户。
解决:在 immich.pod 的 [Pod] 部分添加 UserNS=keep-id:uid=999。这样可以让宿主机当前用户在容器内以 UID 999 的身份运行,从而让数据库写出的文件在宿主机上仍然归当前用户所有。
# immich.pod
[Pod]
PublishPort=2283:2283
UserNS=keep-id:uid=999II. podlet 转换失败
podlet 转换失败,通常是因为 docker-compose.yml 中包含了它暂不支持的配置项。根据 podlet 的报错提示删除不支持的字段、调整格式后,再重新执行转换即可。