Cloud native

Podman, a root-less alternative to Docker

Dominic St-Pierre

Dominic St-Pierre

Software systems builder

To save some money, I recently destroyed my Docker host DigitalOcean droplet, but it, amongst other things, terminated my purchasing power parity (PPP) feature on my store.

Podman, a root-less alternative to Docker

Of course, I didn't want to remove that feature. For me, people living in countries where the USD to their currency is crazy shouldn't have to pay the same price for my courses as someone from, say, the United States or Europe.

My PPP feature works by first turning an IP address into a country and region that I then calculate against a world PPP dataset. It's nothing fancy, but it's something I want to continue supporting.

The easiest way I found to turn an IP address into the data I need is an open-source Go project that comes with a Dockerfile. Plus, I didn't want to set up an Nginx entry and provision an SSL certificate only for this. So a container is way simpler.

Podman

I tried Podman about two or three years ago. It worked for a while, and then it started crashing. I'm not sure if it was because I'm using WSL v2—yes, I use Windows for accessibility reasons, specifically for my screen reader.

Nonetheless, I decided to give it another spin and installed it on my remaining DO droplet, the one I deploy my web apps on.

The thing I obviously like about Podman is the fact that it's rootless, meaning that it's not running via a system daemon but locally per user.

This distinction is fairly important and more secure. This plays nicely with systemd's local user config; everything is isolated to a user and not system-wide.

The icing on the cake is that Podman is compatible with Docker. So in terms of Dockerfiles and most of the CLI commands, it's a seamless transition. Even if you're using docker-compose, Podman has a podman-compose equivalent.

Since it's not a system daemon, the way it works is by forking the podman process and running the container in the fork, which is owned by the user that started it. It's kind of brilliant when you think about it.

Familiar Docker Commands

One minor note to say is that some images need to be prefixed with their repository. For instance, some-image:latest becomes docker.io/repository/some-image:latest. You need to use fully qualified image names.

Pull an image:

$ podman pull nginx:latest

Run a container

$ podman run -d --name my-app -p 8080:80 image-name

List running containers

$ podman ps

View logs

$ podman logs name-or-id

Remove a container

$podman stop name-or-id$ podman rm name-or-id

I mean, it's mostly just a matter of replacing docker with podman, mostly.

Image Building with Buildah

One of the great things about the rootless design is how it splits the concerns of running containers and building images. Podman actually relies on another tool called Buildah to handle image construction.

The beauty of this is that you can build your images without any root privileges, further enhancing the security of your build pipeline.

While you can still use podman build, under the hood, it's leveraging Buildah. If you want more granular control over your image layers without needing a full Dockerfile, Buildah is the dedicated tool for that job.

# Podman's way (uses Buildah internally)
$ podman build -t my-app-image .

# Buildah's dedicated way for more advanced scripting
$ buildah bud -t my-app-image .

Running with systemd

Now, the best part of having the container run by podman is to integrate with systemd, where your container will restart when the server restarts or any other shutdown mechanism.

The first step is to generate a systemd unit file:

$ podman generate systemd --new --files --name my-container

This will generate the proper systemd unit file in the current directory. You'll need to move it where systemd wants to see those files:

$ mkdir -p ~/.config/systemd/user/
$ mv container-my-container.service ~/.config/systemd/user/

Now, just like we do when we add or modify a system unit file, we need to reload the internal systemd cache:

$ systemctl --user daemon-reload

Enabling the Service to Run All the Time

Now, this service will need to be able to start even if the user that owns it isn't logged in. Think of if the server restarts for any reason—this service will need to start even if our user isn't logged in.

This is where loginctl enable-linger comes in.

$ sudo loginctl enable-linger [your-username]

And then, we enable the service to tell systemd we want to run it all the time.

$ systemctl --user enable container-my-container.service
$ systemctl --user start container-my-container.service

Podman-Compose and Kubernetes

Like I said, if you already have a docker-compose.yaml, you would need to fully qualify your image names and install podman-compose, and you should be up and running:

$ pip install podman-compose

It's a Python-based tool. Personally, I stopped using Compose files. I was mainly using them for dev environments when an app required, say, PostgreSQL, Redis, and maybe other things.

These days, I continue to simplify my environment and mostly have a global PostgreSQL running from Docker, and that's mostly it.

But the option exists, and sometimes, no matter if you want to continue using it or have transitioned, at least you have an option to use podman with your existing Docker Compose files.

Another great benefit is that podman can consume Kubernetes YAML files for more advanced orchestration with the podman play kube command:

$ podman play kube

This is a fantastic feature for engineers looking to simplify their local Kubernetes development workflow, allowing you to test complex multi-container setups right on your machine before deploying to a cluster.

So Far, It Works as Advertised

And just like that, I had my IP-to-country service back without installing Docker and everything that comes with it.

My next step will be to retry it in WSL, but that will be for a later time.

This website uses a session cookie for your shopping cart and optionally an affiliate cookie if you came from an affiliate link. See our privacy policy .

Reject