跳转到内容

Podman Quadlet 入门

适用于 AlmaLinux 9.4+ & 10 / Rocky Linux 9.4+ & 10 / CentOS Stream 9 & 10(Podman 4.4+)

要让容器在开机时自动启动、崩溃后自动重启,过去你得用 podman generate systemd 生成一份服务单元。这个方式已经被官方弃用。从 Podman 4.4 起,Podman 内置了 Quadlet:你只写一份声明式的 .container 文件,systemd 在启动时自动把它转换成服务单元来管理。配置更清晰,改起来也更简单。

  • Quadlet 是什么,以及它为什么取代 podman generate systemd
  • 把单元文件放在哪里(root 与 rootless 的区别)
  • 写一个 nginx.container 文件并用 systemd 启动它
  • 配置 rootless 自动启动与自动更新
  • 其它单元类型(.volume.network.pod 等)的用途
  • 一台运行 EL 9.4+ 或 EL 10 的系统
  • 已安装 Podman 4.4 及以上(EL 9.4+/EL 10 默认自带)
  • 普通用户的 sudo 权限

Quadlet 是 Podman 4.4 起内置的一个 systemd 生成器。你把一份描述容器的 .container 文件交给 systemd,systemd 在 daemon-reload 时调用 Quadlet,自动生成对应的 .service 单元。

它的优势在于声明式:你只描述”想要什么”,而不是像 podman generate systemd 那样生成一份”当前状态的快照”。容器参数变化后,旧方式需要重新生成单元文件,而 Quadlet 只需编辑文件再 daemon-reload

Quadlet 会扫描固定的目录。放对位置是它能工作的前提:

场景目录
系统级(root)/etc/containers/systemd/(或发行版自带的 /usr/share/containers/systemd/
用户级(rootless)~/.config/containers/systemd/

下面用一个 Nginx 容器演示完整流程。先创建单元文件:

nginx.container
[Unit]
Description=Nginx web server
[Container]
Image=docker.io/library/nginx:latest
PublishPort=8080:80
Volume=/srv/nginx/html:/usr/share/nginx/html:Z
Environment=TZ=Asia/Shanghai
AutoUpdate=registry
[Service]
Restart=always
[Install]
WantedBy=default.target

几个关键点:

  • Image= 写镜像全路径docker.io/library/...),避免 short-name 触发交互式仓库选择。
  • PublishPort= 等同于 podman run -p,这里把宿主 8080 映射到容器 80。
  • Volume=:Z 后缀在 SELinux 系统上自动打上正确标签(私有标签),等同于 podman run -v ...:Z
  • AutoUpdate=registry 启用镜像自动更新(见下文)。
  • 服务名 = 文件名去掉扩展名:nginx.containernginx.service

接着按 root 或 rootless 分别启动。

把文件放进 /etc/containers/systemd/,然后:

  1. 重新加载,让 systemd 生成对应的 nginx.service

    重新加载单元
    $ sudo systemctl daemon-reload
  2. 启动服务(注意服务名是 nginx,不是 nginx.container):

    启动并查看状态
    $ sudo systemctl start nginx
    $ sudo systemctl status nginx

[Container] 里写 AutoUpdate=registry 后,再启用对应的定时器,Podman 会定期检查上游镜像是否更新并自动滚动:

启用自动更新定时器
$ sudo systemctl enable --now podman-auto-update.timer
手动触发一次(可选)
$ podman auto-update

Quadlet 不止 .container,常见单元类型还有:

文件后缀用途
.container单个容器
.volume命名卷,可被 .containerVolume= 引用
.network自定义网络,可被 Network= 引用
.pod一组共享网络命名空间的容器(Pod)
.kube直接运行 Kubernetes YAML(play kube
.image预先拉取镜像,供其它单元依赖

例如先声明一个卷,再在容器里引用它:

app-data.volume
[Volume]
# 生成名为 systemd-app-data 的卷
app.container(片段)
[Container]
Image=docker.io/library/postgres:16
Volume=app-data.volume:/var/lib/postgresql/data:Z

如果你还在用 podman generate systemd 生成的 container-*.service,迁移思路很直接:把 podman run 的参数逐项翻译成 [Container] 段的字段。

podman run 参数Quadlet 字段
--name(可选)ContainerName=
-p 8080:80PublishPort=8080:80
-v src:dst:ZVolume=src:dst:Z
-e KEY=valEnvironment=KEY=val
--restart=always[Service] 段的 Restart=always

迁移后停用并删除旧的 container-*.service,避免端口/名称冲突。

改了 .container 文件却没生效? Quadlet 只在 daemon-reload 时重新生成单元。每次编辑后都要 systemctl daemon-reload(rootless 加 --user),再 restart 服务。

systemctl start nginx.container 报找不到单元? 服务名是文件名去掉扩展名nginx.container 对应 nginx.service,所以应执行 systemctl start nginx,而不是带 .container

rootless 容器一退出登录就停了? 没有启用 linger。执行 sudo loginctl enable-linger $(whoami),用户服务才会在你不在线时继续运行。

启动时卡住,提示选择镜像仓库? Image= 写了 short-name(如 nginx)触发了交互式选择。在单元文件里写镜像全路径 docker.io/library/nginx:latest

卷挂载后容器读不到文件(权限拒绝)? SELinux 拦截了。给 Volume=:Z 后缀,例如 Volume=/srv/data:/data:Z,让 Podman 自动打上正确的 SELinux 标签。