Using podman for containers
Posted on 2025-02-09 in Programmation
Podman is an alternative to Docker and Docker compose. It uses the same CLI interface than Docker and uses the same standardized image format. So you can use an image built with Docker with it or build an image and then use it with Docker. Its podman-compose command is compatible with Docker compose files, so again nothing to adapt there.
If both tools are so close, you are probably wandering why not use Docker since it’s more famous? While close in its usage, Podman is very different in its implementation. It doesn’t rely on a daemon nor requires root privileges to run containers. So you don’t have to enable anything to get started and you have better isolation by default: within the container root will be mapped to the current user and other users to dedicated UID and GIU on the host. For more on this subject and Docker, see my "use linux user namespaces in docker" article.
Note
This relies on /etc/subuid and /etc/subguid files on linux. Make sure they are filled with something like this (this should be the case on modern distributions):
# username:1st sub id:number of sub id # Typically this will be: jujens:100000:65536
See my article linked above for more on this.
Since there is no daemon started by default on boot, it means containers won’t be restarted after a reboot in the default configuration. I think it can be quite good for development containers you may not need after each reboot. But if you want to use it to run your production containers, that’s a problem. Luckily, Podman let you manage your containers with systemctl thanks to dedicated unit files. This means you have all the power of systemd to run your containers and manage their dependencies.
To do this (full example below):
- Create a file ending with .container in ~/.config/containers/systemd/. For instance, ~/.config/containers/systemd/legadilo.container
- Refresh the daemon with systemctl --user daemon-reload (--user is paramount since we are using a non root user to run the unit).
- Start the unit with systemctl --user start <UNIT> In this case: systemctl --user start legadilo
- Check that the container is running with podman ps
- Reboot and check that the container is still running. By default, your containers will be named systemd-<UNIT> In this case, systemd-legadilo. Any volumes or networks will be created automatically from their associated definition files.
What’s very nice is that you can also rely on this system to auto-update containers. Let’s say you are using the legadilo:latest image. If it changes, running podman auto-update will pull and restart the service with the new image automatically. This can also be run automatically thanks to the podman-auto-update.timer provided systemd timer! For more on systemd timers, see this article.
Here is a sample legadilo.container file to help you get started:
[Unit] Description=Legadilo container # Dependencies can be any service as usual. # Or any other containers referenced by their implicit service file. Requires=postgres.service After=postgres.service [Container] Image=rg.fr-par.scw.cloud/legadilo/legadilo-django:latest # Force a name to prevent the systemd- prefix ContainerName=legadilo # To rely on journald to view the logs (optional). LogDriver=journald # Configure directly the environment variables here. Environment=ENV_VAR=value # Or use an env file. EnvironmentFile=~/.private/legadilo.env PublishPort=8080:8000 Network=legadilo.network [Install] # Required to make the container start on boot. WantedBy=multi-user.target WantedBy=default.target
And the related legadilo.network (put it next to the .container file):
[Unit] Description=Legadilo Network [Network] Subnet=192.168.30.0/24 Gateway=192.168.30.1
And the legadilo-postgres.volume for the PostgreSQL database (still next to the .container file):
[Unit] Description=PG Volume [Volume]
And the legadilo-postgres.container file for completeness:
[Unit] Description=Podman PostgreSQL [Container] Image=docker.io/postgres:17 ContainerName=legadilo-postgres LogDriver=journald Volume=postgres.volume:/var/lib/postgresql/data Environment=POSTGRES_HOST=postgres Environment=POSTGRES_PORT=5432 Environment=POSTGRES_DB=legadilo Environment=POSTGRES_USER=legadilo Environment=POSTGRES_PASSWORD=<RETRACTED> Network=legadilo.network [Install] WantedBy=multi-user.target WantedBy=default.target
You can then inspect the logs with journalctl CONTAINER_NAME=legadilo and journalctl CONTAINER_NAME=legadilo-postgres
Note
I gave a name the legadilo container since it will never clash with something system related. I think it’s easier that way. But, do it the way you want.
Note
If needed, you can access the host with the host.containers.internal name. I think it requires Podman 5.3 or more if you use 5.x versions of Podman to due a limitation in earlier releases 5.x releases.
If you do, please make sure the service listens to the proper interface. Otherwise, it won’t accept the connection coming from the container. It can be 0.0.0.0. to listen to all interfaces or the interface associated with the docker container to limit incoming traffic. In the default configuration, it appears to be 192.168.100.133. By running ip a on the host, you should be able to find the right address.
To conclude, I think Podman is a great replacement for Docker. It solves nicely and by default the long standing issue I had with isolation (ie not running the containers as root while still having files created by the root user in the container owned by my user outside). Being able to manage containers with systemd is awesome and allows me to run all services the same way: with systemctl! I’m now using podman locally on personal work and intent to use it on my servers.
That’s it for today. If you have any questions or remarks, please leave them below!
Resources: