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 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.
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.
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.
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.