Podman - systemd container management

Podman is a daemonless container management engine. But how do you start containers on boot and manage them properly, if there is no daemon? The simple answer is: "systemd". Podman integrates very well with systemd.

Podman - systemd container management

Podman is a daemonless container management engine. But how do you start containers on boot and manage them properly, if there is no daemon? The simple answer is: "systemd". Podman integrates very well with systemd.

Be aware, that you can also use systemd inside a container. If you look for a guide to this, please have a look here. In case you want to Get Started, there is also an article for this topic.

What is systemd?

Systemd is a suite of basic building blocks for a Linux system. Very often, this is mistaken as "the init system", which is not entirely true. With systemd, you are getting a complete ecosystem of different tools to manage the system, services, cgroups, logging and different utilities to control basic system configurations. You can even run containers in systemd via systemd-nspawn/system-machines. But, that is another story.

You can find additional explanations and documentation on the freedesktop.org website.

Manage Podman containers with system

Podman does not have a central daemon, that starts and stops containers. So, how do you run containers on boot? For this, Podman can generate systemd unit files, which can be used via systemd to control containers.

If done right, Podman containers can be controlled like any other Linux service, including logging and starting on boot. Let's see how this works and how we can make good use of it.

Hint
The guide is tested on Fedora 33 with Podman 3.0.1.

Start/Stop/Restart

The easiest trick is, to keep an existing container running on reboot. The container starting and stopping will be handled via systemd, but the creation is still in the hand of Podman. Systemd will also take care, if a container crashed and restart it automatically.

For this scenario, we will need a container, first. Be aware, that I will stick to rootfull containers for now.

# Start a container
$ sudo podman container run -d -p 8080:80 --name web01 docker.io/library/httpd

# Check running containers
$ sudo podman container ls
CONTAINER ID  IMAGE                    COMMAND           CREATED        STATUS            PORTS                 NAMES
4cc483ca6dd8  docker.io/library/httpd  httpd-foreground  5 seconds ago  Up 6 seconds ago  0.0.0.0:8080->80/tcp  web01

Now, we need to generate a systemd unit file via Podman. I am using the arguments --files and --name to generate the file directly and use the container name.

# Generate a systemd unit file from web01
$ sudo podman generate systemd web01 --files --name
/var/home/dschier/container-web01.service

# Check the file content
$ cat /var/home/dschier/container-web01.service
# container-web01.service
# autogenerated by Podman 3.0.1
# Tue Mar 30 10:03:28 CEST 2021

[Unit]
Description=Podman container-web01.service
Documentation=man:podman-generate-systemd(1)
Wants=network.target
After=network-online.target

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStart=/usr/bin/podman start web01
ExecStop=/usr/bin/podman stop -t 10 web01
ExecStopPost=/usr/bin/podman stop -t 10 web01
PIDFile=/var/run/containers/storage/overlay-containers/4cc483ca6dd8f44f369c596d3172f3687e8edca844b5950b32fe3bc06d5f94a3/userdata/conmon.pid
Type=forking

[Install]
WantedBy=multi-user.target default.target

The most interesting parts of this unit file are ExecStart to start the container web01 and ExecStop to stop the container web01. There are also statements like After and Wants, which will allow to start the container only, if the network services are started. To get a full explanation of unit files, please check the official docs.

The last steps will allow us to use systemd to starting, stopping and enabling the container on boot. Since we created a unit file container-web01.service, this will be the name used from systemd. Let's do this.

# Stop the container
$ sudo podman container stop web01

# Copy the unit file to the proper location
$ sudo cp /var/home/dschier/container-web01.service /etc/systemd/system/

# Reload systemd
$ sudo systemctl daemon-reload

# Check the status
$ sudo systemctl status container-web01
○ container-web01.service - Podman container-web01.service
     Loaded: loaded (/etc/systemd/system/container-web01.service; disabled; vendor preset: disabled)
     Active: inactive (dead)
       Docs: man:podman-generate-systemd(1)

# Start the container
$ sudo systemctl start container-web01

# Enable the container on boot
$ sudo systemctl enable container-web01

# Check the status again
$ sudo systemctl status container-web01
● container-web01.service - Podman container-web01.service
     Loaded: loaded (/etc/systemd/system/container-web01.service; enabled; vendor preset: disabled)
     Active: active (running) since Tue 2021-03-30 10:20:49 CEST; 51s ago
       Docs: man:podman-generate-systemd(1)

# Also check via Podman
$ sudo podman container ls
CONTAINER ID  IMAGE                    COMMAND           CREATED         STATUS                 PORTS                 NAMES
4cc483ca6dd8  docker.io/library/httpd  httpd-foreground  21 minutes ago  Up About a minute ago  0.0.0.0:8080->80/tcp  web01

That's it already. You can now start to play around with your containers. I recommend not using the podman commands any longer to manage the container, since it can interfere with the status detection of systemd.

