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

转换前需要先做几处适配:

  1. 移除 container_name:Quadlet 会根据单元文件名自动命名容器。
  2. 替换环境变量引用:将 ${IMMICH_VERSION} 等变量替换为具体值。
  3. 调整服务间通信:同一 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. 部署和管理服务

  1. 保存文件:将上述文件放到 ~/.config/containers/systemd/。

  2. 重载 Systemd:

    systemctl --user daemon-reload
  3. 创建目录:绑定挂载用到的目录需要提前手动创建,否则容器启动会失败。

  4. 启动 Pod:Systemd 会根据依赖自动启动所有容器。

    systemctl --user start immich-pod
  5. 检查状态:

    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=999

II. podlet 转换失败

podlet 转换失败,通常是因为 docker-compose.yml 中包含了它暂不支持的配置项。根据 podlet 的报错提示删除不支持的字段、调整格式后,再重新执行转换即可。

编辑此页

目录