Beginner’s Guide to Using Podman Compose

Beginner's Guide to Using Podman Compose

If you have looked for alternatives to Docker, Podman might have attracted your attention.

One thing that Podman does not yet have is the ability to automatically pull appropriate images and start the containers based on a compose file.

There exists a tool called podman-compose that is an alternative to docker-compose tool and it works with Podman, as you would expect. So let us see how to use this tool.

What is podman-compose?

Docker provides the functionality to specify all the necessary details like the container name, image used, restart policy, volumes, bind mounts, ports, labels, etc inside a single file. This file is usually called the docker-compose.yml file.

This functionality is missing from Podman. Hence we need to use the podman-compose tool to achieve this functionality.

The podman-compose tool does this by adhering to the Compose specification. This is the same specification that Docker adheres to, making it compatible with an existing docker-compose.yml file. (There may be some pedantic differences like enclosing values between double quotes ("), etc but those can be easily solved by looking at the errors.)

Installing the podman-compose tool

Since the podman-compose tool is a relatively new tool, your stable/LTS Linux distribition might not have it in the first party repositories. But nonetheless, let us see what your options are and how to install it.

On Ubuntu 22.10 (Kinetic Kudu) and later and Debian 12 (Bookworm) and later, you can install it using the apt package manager like so:

sudo apt install podman-compose

Users of Fedora 36 and later (the package version on Fedora 35 is 0.1.7-6.git) can use the dnf package manager to install podman-compose like so:

sudo dnf install podman-compose

OpenSUSE Tumbleweed or Leap 15 and later can install the podman-compose tool like so:

sudo zypper install podman-compose

If you are a proud Arch Linux user, you do not need my help. But below is the installation command nonetheless 😉

sudo pacman -Syu podman-compose

Verify the installation

To ensure that the podman-compose utility is either installed or its path is included in the PATH environment variable, you can check it like so:

podman-compose --version

This should also list your Podman version.

On my Fedora 36 machine, I get the following output:

$ podman-compose --version
['podman', '--version', '']
using podman version: 4.3.1
podman-composer version  1.0.3
podman --version
podman version 4.3.1
exit code: 0

Get a $10 credit for Fathom, a privacy-focused website analytics company
Someone has shared a link with you that gives you $10 credit upon sign-up.
Beginner's Guide to Using Podman Compose

Basics of the podman-compose tool

For the sake of keeping this tutorial short, sweet and digestable, I will not cover the structure of a compose file. But fret not! We already have a quick guide to using docker-compose.

For the sake of convenience, below is the compose file that I am using:

version: 3.7

services:


    reverse-proxy:
        image: docker.io/library/caddy:alpine
        container_name: caddy-vishwambhar
        command: caddy run --config /etc/caddy/Caddyfile
        restart: always
        ports:
            - "8080:80"
            - "8443:443"
        volumes:
            - /docker-volumes/caddy/Caddyfile:/etc/caddy/Caddyfile:Z
            - /docker-volumes/caddy/site:/srv:Z
            - /docker-volumes/caddy/caddy_data:/data:Z
            - /docker-volumes/caddy/caddy_config:/config:Z
            - /docker-volumes/caddy/ssl:/etc/ssl:Z
        labels:
            - io.containers.autoupdate=registry
            - pratham.container.category=proxy
        environment:
            - TZ=Asia/Kolkata
        depends_on:
            - gitea-web


    gitea-web:
        image: docker.io/gitea/gitea:latest
        container_name: gitea-govinda
        restart: always
        ports:
            - "8010:3000"
            - "8011:22"
        volumes:
            - /docker-volumes/gitea/web:/data:Z
            - /docker-volumes/gitea/ssh:/data/git/.ssh:Z
            - /etc/localtime:/etc/localtime:ro
        labels:
            - io.containers.autoupdate=registry
            - pratham.container.category=gitea
        environment:
            - RUN_MODE=prod
            - DISABLE_SSH=false
            - START_SSH_SERVER=true
            - SSH_PORT=22
            - SSH_LISTEN_PORT=22
            - ROOT_URL=https://git.mydomain.com
            - DOMAIN=git.mydomain.com
            - SSH_DOMAIN=git.mydomain.com
            - GITEA__database__DB_TYPE=postgres
            - GITEA__database__HOST=gitea-db:5432
            - GITEA__database__NAME=gitea
            - GITEA__database__USER=gitea
            - GITEA__database__PASSWD=/run/secrets/gitea_database_user_password
            - GITEA__service__DISABLE_REGISTRATION=true
            - TZ=Asia/Kolkata
        depends_on:
            - gitea-db
        secrets:
            - gitea_database_user_password


    gitea-db:
        image: docker.io/library/postgres:14-alpine
        container_name: gitea-chitragupta
        restart: always
        volumes:
            - /docker-volumes/gitea/database:/var/lib/postgresql/data:Z
        labels:
            - io.containers.autoupdate=registry
            - pratham.container.category=gitea
        environment:
            - POSTGRES_USER=gitea
            - POSTGRES_PASSWORD=/run/secrets/gitea_database_user_password
            - POSTGRES_DB=gitea
            - TZ=Asia/Kolkata
        secrets:
            - gitea_database_user_password


secrets:
    gitea_database_user_password:
        external: true

Let us now start with the basic commands.

Starting all containers from the compose file

Using the up command, we can create and start the services described in our compose file (docker-compose.yml).

You can simply use the up command and start all the specified containers/services that are listed in the compose file like so:

podman-compose up -d

Running the above command will perform all the necessary actions needed to start the services/containers listed in the compose file. That includes steps like the folloiwng:

  • Pull all the images that are not available locally
  • Create the containers with all the specified options (ports, volumes, secrets, networks, etc)
  • Start the containers in a specific order (defined by constraints like depends_on)

If you looked closely at the above example, you might have noticed a new option; the -d option. This option starts the container in the background, detaching it from the current shell.


Once the containers are up and running, you can verify that by running the podman ps command:

$ podman ps
CONTAINER ID  IMAGE                                COMMAND               CREATED      STATUS          PORTS                                         NAMES
d7b7f91c03aa  docker.io/library/caddy:alpine       caddy run --confi...  4 hours ago  Up 4 hours ago  0.0.0.0:8080->80/tcp, 0.0.0.0:8443->443/tcp   caddy-vishwambhar
1cfcc6efc0d0  docker.io/library/postgres:14-alpine postgres              4 hours ago  Up 4 hours ago                                                gitea-chitragupta
531be3df06d0  docker.io/gitea/gitea:latest         /bin/s6-svscan /e...  4 hours ago  Up 4 hours ago  0.0.0.0:8010->3000/tcp, 0.0.0.0:8011->22/tcp  gitea-govinda

Stop all containers from the compose file

To stop all the containers specified in the compose file, use the down command.

podman-compose down

Additionally, you can give a timeout so the containers can shut themselves down safely. This is done using either of the following options:

podman-compose down -t TIMEOUT_IN_SECONDS
podman-compose down --timeout TIMEOUT_IN_SECONDS

Please note that the down command only stops the container(s). If you want to delete containers, that will need to be done manually.

Start, stop or specific services

If you are iterating through multiple configurations like ports, volumes, environment variables, etc, you might be using the podman-compose up and the podman-compose down command repeatedly.

This will start and stop all services, respectively. Meaning, if you only have one service to start/stop, you now have to wait for all the services listed in a compose file to start and shut down. That’s no good!

To solve this, we can use the start and stop commands to start or stop individual services. There is even a restart command. This does exactly what it says 🙂

Below is a demonstration where I start the gitea-db service, stop it and then restart it, just for you 😉

$ podman-comopse start gitea-db

$ podman-compose stop gitea-db

$ podman-compose restart gitea-db

Pull all necessary images at once

Let’s say that you specified 10 different services in your compose file. What if waiting once is okay to you, but not when you want to start the contiainers–for whatever reason?

If that is the case, all you have to do is use the pull command like so:

podman-compose pull

Running the above command will pull all the images that are specified in the compose file.

Use a different name for your compose file

Now, I do not know why you might do this. There are several reasons for you to do this. Maybe keeping the compose file’s name as docker-comopse.yml triggers you to type docker instead of podman.

Whatever the reason might be, you can use either of the following flags to specify the name of the comopse file, like so:

podman-compose -f COMPOSE_FILE_NAME
podman-compose --file COMPOSE_FILE_NAME

Let’s assume my compose file is not named docker-compose.yml, but is instead named my-compose-file.yml. To use this compose file, I will run the following command:

podman-compose --file my-compose-file.yml

Running the above command will inform the podman-compose tool that the compose file is named my-compose-file.yml instead of docker-compose.yml.

Conclusion

Podman is an amaizng container orchestration tool; and along with the podman-compose tool, creating multiple containers with your specified details become easier! I recommend that you try out the podman-compose tool and let us know about your experience.

Creating and Destroying Containers Using Podman

Creating and Destroying Containers Using Podman

In this part of the Podman series, let’s see about creating and deleting containers.

In case you didn’t know already, Podman is a Docker alternative for managing containers. It follows a similar command structure as Docker.

Pulling images beforehand

Each container needs an image to exist. Without an image, nothing gets executed. Hence, an image needs to be “pulled” from an image registry.

Some of the popular image registries are:

The syntax for pulling an image using Podman is as follows:

podman pull [OPTIONS] FULLY_QUALIFIED_IMAGE_NAME[:tag|@digest]

If you are wondering what FULLY_QUALIFIED_IMAGE_NAME means, look at the two commands below:

# with FQIN
podman pull docker.io/library/debian

# without FQIN
podman pull debian

As you might have noticed, in a fully qualified image name, the format is as such: registry/username/image-name. The registry address for hub.docker.com is docker.io.

To pull a specific tag, apply the tag after the image name, followed by a colon (:). Following is the command to pull the stable-slim tag of the Debian image:

podman pull docker.io/library/debian:stable-slim

List available images

Once one or more images are pulled, you can check which images are available locally with the podman images command. Since I pulled the debian:stable-slim image, my output looks like the following:

$ podman images
REPOSITORY                TAG          IMAGE ID      CREATED     SIZE
docker.io/library/debian  stable-slim  86f9b934c377  2 days ago  77.8 MB

Now that you have your image, you can create a new container.

Creating a container

To create a container with Podman, use the podman run command in this fashion:

podman run [OPTIONS] image [COMMAND [ARGS]]

I will use the -d option to keep the container running in the background. I will also use the -t option to allocate a pseudo-TTY to the Debian image, so it keeps running. You can find a complete list of available options here.

For now, I will create a simple container based on the Debian stable-slim image that you pulled earlier.

podman run -d -t debian:stable-slim

If the container creation were successful, you would receive a random string of alphanumeric characters as the command output. This is the unique container ID.

61d1b10b5818f397c6fd8f1fc542a83810d21f81825bbfb9603b7d99f6322845

List containers

To see all the containers that are running, use the podman ps command. This is similar to the ps command in Linux. Instead of showing system processes, it shows the running containers and their details.

Since I used the -t option as a hack to keep the Debian container running, let us see what the output of the podman ps command looks like.

$ podman ps
CONTAINER ID  IMAGE                                 COMMAND     CREATED         STATUS             PORTS       NAMES
61d1b10b5818  docker.io/library/debian:stable-slim  bash        44 seconds ago  Up 44 seconds ago              gallant_mahavira

From here, you can get various details about our container name. Some of the details are the shorter length but unique container ID, the image used to create this container, when it was created, what ports of the host machine are mapped to which ports of the container and the container name.

Here, you can see that the image used was debian:stable-slim, it was created 44 seconds ago and the container name is gallant_mahavira. When a container name is not specified, a name is generated at random. You can pass the container name when creating a container using the --name CONTAINER_NAME option.

A container can either be running or it can not be running (stopped). The stopped containers can be listed like this:

podman container list -a

I don’t have any stopped containers yet so let’s learn to stop them first.

Stopping containers

To stop a container, use the podman stop command with either the container ID or the container name.

Below is the syntax of the podman stop command:

podman stop [CONTAINER_NAME|CONTAINER_ID]

Let me stop the running container using its name:

$ podman stop gallant_mahavira
gallant_mahavira

Now you can use the aforementioned command to list all the containers, including the stopped ones:

$ podman container list -a
CONTAINER ID  IMAGE                                 COMMAND     CREATED         STATUS                      PORTS       NAMES
61d1b10b5818  docker.io/library/debian:stable-slim  bash        14 minutes ago  Exited (137) 3 minutes ago              gallant_mahavira
💡
The commands podman ps, podman container ps, podman container list and podman container ls all link to the same binary and these commands can be used interchangeably. i.e., you can run the podman ps -a command instead of the podman container list -a command and get the same output.

Starting a container that was stopped

To start a container that was either stopped or failed, use the podman start command.

Assuming, the container you created from the Debian image failed, for whatever reason, you can start it again using its container name or ID, like so:

$ podman start 61d1b10b5818f397c6fd8f1fc542a83810d21f81825bbfb9603b7d99f6322845

Destroying a container

To completely delete or destroy a container, you use the podman rm command.

🚧
Please ensure to stop the container before deleting it.

Once a container is deleted, it will no longer exist. So, when you check the output of the podman container list -a command, the container will not exist in the list.

Here’s an example of stopping and deleting a container with Podman. I used both the container name and ID in the example.

$ podman ps
CONTAINER ID  IMAGE                                 COMMAND     CREATED         STATUS           PORTS       NAMES
61d1b10b5818  docker.io/library/debian:stable-slim  bash        44 minutes ago  Up 1 second ago              gallant_mahavira

$ podman stop gallant_mahavira
gallant_mahavira

$ podman rm 61d1b10b5818
61d1b10b5818f397c6fd8f1fc542a83810d21f81825bbfb9603b7d99f6322845

$ podman container list -a
CONTAINER ID  IMAGE       COMMAND     CREATED     STATUS      PORTS       NAMES

As you can see now, the container is completely gone. If you want, you can create a new container using any image you want.

Conclusion

The tutorial covers the basics of container management with Podman. You learned about creating containers, listing them, stopping them and deleting them.

If you have any doubts, please do not hesitate to comment!

🐧LHB Linux Digest #22.11: Concept of Links, Podman, Vim and More

🐧LHB Linux Digest #22.11: Concept of Links, Podman, Vim and More

Do you like the LHB Linux Digest newsletter? If yes, would you like to provide a 100-200 characters testimonial along with your name, country and job title (student, home user, self hoster, retired teacher, sysadmin with x years of experience etc)? You can concisely share what you like about the newsletter by replying to this email.

I plan to showcase some testimonials on the newsletter page (in works). It’s your chance to be famous! Just kidding 😉

💬 Let’s see what you have in this month’s issue:

  • All about links in Linux
  • Selected new articles on Linux Handbook
  • And the usual newsletter elements like memes, deals and nifty tool to discover

Understanding the Differences Between Podman and Docker

Understanding the Differences Between Podman and Docker

Containerization is all the rage right now. Docker, introduced back in 2014, has become the most popular tool for managing containers. Later, in 2018, Red Hat unveiled Podman as an alternative to Docker.

Since both Docker and Podman are intended to do the same thing, let us see what advantages one holds over the other.

Understanding “containerization” first

Let’s assume that you are the IT person in your organization. You are told to deploy a variety of mission-critical software. What if software A and software B have the same dependency but require different versions of the same dependency? Or worse, what if the dependency of software A conflicts with dependency for software B?

In that case, you deploy them in separate virtual machines. But that removes the scalability factor from it. With two virtual machines running [on the same hardware], the software inherits only 50% of the total system computation power. Increase the number of software necessary from 2 to 10 and you understand how ridiculous this gets.

An inherent negative of a virtual machine is that it also runs a full-blown operating system. In this scenario, this is a net negative. If you have ten virtual machines running RHEL, you have ten copies of the same binaries, inefficiently hogging up your RAM. Even the most minimal installation will still result in more than 4GB of disk space per VM.

So, instead of a virtual machine–where you have a full operating system, your software and its dependencies–that is highly inefficient (in this particular scenario), you can have a container image. These container images only have the software and it’s dependencies packaged together. An added bonus is that these container images are usually less than 300MB in size!

The problem lies with the nature of virtual machines. When you create a virtual machine, the hardware gets virtualized. That means that the CPU, RAM, storage and other resources are virtualized. This is bound to have a noticeable overhead.

Understanding the Differences Between Podman and Docker

Meanwhile, with containers, the software (your Operating System) gets virtualized. This has low overhead compared to traditional virtual machines.

So why do I need Docker or Podman?

You might have used Oracle’s VirtualBox to manage virtual machines in the past. VirtualBox allows you to create, start, stop, pause, modify and even remove virtual machines. That is what Docker and Podamn do too, but for containerized software.

While Docker and Podman have different philosophies, both help you manage your containers. So let us now take a look at how they differ and which one is the best for your use case.

Docker vs. Podman

As I mentioned earlier, Docker and Podman are container management software and are excellent at it. When Red Hat announced Podman as an alternative to Docker, they said that Podman is compatible with Docker’s command line interface. Meaning, moving from Docker to Podman will not require any major changes to your existing code. This also means that you can just substitute the docker command with podman and it just works™.

But they still have a few key fundamental differences, so let’s look at them.

1. daemon-based vs. daemon-less

The main difference that sets Docker and Podman apart is the way they run on your system.

Docker’s core runs as a daemon (dockerd). Meaning, it is always running in the background, managing the containers. Meanwhile, Podman is like your average program; once you perform an action (start/stop a container) using Podman, it exits.

Docker’s daemon-based approach has the following benefits for you:

  • Allows you to easily auto-start containers when the system boots up.
  • Since Docker is a daemon itself, no external service manager like systemd is needed.

That doesn’t mean that Podman is bad. Below are the benefits Podman provides over Docker:

  • If the Docker daemon crashes, the containers are in an uncertain state. This is prevented by making Podman daemon-less.
  • You can use systemd to manage your containers. That gives you virtually unlimited configurability compared to Docker.
  • Hooking Podman with systemd allows you to also update running containers with minimal downtime. You can also recover from any bad updates.

How to Autostart Podman Containers?
Podman is awesome but it doesn’t autostart containers after system reboots. Here’s how to fix that.
Understanding the Differences Between Podman and Docker

2. Security

The biggest argument for using Podman over Docker is security (well, sort of). Podman is pitched as a more secure alternative to Docker.

In my opinion, if you are somewhat of a security-minded person, two of Podman’s primary features will attract you. Previously, I mentioned that the main differentiating factor between Docker and Podman is that Podman does not run as a daemon.

The second main feature of Podman is that it can run containers without root access. What does this mean? It means that you do not need superuser privileges to mange containers.

I have three arguments for why you should use Podman over Docker if you have cybersecurity anxiety, like me.

1: dockerd runs as root

As you already know by now, Docker’s core runs as a “system daemon” i.e. as a daemon executed by the root user.

I have already stated the benefits of having a daemon, but running a daemon as the root user has some security issues too.

Primarily, if the Docker daemon (dockerd) gets compromised, you are left with a system where an attacker can get root access. You certainly don’t want that, do you?

The advantage of using Podman is visible here. Podman does not have a daemon running. And certainly does not have any strict requirements for root access. This brings us to the 2nd argument.

2: Podman supports root-less containers

If you somehow learned about Podman, you might have also heard that it supports running containers without root access.

That is certainly very true and extremely good for security.

Let us assume that the Docker daemon is secure. Now assume that the container image you are using has a security flaw. But this is not known by the developer.

If this image is being run in a container that is owned by the root user, you are SOL.

With Podman, you have the ability to run a container without requiring any root privileges. That means, even if a container image has a security vulnerability, only the user who owns that container is compromised. Other users on that system are still safe, especially the root user.

Docker recently got support for rootless execution of containers, but it has a few missing features. Namely, AppArmor support is absent. AppArmor is Debian’s and Ubuntu’s default Mandatory Access Control (MAC) system.

Getting Started With Rootless Container Using Podman
The advantages of a rootless container are obvious. Learn how to use rootless containers with Podman in this tutorial.
Understanding the Differences Between Podman and Docker

3: Automatically update images

Surely this can’t be a feature, right? Well, it is.

Consider the 2nd argument I made with a slight difference. In this case, assume that the security vulnerability is known to the developers. Hence, they made a public announcement and a patched image is already available.

All good? Well, yes and no. Docker does not have an auto-update mechanism built in. “And neither does Podman. So what?”

I agree, not even Podman has an auto-update mechanism built in. Or does it? It depends on how you define “built in”. Since Podman does not have a daemon of its own, it cannot do regular checks for updates.

But, Podman being a Red Hat product, integrates extremely well with systemd.

⚠️
The arguments I have made do not mean that Docker is insecure and that everyone should use Podman. Nothing is 100% secure. But my arguments do highlight something to look out for!

I cover automatically updating Podman containers in a separate article. This is done using systemd. It is linked below.

How to Automatically Update Podman Containers
Here’s a detailed tutorial on setting up automatic updates for Podman containers.
Understanding the Differences Between Podman and Docker

3. All in one vs segregated

After reading until here, you might have realized that Docker has an “all in one” approach and Podman is… different.

What does this mean for Docker users? For Docker users, you only need the Docker binary installed in your system. The docker command can be used to build images, publish images (to registries like hub.docker.com) and manage containers.

But with Red Hat’s offering, there are three separate binaries. To build your images, you use the buildah command. If you want to publish the image to a registry like hub.docker.com, use the skopeo command. And if you want to manage containers, use the podman command.

Getting Started With Buildah to Manage Containers in Linux
Buildah is used to create, build, manage, run container images as well as containers. It can be used with Docker, Podman, Kubernetes! Let’s get started with Buildah.
Understanding the Differences Between Podman and Docker

If you want an all-in-one solution like Docker or a segregated solution from Red Hat depends on your preferences. Both do what they are designed for.

In case you are looking for a way to install Podman on Ubuntu, here is a good resource.

How to Install Podman on Ubuntu [Proper Way]
Podman requires Buildah and Skopeo to give you the complete container workflow. Learn how to install all these container tools on Ubuntu.
Understanding the Differences Between Podman and Docker

4. Docker Swarm

Docker Swarm is handy when you want to scale up your containers using multiple physical/virtual machines. You can put several computers under a “swarm” and they get a specific purpose. You can have a swarm handling only database requests while a different swarm serves a web server.

While this feature is notably absent in Podman. However, you can achieve the same thing using Kubernetes. I’d argue that Kubernetes is more widely used than Docker Swarm and if scalability in terms of multiple machines is needed, your needs are best served by Kubernetes.

How to use Podman inside of Kubernetes
More information about Podman in containers; specifically with regard to Kubernetes.
Understanding the Differences Between Podman and Docker

Conclusion

Docker is almost synonymous with containers. Podman was created by Red Hat to extend its offering of containerization tools and overcome some of the shortcomings of Docker. Being compatible with docker commands also makes it easier for moving from Docker to Podman without having to forget your Docker knowledge.

It’s not that Docker is not improving itself. Adding the rootless is a sign in that regard.

Overall, it is up to you what you want to use. Personally, I have taken a liking for Podman. You can guess that from all the Podman tutorials I have covered here.

Getting Started With Rootless Container Using Podman

Getting Started With Rootless Container Using Podman

Are you deploying software using containers? Are you using Podman? Do you want to up your security game by running containers with as little privilege as possible? Boy, do I have an article for you!

What is Podman?

Podman is a Red Hat product aimed as a replacement for Docker. For 99% of tasks, it is indeed a true Docker replacement. A few of its features are support for root-less containers, uses the fork/exec model to start containers, is daemon-less, and more.

The advantages of a rootless container are obvious. If it can be prevented from running as root, you run it without root privileges.

via GIPHY

With this article, I hope to help remove some hurdles that may crop up when you use Podman to deploy rootless containers.

Podman in rootless execution

If you are a seasoned IT professional, you might have committed either one of the following crimes:

  • Running the docker command using sudo, escalating its privileges
  • Adding your user non-root user to the docker group. big oof

As you might have realized by now, this is a terrible security practice. You are giving the Docker daemon root access to your machine. That exposes two methods of exploitation:

  • The Docker daemon (dockerd) runs as root. If dockerd has a security vulnerability, your entire system is compromised because dockerd is a process owned by the root user.
  • An image that you use might have vulnerabilities. What if the vulnerable imgae is used by a container that is running as a process of the root user? An attacker can use the vulnerable image to gain access to your entire system.

The solution is simple, don’t run everything as root, even if you trust it. Remember, nothing is 100% secure. I present to you Podman’s ability to manage containers without root access.

If you start a container using Podman as a non-root user, said container does not gain any additional privileges, nor will Podman ask you for a sudo password.

Below are the benefits Podman provides when you use it for root-less containers (without any super-user privileges):

  • You can isolate a group of common containers per local user. (e.g., run Nextcloud and MariaDB under user nextcloud_user and containers Gitea and PostgreSQL under the user gitea_user)
  • Even if a container/Podman gets compromised, it can not get complete control over the host system, since the user executing the container is not root. But yes, the user under which the exploited container is running might as well be considered as user gone rogue.

Limits of root-less Podman

When you use root-full Podman/Docker, you are giving Podman/Docker super-user level privileges. That is certainly very bad, but it also means that all of the advertised functionalities work as intended.

Instead, when you run Podman containers without root privileges, it has some limits. Some of the major ones are as follows:

  • Container images can not be shared across users. If user0 pulls the ‘nginx:stable-alpine‘ image, user1 will have to separately pull the ‘nginx:stable-alpine‘ image for themselves. There is no way [at least not yet] that allows you to share images between users. But, you can copy images from one user to another user, refer to this guide by Red Hat.
  • Ports less than 1024 cannot be binded out of the box. A workaround exists.
  • A root-less container may not be able to ping any hosts. A workaround exists.
  • If you specify a UID in root-less Podman container, any UID that is not mapped to a pre-existing container may fail. Best to execute Podman from an existing user shell. Or better yet, create a systemd service to auto-start it.

podman/rootless.md at main · containers/podman
Podman: A tool for managing OCI containers and pods. – podman/rootless.md at main · containers/podman
Getting Started With Rootless Container Using Podman

Getting started with root-less Podman

Before you get started with the rootless execution of containers, there are a few prerequisites that need to be met.

Make sure you have slirp4netns installed

The slirp4netns package is used to provide user-mode networking for unprivileged network namespaces. This is a necessary if you want your root-less container to interact with any kind of network.

You can install the slirp4netns package on Debian/Ubuntu based Linux distributions using the apt package manager like so:

sudo apt install slirp4netns

On Fedora/RHEL based Linux distributions, use the dnf package manager to install slirp4netns like so:

sudo dnf install slirp4netns

You Arch Linux users know how to do it with pacman, but regardless, below is the command you might be looking for:

sudo pacman -Sy slirp4netns

Make sure that your subuid and subgid are properly configured

Since root-less Podman containers are run by an existing user on the system, said non-root users need permission to run a root-less container as a UID that is not their own UID. This also applies to the GID.

Each user is given a range of UIDs that it is allowed to use. This is specified in the /etc/subuid file; and the /etc/subgid file is for the GIDs a user is allowed to use.

The format of this file is as following:

username:initial UID/GID allocated to user:range/size of allowd UIDs/GIDs

So, let us say my user, pratham wants 100 UIDs for himself and krishna wants 1000 UIDs for himself. Below is how the /etc/subuid file would look like:

pratham:100000:100
krishna:100100:1000

What this effectively means is that the user pratham can use UIDs between ‘100000’ and ‘100100’. Meanwhile, user krishna can use UIDs between ‘100100’ and ‘101100’.

Usually, this is already set up for each user you create. And usually, this range is set to ‘65536’ usable GIDs/UIDs. But in some cases, this needs to be done manually.

But hold on, if this is not already done for your user, you do not need to do this by hand for each user. You can just use the usermod command for this. Below is the command syntax to do so:

sudo usermod --add-subuids START-RANGE --add-subgids START-RANGE USERNAME 

Replace the strings START, RANGE and USERNAME according to your needs.

⚠️
Make sure that the permissions for files /etc/subuid and /etc/subgid are set to 644 and is owned by root:root.

Want to bind Ports less than 1024?

If you are using a reverse proxy for SSL, you will know that ports 80 and 443 need to be accessible by a certificate provider like Let’s Encrypt.

If you try to bind ports lower than 1024 to a root-less container managed by Podman, you will notice that it is not possible. Well, it is possible, but that is not configured out of the box.

A non-root user is not allowed to bind anything on ports less than port 1024.

So, how do I bind ports lower than 1024 in root-less Podman? To do that, first determine the lowest port that you need. In my case, to deploy SSL, I need ports 80 and 443. So the lowest port that I need is port 80.

Once that is determined, add the following line to the /etc/sysctl.conf file:

net.ipv4.ip_unprivileged_port_start=YOUR_PORT_NUMBER

Essentially, you are changing the value of net.ipv4.ip_unprivileged_port_start to the lowest port you need. If I substitute YOUR_PORT_NUMBER with 80, I can bind port 80 with Podman in a root-less container.

WHERE ARE MY IMAGES?

No need to scream at me buddy. I was going to tell you, eventually…

As I pointed out earlier, a limitation of Podman is that it can not share images between users. They either need to be pulled for each user or be copied from one user to another user. Both of these take up 2x/3x/4x space (depending on how many duplicates exist).

The image for any user is stored in their home directory. Specifically, they are stored inside the ~/.local/share/containers/storage/ directory.

Give it a try!

Once all the prerequisites are satisfied, you can run the podman run command from a non-user’s shell and start a container.

Since I use Caddy Server for my SSL, I will use that in this tutorial. To run Caddy Server as a root-less container using Podman, while binding it ports lower than 1024, I will simply run the following command:

$ whoami
pratham

$ podman run -d --name=prathams-caddy -p 80:80 -p 443:443 caddy:alpine
e6ed67eb90e6d0f3475d78b287af941bc873f6d62db60d5c13b1106af80dc5ff

$ podman ps
CONTAINER ID  IMAGE                           COMMAND               CREATED        STATUS            PORTS                                     NAMES
e6ed67eb90e6  docker.io/library/caddy:alpine  caddy run --confi...  2 seconds ago  Up 2 seconds ago  0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp  prathams-caddy

$ ps aux | grep caddy
pratham     3022  0.0  0.0  85672  2140 ?        Ssl  06:53   0:00 /usr/bin/conmon --api-version 1 -c e6ed67eb90e6d0f3475d78b287af941bc873f6d62db60d5c13b1106af80dc5ff [...]
pratham     3025  0.1  0.3 753060 32320 ?        Ssl  06:53   0:00 caddy run --config /etc/caddy/Caddyfile --adapter caddyfile

As you can see, the user pratham is not root and also that I did not use the sudo command to escalate privileges of the user pratham. I was able to run the Caddy Server container with root-less privileges using Podman.

The output of ps command shows that the PID 3022 is of a process owned by the pratham user. This process is the Caddy Server container (I have trimmed the output). The PID 3025 is a child process of PID 3022 which is also under the pratham user.

Did I not address your issue?

I apologize if I did not cover your issue with root-less Podman containers. Since Podman is new (in software years), it will have some unexpected issues.

Fret not. I have included the official troubleshooting guide below. Refer to that when in doubt. It is also the one that will be updated frequently.

podman/troubleshooting.md at main · containers/podman
Podman: A tool for managing OCI containers and pods. – podman/troubleshooting.md at main · containers/podman
Getting Started With Rootless Container Using Podman

Conclusion

In this article I demonstrate how you can get started with managing root-less container using Podman. I talk about the necessary software that enables this functionality. I go over common issues you may face with root-less Podman containers and how to mitigate them. I also link to the official documentation where you might get a more technical troubleshooting guide.

How to Automatically Update Podman Containers

How to Automatically Update Podman Containers

It is a good practice to update software, especially when you get new features and/or added security in the new updates.

In this article, I will show you how to enable automatic updates for the containers managed by Podman.

For demonstration purposes, I will use the caddy image from Docker Hub.

Determine the source for fetching image

To use a container image, Podman needs to pull that image from somewhere. This “somewhere” is referred to as the auto-update policy.

The auto-update policies are as following:

  • registry: When the auto-update policy is set to the string registry, Podman will pull the image from a remote registry like Docker Hub and Quay.io.
  • local: When the auto-update policy is set to the string local, Podman will fetch image from locally built images. This update policy comes in handy when you are a developer and wish to test local changes before pushing them to a remote registry.
✍🏻
I am running a root-less container. I have tried specifying commands if you use a root-full container wherever I can, but me being a human, that may have been missed.

Simply use the sudo when you encounter an error related to privileges (only if you have a root-full container).

Enabling auto-updates

Now that you are aware of what an “auto-update policy” is, we can now proceed with this tutorial.

All that you need to do to enable automatic updates for a container managed by Podman is to add the following label:

io.containers.autoupdate=AUTO_UPDATE_POLICY

Substitute the string AUTO_UPDATE_POLICY with either registry or with local and you’re good to go!

“But how will the container auto-update when Podman does not have a daemon?”

systemd integration

When I said that “You only need to add the io.containers.autoupdate label to your container,” you see… I lied misspoke.

The container that needs to be automatically updated must be managed by systemd. “Why are you shoving systemd down my throat?” Well because Podman has a daemon-less architecture. And the container needs to be managed somehow.

If you want your container to be started automatically on boot, you already are using systemd to do so.

In the article linked below, I discuss how you can integrate a Podman container–be it a root-full container or a root-less container–with systemd (with the intention of auto-starting it on boot as well).

How to Autostart Podman Containers?
Podman is awesome but it doesn’t autostart containers after system reboots. Here’s how to fix that.
How to Automatically Update Podman Containers

Regardless, I will give you a quick overview of how to manage Podman containers using systemd.

Step 0: Create a container

Make sure that you have an existing container. It does not matter if the container is running or stopped.

You can check what containers you have by running the following command:

podman container list

For this tutorial, I have pulled an older image of Caddy Server (version 2.5.2-alpine) and have renamed it to alpine. Renaming this image will help demonstrate the update process. You can check it yourself because the image ID of tag 2.5.2-alpine and alpine are same 😉

Using this image, I created a container that is named prathams-caddy.

Below is how is looks on my computer:

$ podman images
REPOSITORY               TAG           IMAGE ID      CREATED      SIZE
docker.io/library/caddy  2.5.2-alpine  d83af79bf9e2  2 weeks ago  45.5 MB
docker.io/library/caddy  alpine        d83af79bf9e2  2 weeks ago  45.5 MB

$ podman container list
CONTAINER ID  IMAGE                   COMMAND               CREATED        STATUS            PORTS       NAMES
99d1838dd999  localhost/caddy:alpine  caddy run --confi...  5 seconds ago  Up 6 seconds ago              prathams-caddy

As you can see, I have a container called prathams-caddy and it is running the caddy image (which is at an older version).

The container prathams-caddy was created with the label io.containers.autoupdate set to registry. If you have an existing container without this label, do not worry; you don’t need to re-create your container. This will be covered in the next step.

Step 1: Generate a systemd service file for your container

Well, to manage your Podman container via systemd, it needs to be turned into a service. Realistically speaking, you want your container to start at boot and want to stop it when you turn off the system. It makes sense to run your container as a systemd service.

“But isn’t it a too much work to write a systemd service file for each container I have?” It is. And developers of Podman have thought of this. The only manual work you need to to do is run a command for each of your containers.

If you have a root-full container (a container with root privileges), run the following command:

sudo podman generate systemd -f --new --name CONTAINER_NAME

If your container is a root-less container (a container without root privileges), run the following command:

podman generate systemd -f --new --name CONTAINER_NAME

Substitute the string CONTAINER_NAME with the name of your container and a file with the name container-CONTAINER_NAME.service will be created.

For my Caddy Server container, I will do the following:

$ podman generate systemd -f --new --name prathams-caddy
/home/pratham/container-prathams-caddy.service

As evident from the output, the file container-prathams-caddy.service was created. It will be created in the current working directory.

But this is for containers that already have a value set for the label io.containers.autoupdate. What about containers that already exist without this label? You don’t have to re-create your containers with this label.

In that case, edit the systemd service file and add the following line to the ExecStart field like so:

[...]
ExecStart=/usr/bin/podman run 
    [...]
    --label io.containers.autoupdate=registry
[...]

Essentially, your systemd service file is just invoking the podman run command. All you are doing is adding the label io.containers.autoupdate to the podman run command. Neat?

Step 2: Move the systemd service file

Now that a systemd service is already created for us, we need to enable it. But before it is enabled, the service file needs to be moved in one of the following directories:

  • /etc/systemd/system/: If the container is root-full and needs to be started with superuser privileges.
  • ~/.config/systemd/user/: If the container is root-less, place it in the corresponding directory of the user that is intended to start it.

The container prathams-caddy is a root-less container so I will move it accordingly.

$ mv -v container-prathams-caddy.service ~/.config/systemd/user/
renamed 'container-prathams-caddy.service' -> '/home/pratham/.config/systemd/user/container-prathams-caddy.service'

Step 3: Enable the systemd service

Now that the service file is place in an appropriate directory, we can proceed enabling it. But first, systemd needs to be made aware of our newly created service, without rebooting our computer.

If the service needs superuser privileges, reload systemd using the following command:

sudo systemctl daemon-reload

If the service is starting a root-less container, run the following command:

systemctl --user daemon-reload

Once that is done, we can simply use the systemctl enable command to enable our service. Use either command based on your requirements:

# for a root-full container
sudo systemctl enable SERVICE_NAME.service

# for a root-less container
systemctl --user enable SERVICE_NAME.service

Once enabled, you can check the status of your service. Do not be alarmed if it says inactive (dead). This is because our service starts at boot and we haven’t booted up the computer after enabling the service.

$ systemctl --user enable container-prathams-caddy.service
Created symlink /home/pratham/.config/systemd/user/default.target.wants/container-prathams-caddy.service → /home/pratham/.config/systemd/user/container-prathams-caddy.service.

$ systemctl --user status container-prathams-caddy.service
○ container-prathams-caddy.service - Podman container-prathams-caddy.service
     Loaded: loaded (/home/pratham/.config/systemd/user/container-prathams-caddy.service; enabled; vendor preset: enabled)
     Active: inactive (dead)
       Docs: man:podman-generate-systemd(1)

Now is the best time to stop the container (if it is running) and podman container rm it and reboot.

$ podman stop prathams-caddy
prathams-caddy

$ podman container rm prathams-caddy
99d1838dd9990b2f79b4f2c83bc9bc16dfbaf3fdeeb6c6418ddd6e641535ce21

Step 4 (optional): Enable user lingering

If you created a systemd service for a root-less container, it is best to enable user lingering for your particular user.

This can be achieved by running the following command:

sudo loginctl enable-linger

So will my container auto-update now?

“I added the io.containers.autoupdate label to my container(s). I also manage my container(s) with systemd now. Will my container automatically update the image now?”

Well no… But only one step remains… that ought to count for something, right? Right? right…?

All that needs to be done now is to enable the podman-auto-update service. Do so with the following command:

sudo systemctl enable podman-auto-update.service

With the podman-auto-update service enabled, systemd will check if any image needs to be updated. If there are updates, the images if fetched first. Then the container is restarted. The old image is kept in case the update needs to be rolled back for n number of reasons.

Auto updates? No thank you.

If auto-updates are not your thing, you may also be pleased to know that you can manually update containers with just one command, provided they are managed by systemd.

That command is the podman auto-update command. And if you just want to check for updates, pass in the --dry-run option so that no containers are acutally upgraded.

Let’s check if I can upgrade my caddy image from ‘2.5.2-alpine’ to ‘2.6.1-alpine’ using the podman auto-update command.

$ podman container list
CONTAINER ID  IMAGE                   COMMAND               CREATED        STATUS            PORTS       NAMES
a712a3c8846b  docker.io/library/caddy:alpine  caddy run --confi...  2 seconds ago  Up 2 seconds ago              prathams-caddy

$ podman auto-update --dry-run
UNIT                              CONTAINER                      IMAGE         POLICY      UPDATED
container-prathams-caddy.service  a712a3c8846b (prathams-caddy)  caddy:alpine  registry    pending

As you can see in the UPDATED column of the output of podman auto-update command, it says pending. This indicates that an update is available.

To update the container, remove the --dry-run option.

Conclusion

The process of enabling automatic updates for your Podman containers can feel a bit involving, but trust me, this will pay off in the long run. All of your containers that are managed will be automatically updated at midnight (if there are any updates). And, if the container faces any problems, systemd will roll it back to an older image so your container keeps on running.

If you found this useful, please comment down below and tell us your thoughts! <3

How to Autostart Podman Containers?

How to Autostart Podman Containers?

Podman is by far one of my favourite tools for container management. It has a daemon-less architecture that allows an unprivileged user to run containers without root access, further enhancing system security.

That doesn’t mean Podman is perfect, without issues. One such pain in the neck is its restart policy. The containers will not restart automatically after your server reboots.

The problem: Container restart policy

If you look at the man page of podman-run, you will notice that the --restart option will not start the container(s) again when the system reboots.

It says, “Please note that --restart will not restart containers after a system reboot.”

This is different than what you might be used to – with Docker. The reason behind behaviour is Podman’s daemon-less architecture.

The containers managed by Docker respect this for every reboot because the Docker daemon starts at boot and starts the specified containers.

But, being daemon-less means Podman does not start on boot, so the containers do not start on boot either.

The solution: systemd

Love it or hate it, the distribution of your choice probably uses systemd as the init system. Best put it to good use! 🙂

Step 1: Get the container up and running

There can be multiple ways you might want to start a container. Maybe you want a simple container and are using the podman run command. You might use a docker file if you have a complex configuration.

For demonstration purposes, I will create a container based on the mariadb container image and name my container chitragupta-db.

$ podman ps
CONTAINER ID  IMAGE                             COMMAND               CREATED         STATUS             PORTS       NAMES
422eed872347  docker.io/library/mariadb:latest  --transaction-iso...  58 seconds ago  Up 58 seconds ago              chitragupta-db

Now that a container is running, I will proceed with the next step.

Step 2: Create a systemd service

As I mentioned earlier, Podman is a daemon-less container management software. Hence, the startup of podman managed containers needs to be externally managed.

I will demonstrate doing exactly that, using systemd.

systemd is an init system that manages services/daemons on Linux-based operating systems. Therefore, it is the perfect candidate for solving our problem.

Step 2A: Create a systemd unit file

The systemd unit file for your container does not need to be manually created. There is a handy command for that. That command is podman generate systemd and the syntax is as follows:

podman generate systemd --name CONTAINER_NAME

To generate a systemd unit file for your container, use the podman generate systemd command along with the name of your container.

In my case, I named my container chitragupta-db, so I will use that.

How to Autostart Podman Containers?

As you can see, this particular podman command did all the work for us. But this isn’t particularly useful… yet.

The output of podman generate systemd is what you should have in your unit file for your service. But why copy paste? The podman command also comes with a handy option, which is the --files (or -f for short) option.

Using the --files option will populate a file with the necessary contents instead of printing it to your console/terminal. Using this option will create a file named container-CONTAINER_NAME.service in your current working directory.

$ podman generate systemd --name chitragupta-db -f
/home/pratham/container-chitragupta-db.service

$ ls *.service
container-chitragupta-db.service

In my case, the name of my container is chitragupta-db, it created a file named container-chitragupta-db.service in my current working directory.

Since the podman generate systemd command is creating a systemd unit file, you can also use the --after=, --requires=, --wants= options to specify respective dependencies for your container(s).

Step 2B: Using the correct location

As you might have noticed, the specified podman command will create a new systemd unit file in your current working directory. But, as the container (at least in my use case) is rootless, it needs to be run as a normal user, instead of root.

For that to occur, the generated systemd unit file needs to be placed in the ~/.config/systemd/user/ directory. Change your directory and generate the systemd file in the specified directory.

$ podman generate systemd --name chitragupta-db -f
/home/pratham/.config/systemd/user/container-chitragupta-db.service

The systemd unit file was generated without any user input and it is placed inside the correct directory.

Now all that remains is to enable it.

Step 3: Enable systemd service

Now that podman has automatically generated a systemd unit file in the correct location (~/.config/systemd/user/), it is time to enable this service.

Enabling a systemd service for a particular user is done using the --user option.

Use the following command syntax to do so:

systemctl --user enable SERVICE_NAME.service

Your output should be similar to what is shown below:

$ systemctl --user enable container-chitragupta-db.service
Created symlink /home/pratham/.config/systemd/user/default.target.wants/container-chitragupta-db.service → /home/pratham/.config/systemd/user/container-chitragupta-db.service.

Once enabled, you can check the status of this systemd service, be sure to use the --user option, as this service is being run as the current user, instead of being executed by root.

$ systemctl --user status container-chitragupta-db.service
○ container-chitragupta-db.service - Podman container-chitragupta-db.service
     Loaded: loaded (/home/pratham/.config/systemd/user/container-chitragupta-db.service; enabled; vendor preset: disabled)
     Active: inactive (dead)
       Docs: man:podman-generate-systemd(1)

Do not be worried that the service status is inactive (dead).  The container chitragupta-db is still running and you have not rebooted yet 😉

Step 4: Enable user lingering

As you know by now, this service is being run by a normal user (pratham is the user in my case) and not the root user. That means that said user needs to be logged in at the boot and should stay active even if they log out from a GUI or TTY session.

This can be achieved by the use of loginctl command. From the terminal session of your user, run the following command:

sudo loginctl enable-linger

This command will ensure that a user session for your user is spawned at boot and kept active even after logouts from GUI or tty session(s).

Finally, the re-starting of containers managed by Podman at boot has been enabled. A reboot will automatically restart the containers of which you have created a systemd unit file of, and enabled them.

This was quite a long process, but thankfully manual intervention was not necessary.

Conclusion

This tutorial guides you to the process of creating systemd unit files for managing the autostart of containers managed by Podman, at boot. The use of systemd helps the administrator keep an eye on containers using the systemd interface many are familiar with.

Do comment and let us know your thoughts on this. If you have any alternative way to do it, comment below.