If you want to remove the service again, you need to remove the file and reload systemd.

# Stop and disable the container
$ sudo systemctl disable --now container-web01

# Remove the unit file
$ sudo rm /etc/systemd/system/container-web01.service

# Reload systemd
$ sudo systemctl daemon-reload

Recreate

Starting, Stopping and Restarting a container on boot is not a lot of value. But, we can also recreate containers. This can be very handy for various scenarios.

  • Ensure containers are present on reboot
  • Ensure a valid state after restarting a container
  • Download the image, if not present
  • Mitigate human errors
  • Deploy containers to new machines
  • Auto-update container images (will be done in another article)

We will start again with a running container (rootfull) and create a systemd unit file.

# Start a container
$ sudo podman container run -d -p 8080:80 --name web01 docker.io/library/httpd

# Check the status
$ sudo podman container ls
CONTAINER ID  IMAGE                    COMMAND           CREATED         STATUS             PORTS                 NAMES
983825ad8a9f  docker.io/library/httpd  httpd-foreground  16 seconds ago  Up 16 seconds ago  0.0.0.0:8080->80/tcp  web01

# Generate a systemd unit file
$ sudo podman generate systemd web01 --new --name --files
/var/home/dschier/container-web01.service

The systemd unit file will have the below content.

# container-web01.service
# autogenerated by Podman 3.0.1
# Tue Mar 30 10:52:13 CEST 2021

[Unit]
Description=Podman container-web01.service
Documentation=man:podman-generate-systemd(1)
Wants=network.target
After=network-online.target

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStartPre=/bin/rm -f %t/container-web01.pid %t/container-web01.ctr-id
ExecStart=/usr/bin/podman container run \
    --conmon-pidfile %t/container-web01.pid \
    --cidfile %t/container-web01.ctr-id \
    --cgroups=no-conmon \
    --replace \
    -d \
    -p 8080:80 \
    --name web01 \
    docker.io/library/httpd
ExecStop=/usr/bin/podman container stop --ignore --cidfile %t/container-web01.ctr-id -t 10
ExecStopPost=/usr/bin/podman container rm --ignore -f --cidfile %t/container-web01.ctr-id
PIDFile=%t/container-web01.pid
Type=forking

[Install]
WantedBy=multi-user.target default.target
container-web01.service

I have adjusted the file slightly, to make it more readable. As you can see, there are a couple of new statements and other statements have changed. The container will be created via ExecStart now and removed via ExecStop. Starting the container will also replace any existing container.

Let's see how this works.

# Remove the running container
$ sudo podman rm -f web01

# Check podman containers
$ sudo podman container ls -a
CONTAINER ID  IMAGE   COMMAND  CREATED  STATUS  PORTS   NAMES

# Copy the unit file to the proper location
$ sudo cp /var/home/dschier/container-web01.service /etc/systemd/system/

# Reload systemd
$ sudo systemctl daemon-reload

# Enable and start the systemd managed container
$ sudo systemctl enable --now container-web01
Created symlink /etc/systemd/system/multi-user.target.wants/container-web01.service → /etc/systemd/system/container-web01.service.
Created symlink /etc/systemd/system/default.target.wants/container-web01.service → /etc/systemd/system/container-web01.service.

# Check the status via systemd
$ sudo systemctl status container-web01
● container-web01.service - Podman container-web01.service
     Loaded: loaded (/etc/systemd/system/container-web01.service; enabled; vendor preset: disabled)
     Active: active (running) since Tue 2021-03-30 11:00:55 CEST; 13s ago

# Check the status via podman
$ sudo podman container ls
● container-web01.service - Podman container-web01.service
     Loaded: loaded (/etc/systemd/system/container-web01.service; enabled; vendor preset: disabled)
     Active: active (running) since Tue 2021-03-30 11:00:55 CEST; 13s ago

# Check if the container works
$ curl localhost:8080
<html><body><h1>It works!</h1></body></html>

# Check the logs of the container
$ sudo journalctl -u container-web01

Now, you can start, top, restart, reboot and the container will behave as if it is a regular service. You can also use the systemd management in combination with Podman volumes, custom images or even Podman Pods (which will create multiple unit files).

In case you want to deploy a container (or multiple containers) to  a new server, you just need to copy the unit file to the target server, reload systemd and enable the service.

Documentation

The Podman project provides additional documentation. You can also find various blogs to get a better understanding.

Using Podman and Systemd to manage container lifecycle
Using systemd to control the startup of Podman containers
Running containers with Podman and shareable systemd services
New features coming in Podman v1.7 make using systemd in conjunction with Podman even easier.

Conclusion

Podman and systemd can be quite handy, when it comes to container lifecycles. You can start, stop, restart, create and even share containers for deployments. You are also getting nice logging capabilities.

In further articles, I will use these services to prepare deployments and do automatic container updates, without the need of additional containers.