Problem: I attempted to create a systemd unit for a podman container to autostart at boot, but encountered the following info message and decided to investigate:
user@host $ podman generate systemd --new --name syncthing
DEPRECATED command:
It is recommended to use Quadlets for running containers and pods under systemd.
Solution: Podman Quadlets, a declarative way to create systemd units for containers.
Understanding Podman Quadlets
Podman Quadlets use six types of files: .container
, .pod
, .kube
, .network
, .image
, and .volume
to create the respective units.
Using volume
, image
, and network
units allows containers to depend on them being automatically pre-created. This is particularly beneficial when using special options to control their creation, as Podman otherwise creates them with default options.
Podman recursively searches in ~/.config/containers/systemd/
for these files.
For more information, refer to the Podman Systemd Unit Documentation.
Example: Syncthing Container
Here’s an example of a .container
file for a Syncthing container:
~/.config/containers/systemd/syncthing.container
---
[Unit]
Description=Syncthing
## Stops the unit if the service starts more than 3 times every 400 seconds.
StartLimitIntervalSec=400
StartLimitBurst=3
[Container]
ContainerName=syncthing
Image=docker.io/syncthing/syncthing:latest
# PodmanArgs=--memory 128m
## TCP file transfers
PublishPort=10.0.0.2:22000:22000/tcp
## QUIC file transfers
PublishPort=10.0.0.2:22000:22000/udp
## Web UI
PublishPort=10.0.0.2:8087:8384
## Receive local discovery broadcasts
PublishPort=10.0.0.2:21027:21027/udp
Volume=/path/in/host/for/syncthing:/var/syncthing
Timezone=Europe/Madrid
UIDMap=0:1:65536
[Service]
Restart=on-failure
MemoryMax=128M
## Allow enough time for the image to be downloaded
TimeoutStartSec=900
## Wait 90 seconds to restart again
RestartSec=90
[Install]
WantedBy=multi-user.target default.target
To apply the changes, reload the systemd daemon and start the service:
systemctl --user daemon-reload
systemctl --user start syncthing.service
This configuration ensures the service starts at boot. If you don’t want a unit to start at boot, remove the [Install]
section of the .container
file.
Note: Pods will be implemented in Podman 5.0 and not in 4.9, so I will update my pods in the future.
Podlet
Podlet is a utility that allows the creation of the necessary quadlets files from a Podman command. It can be run in a container. I tried it and is not fully functional, but covers the basics.
Here’s an example of how to run Podlet in a container:
podman run -it quay.io/k9withabone/podlet --install --description Caddy \
podman run \
--restart always \
-p 8000:80 \
-p 8443:443 \
-v ./Caddyfile:/etc/caddy/Caddyfile:Z \
-v caddy_data:/data \
-m 1G \
docker.io/library/caddy:latest
For more information, visit the Podlet GitHub Repository.
Ansible
Now, let’s demonstrate how to migrate from Ansible’s built-in container plugin to using Quadlets.
Given that I have a variable to set up the path for Syncthing data, I need to create a template.
Here is my current deployment of the container in Ansible:
./tasks/containers/syncthing.yml
---
- name: Create syncthing container
containers.podman.podman_container:
name: syncthing
image: docker.io/syncthing/syncthing:latest
state: present
recreate: yes
restart_policy: on-failure:10
volume:
- "{{ host_podman_syncthing_path }}:/var/syncthing"
memory: 128m
publish:
- "10.0.0.2:22000:22000/tcp" # TCP file transfers
- "10.0.0.2:22000:22000/udp" # QUIC file transfers
- "10.0.0.2:8087:8384" # Web UI
- "10.0.0.2:21027:21027/udp" # Receive local discovery broadcasts
env:
TZ=Europe/Madrid
uidmap: "0:1:65536"
Here is the new Ansible template to create the .container
Quadlet with the variable:
./templates/containers/syncthing/syncthing.container.j2
---
[Unit]
Description=Syncthing
## Stops the unit if the service starts more than 3 times in 400 seconds.
StartLimitIntervalSec=400
StartLimitBurst=3
[Container]
ContainerName=syncthing
Image=docker.io/syncthing/syncthing:latest
# PodmanArgs=--memory 128m
## TCP file transfers
PublishPort=10.0.0.2:22000:22000/tcp
## QUIC file transfers
PublishPort=10.0.0.2:22000:22000/udp
## Web UI
PublishPort=10.0.0.2:8087:8384
## Receive local discovery broadcasts
PublishPort=10.0.0.2:21027:21027/udp
Volume={{ host_podman_syncthing_path }}:/var/syncthing
Timezone=Europe/Madrid
UIDMap=0:1:65536
## Auto update to the latest image
AutoUpdate=registry
[Service]
Restart=on-failure
MemoryMax=128M
## Allow enough time for the image to be downloaded
TimeoutStartSec=900
## Wait 90 seconds to restart again
RestartSec=90
[Install]
WantedBy=multi-user.target default.target
And deploy the template and reload systemd:
./tasks/containers/syncthing.yml
---
- name: Create a systemd unit for syncthing container
ansible.builtin.template:
src: "./templates/containers/syncthing/syncthing.container.j2"
dest: '{{ host_podman_systemd_files_path }}/syncthing/syncthing.container'
owner: '1000'
group: '1000'
mode: '0600'
force: yes ## Overwrites the existing file
register: container_unit
- name: Reload systemd daemon as user
when: container_unit.changed
ansible.builtin.systemd:
daemon_reload: yes
scope: user