Side note: Monitor your containers with eBPF
Head over to Better Stack and start monitoring your containers with zero-overhead observability. Track container health, detect crashes, and get instant alerts—whether you're running Docker or Podman.
Containerization has become an essential tool for developers and system operators to package and deploy applications on various systems and platforms efficiently. Many containerization solutions exist today, but without a doubt, Docker has emerged as the de facto standard. This is largely due to its excellent tooling, strong community, and vast ecosystem of pre-built images that can be easily shared and used across different environments.
Docker has held this position for many years, and it has truly revolutionized how applications are shipped. At the same time, its wide adoption inspired the development of many other containerization solutions offering even more features and capabilities. One such solution is Podman.
Podman is an open-source container engine that aims to provide a more secure and lightweight alternative to Docker. It allows users to run containers without requiring a daemon, making it easier to manage and deploy containers on a variety of systems. Additionally, Podman offers better security defaults through features such as rootless containers (i.e., running containers through non-root users), user namespaces, and a more careful utilization of kernel capabilities, all of which can protect the host system from potential vulnerabilities and security threats.
With its growing community and close compatibility with Docker images and commands, Podman has gained significant traction among developers and system administrators looking for alternative containerization solutions.
In this article, we'll explore some of the key features and benefits of using Podman as a containerization tool. We'll also discuss how it compares to Docker and see why it has become a popular alternative choice in the industry.
Let's dive right in.
Before proceeding with this article, ensure that you meet the following prerequisites:
Head over to Better Stack and start monitoring your containers with zero-overhead observability. Track container health, detect crashes, and get instant alerts—whether you're running Docker or Podman.
While both Podman and Docker allow users to run, manage, and deploy containers in an efficient and scalable manner, there are some key differences between the two. In this section, we will explore several of these differences:
One of the main differences between Podman and Docker lies in their architecture. While Docker relies on a client-server model, Podman employs a daemonless architecture. With Podman's approach, users manage containers directly, eliminating the need for a continuous daemon process in the background. This direct management often results in Podman containers launching significantly faster, sometimes up to 50% quicker than Docker, depending on the image used.
This architecture also enhances security. In Docker, initiating a container means sending a request to the Docker daemon via the Docker client which subsequently launches the container, which means that the container processes are children of the Docker daemon, not the user session:
As a result, any significant event coming from a container process that's picked
up by the Linux Audit system (auditd) specifies its audit user ID as unset
rather than the actual ID of the user who started the respective container in
the first place. This makes it extremely difficult to link malicious activity to
a specific user and taints the security of the system.
With Podman, since each container is instantiated directly through a user login
session, the container process data retains this information and auditd can
accurately detect and list the ID of each user who started a specific container
process, maintaining a clear audit trail.
The absence of a daemon in Podman leads to a distinct approach to managing
container lifecycles compared to Docker. On Linux, Podman relies extensively on
Systemd for this purpose. For instance, to correctly
enforce restart policies for containers using the --restart always flag,
Podman relies on a systemd service called podman-restart. This service
automatically restarts all designated containers after each system reboot.
Moreover, Podman exposes a handy command for generating Systemd service files
from running containers. This allows you to bring your containers under
systemd management to start, stop, and inspect the various services running
inside of them more easily. In contrast, Docker handles all these tasks
internally through the daemon itself.
When developing locally, Docker users normally reach for
Docker Compose to define and manage
multi-container applications more easily. While Podman doesn't support Compose
files out of the box, it provides a compatible alternative called
Podman Compose, which typically
works seamlessly with existing docker-compose.yml files. For a native
experience, you may also use pods, a concept that Podman borrows from
Kubernetes and allows users to manage a group of
containers as one uniform unit.
When it comes to production deployment, Podman lacks a tool like Docker Swarm for orchestrating multi-container application workloads. The best possible alternative in such cases is to use an external orchestration system such as Kubernetes, which offers similar features and integrates well with Podman, although it may require some additional configuration and setup to ensure that everything works correctly.
Containers aim to isolate applications from the host system securely, minimizing compatibility issues and enhancing security. A primary security concern is the risk of container breakout, where an attacker could compromise the host system. To mitigate such risks, running containers with minimal privileges is essential.
Podman is designed to help with this by providing stronger default security settings compared to Docker. Features like rootless containers, user namespaces, and seccomp profiles, while available in Docker, aren't enabled by default and often require extra setup.
Podman's default setup includes rootless containers running in isolated user
namespaces, limiting the impact of any potential breakout. In contrast, Docker's
default setting runs container processes as root, posing a higher risk in case
of a breakout.
Moreover, Podman containers, tied to user sessions, allow audit systems to trace malicious activities back to specific users, unlike Docker, where tracing to a user is challenging due to the system-wide daemon.
Podman and Docker use Linux kernel capabilities and seccomp profiles to control process permissions. By default, Podman launches containers with a narrower set of 11 capabilities compared to Docker's more permissive default setting of 14 capabilities.
In general, while both Podman and Docker can be configured for robust security, Podman generally requires less effort to reach a secure configuration.
Learn More: Docker Security: 14 Best Practices You Should Know
To sum it up, both Docker and Podman are capable tools, and knowing the main differences between the two can help you choose the right one for your specific needs. Please refer to the table below for some of the major differences between the two, and you can assume full parity for most other features.
| Podman | Docker | |
|---|---|---|
| Daemonless architecture | ✔ | ✘ |
| Systemd integration | ✔ | ✘ |
| Group containers in pods | ✔ | ✘ |
| Supports Docker Swarm | ✘ | ✔ |
| Supports Kubernetes YAML | ✔ | ✘ |
With all of this clarified, let's go ahead and install Podman locally.
Like Docker, Podman can run without a problem on all popular operating systems. This includes macOS and Windows, as well as all major Linux distributions. The only significant difference to note is that while Podman can run natively on Linux, it requires a virtual machine to work on Windows and macOS. This adds a few extra steps to the installation process on these systems, but other than that, the core functionality remains the same.
This tutorial assumes that you are using a Debian-based Linux distribution such as Ubuntu, Mint, or Debian itself. The installation process for other operating systems is very similar and can be applied by referring to the official Podman installation instructions.
Make sure that you are using a relatively recent version of your preferred
distribution, as PPA repositories on older versions may not have the latest
Podman available for installation. For instance, at the time of this writing,
the latest major version of Podman is 4.x, yet the most recent LTS version of
Ubuntu
(Ubuntu 22.04 LTS)
locks you in to Podman 3.x. Therefore, this tutorial is based on a newer
non-LTS version
(Ubuntu 23.10).
Start by downloading the latest information from all configured upstream package sources:
Then run the following command to install Podman:
You will see an output similar to:
Type Y when prompted, then hit Enter to continue.
After the installation is completed, you can verify that Podman is available by typing:
The output indicates that version 4.3.1 was successfully installed locally,
and you can run podman commands. You are now ready to launch your first
container.
Let's verify that the installation works by running the well-known "Hello World!" image from Docker Hub:
You will observe an output similar to:
This output shows something important. Even though the "Hello World!" image was built with tools from the Docker ecosystem, Podman was still able to run the container.
That's because both Docker and Podman follow the OCI (Open Container Initiative) standards. The OCI defines an image format specification and a runtime specification enabling different container runtimes, such as Podman and Docker, to interoperate.
As a result, Podman can seamlessly work with most Docker images and containers. This compatibility allows you to migrate your existing workloads to Podman easily without having to make any modifications and also to leverage the vast library of Docker images available on Docker Hub.
Let's further break down the output from the command above. The first line says:
This message indicates that Podman referenced a configuration file to obtain the
fully qualified name of the hello-world image. Unlike Docker, Podman
recommends against using short names to refer to images and does not default to
a specific registry, unless explicitly instructed to do so via its configuration
files. Docker, on the other hand, always uses Docker Hub (docker.io) as its
default registry and tries to locate every image there when a fully qualified
name is not explicitly provided.
You can further inspect the shortnames.conf file and confirm that the
hello-world alias maps to the image docker.io/library/hello-world:
The next few lines say:
These messages indicate that the latest version of the hello-world image was
successfully downloaded to your local machine.
You can verify this by issuing:
The remaining output (the "Hello from Docker!" message and all subsequent lines)
display a message that's hard-coded into the
hello
binary that ships with the hello-world image. This message could be a little
misleading as it suggests that the Docker engine executed the container when, in
reality, Podman did. It's important to understand that neither the Docker client
nor the Docker daemon were in any way involved in the process.
Before you move on further, remove the hello-world image, as it's no longer
needed:
The output indicates that the image was removed, displaying its tag and ID for reference:
Next, let's examine what happens when you attempt to run an arbitrary image using only its short name.
As we discussed previously, Podman suggests using fully-qualified names for container images to avoid ambiguity and ensure that the correct image is always referenced from a specific registry.
Let's try running a container using the official Caddy image from Docker Hub. If you're unfamiliar with Caddy, it's a lightweight web server and reverse proxy known for its ease of use and fantastic performance. By running Caddy as a container, you can quickly spin up a local web server for testing purposes.
Coming from a Docker background, you would typically use the following command to start the container:
This translates to the following command in Podman:
Most of the time, transitioning from Docker to Podman is a matter of changing
docker to podman in your command. In fact, many users add
alias docker=podman to their shell config files and use Podman as an alias for
docker commands.
The flags are also identical:
--rm specifies that the container should be automatically removed after it
exits, so it doesn't clutter up your system.-p 8080:80 specifies that port 8080 on the host machine should map to port
80 in the container, so that you can access the web server running inside
the container by typing localhost:8080 in your browser.Try running the command. If everything goes well, a container should launch,
allowing you to open localhost:8080 in a browser to see the built-in Caddy
test page.
Contrary to what you might anticipate, the command fails and returns the following error:
The command didn't work because Podman couldn't understand where to pull the
caddy image from.
It first looked up shortnames.conf for an alias named caddy but could not
find one. Try a grep on shortnames.conf, and you will see that it returns no
matches indeed:
It then looked up the registries.conf file for a list of so-called unqualified
search registries. An unqualified search registry is the one that Podman tries
to contact whenever a non-fully qualified image name is supplied with the run
command.
There are three possible solutions to the issue that you encountered. Let's explore all of them:
You can specify the fully-qualified name:
This works! However, coming from a Docker background, you may find using short names more ergonomic, because this is the workflow that you're used to. This is absolutely possible with Podman, using either aliases or unqualified search registries which we'll explore below.
You may decide to define an alias for caddy instead. When doing so, keep in
mind that the /etc/containers/registries.conf.d/shortnames.conf file is not
meant to be modified directly, as it is shipped as part of the
shortnames project. The correct way
to define a new alias is by adding an [aliases] section to your
registries.conf file like this:
The default registry configuration for Podman is located at
/etc/containers/registries.conf, but modifying this file requires root
privileges. This slightly defeats the idea of rootless access that Podman aims
to support. However, Podman offers a mechanism to overcome this limitation. You
can put your configuration into $HOME/.config/containers/registries.conf, and
it will take precedence over /etc/containers/registries.conf. This requires no
root privileges.
Go ahead and create the $HOME/.config/containers/registries.conf file on your
system:
Then add the following line to your file:
Now re-run:
You will see the following output indicating that the solution works:
While this solution works, adding aliases for every image that you plan on using will be tedious and time-consuming in the long run. This leads us to the third possible solution—configuring an unqualified search registry.
Before you do that, revert the changes that you did so far so you can start with a clean state.
Hit Ctrl+C to terminate the Caddy container and return to your terminal. Then
remove the [aliases] configuration from the registries.conf file by
truncating the entire file:
Remove the caddy image that Podman just downloaded:
As expected, the output shows the tag and ID of the image that was removed:
You are now ready to define an unqualified search registry.
Open your $HOME/.config/containers/registries.conf file and paste the
following contents:
Now re-run:
You will see the following output:
The caddy image was successfully downloaded, a container was launched, and
Caddy is now ready to serve web requests.
To confirm that everything works, you can navigate to localhost:8080. There,
you should see the Caddy test page:
Using an unqualified search registry is unquestionably a better option than using aliases, especially if you intend to use Podman as a drop-in replacement for Docker, because you can continue using short names the way that you are used to, and they will resolve from Docker Hub by default.
Before you continue further, go back to your terminal and hit Ctrl+C to stop
the container, then remove the Caddy image:
You are likely used to working with private registries that host your organization's proprietary images. Docker can undoubtedly facilitate that, and so can Podman. The process is very similar for both tools.
This example assumes that you have a working Docker Hub account. If you don't have an account, you can register one for free. The free tier allows you to maintain one private repository free of charge.
Log into your Docker Hub account and navigate to Account Settings:
Go to Security and click on New Access Token:
Specify Podman tutorial as the description and Read & Write as the desired permissions, then click Generate:
Copy the generated access token and store it somewhere safe. We will refer to
this token as <your_access_token>:
The token should now be listed under the available access tokens in your Docker Hub account:
Navigate back to Repositories and create a new private repository as follows:
If all goes well, you should see the repository listed in your Docker Hub account:
Let's now configure Podman to run with your private repository.
Type the following command:
Here, docker.io can be omitted if you have listed it as the first
unqualified-search-registries entry in your registries.conf file.
Nevertheless, it's still considered a good practice to specify the registry
explicitly.
Otherwise, podman login will fail with the following error:
Type in your username and access token when prompted, and you should receive the following output:
Continue by downloading the official hello-world image locally:
A successful output will look like this:
Now list your local Podman images:
You should see the following output:
Go ahead and upload a copy of the hello-world image to your private
repository.
You will get the following output:
Navigate back to your private repository on Docker Hub to verify that the image was successfully uploaded:
You can now remove the public hello-world image from your local machine:
Now, try running a container using the image from your private repository:
Podman goes ahead, successfully pulls the private image, and launches a container using the image:
Without valid login credentials, you would have received the following error instead:
As you can see, using Podman with a private registry is almost identical to
using Docker for the same purpose. The only difference is that you prefixed your
commands with podman instead of docker. Just like Docker, you can use Podman
with all popular private registries.
Before you continue further, make sure to logout:
Also, remove the private image from your local machine:
With this, you can proceed to the next section and learn how to orchestrate multiple containers with Podman.
See the complete picture when requests flow between your WordPress and MySQL containers. Better Stack uses eBPF-based distributed tracing to capture every database query, API call, and network hop—with zero instrumentation.
So far, you have only been launching one container at a time to explore how Podman works. At some point, you'll surely find it necessary to run multiple containers working together as a unit. In this section, you'll explore one of the possible ways to do that with Podman Compose.
Podman offers several options for orchestrating multiple containers, but Podman Compose is the most similar to what's used in the Docker world. Other options are pods and Kubernetes manifests, but both call for a deeper comprehension of Podman (and Kubernetes), so we'll leave them out for now.
In the following example, you'll use the Docker Compose instructions supplied with the official WordPress image on Docker Hub to launch a simple WordPress installation backed by a MySQL database server. If you're unfamiliar with WordPress, it is a popular blogging and content management platform written in PHP.
In the Docker ecosystem, Compose allows you to define and manage multiple
containers through definitions stored inside a docker-compose.yml file. Being
used to working with Docker Compose, you can continue using your existing
docker-compose.yml files with Podman with the help of Podman Compose.
Podman Compose is a community-driven tool that implements the Compose specification and seamlessly integrates with Podman. It relies on Python 3 to work, and one of the easiest ways to get started with it is through pipx.
If you don't already have Python 3 or pipx installed on your system, you can
install them by running:
You can verify that pipx is working by typing:
This will output a version identifier similar to:
The output confirms that version 1.2.0 is of pipx is installed on your
system, and you're ready to start using it.
You can run the following command to install Podman Compose:
If everything goes well, you should see:
The output indicates that pipx was placed in your $HOME/.local/bin folder.
However, that folder is likely not included in your $PATH variable, meaning if
you type podman-compose right now, you will get a similar error:
Don't be confused by this error. Podman Compose was successfully installed, but
you need to add its installation folder to your $PATH.
You can address this by running:
This command will ensure that $HOME/.local/bin is appended to your $PATH
through one of your shell's config files (.profile, .bash_profile,
.bashrc, etc. depending on your specific setup).
After running the command, you will see the following output:
You can follow the instructions and re-open your terminal session. Alternatively, if you know precisely which shell config file was modified, you can source the file for the changes to take immediate effect.
For example:
To confirm that the $PATH is set correctly, you can run:
You should see /home/<your_user_name>/.local/bin listed in the output:
Create a new folder and cd into it:
Create an .env file and define the following variables:
Then, create a new docker-compose.yml file and paste the following contents:
Save the file, then run:
Let's examine the output segment by segment.
Initially, podman-compose launches and starts analyzing the
docker-compose.yml file:
It detects two services named wordpress and db, and starts processing them
in sequential order.
First, it finds out that the wordpress service requires an external volume. It
tries to locate the volume by issuing podman volume inspect <volume_name>, but
since it doesn't exist, it goes ahead and creates one by issuing
podman volume create <volume_name>:
Next, it checks whether there is a suitable network for deploying the
wordpress service. It doesn't find one, so it creates it and then performs
another check to confirm that the network exists:
Finally, with a suitable network and an external volume in place, Podman Compose
launches the wordpress container:
Since Podman cannot find a wordpress image available locally, it goes ahead
and looks up the unqualified search registry that you configured earlier
(docker.io), then downloads it:
Next, Podman Compose proceeds with processing the instructions for the db
service.
First, it creates its external volume:
It then ensures that there is a suitable network for its deployment:
Finally, it launches the db container:
Just like with the wordpress image, Podman cannot find a mysql:5.7 image
locally, so it goes ahead and obtains it from Docker Hub:
At this point, Podman Compose exits successfully, and everything appears to be launched correctly. Let's go ahead and verify this.
First, try to open localhost:8080 in a browser. You should see the WordPress
installation page:
Now, go back to your terminal and type in:
Indeed, both the wordpress container and the db container are up and
running:
You can also go ahead and explore the list of available images:
The list contains all the necessary images for running MySQL and WordPress:
You can further explore the list of available networks:
Two networks show up:
The podman network is created by default when you install Podman for the first
time. It is used for launching containers when no other network is explicitly
specified. On the other hand, the podman-tutorial_default network is created
by Podman Compose to isolate the containers defined in your docker-compose.yml
from any other containers potentially running on the same system.
Finally, let's check the available volumes:
As expected, two external volumes appear:
You can go ahead and stop the containers:
Podman Compose stops and removes the containers from your system but keeps the network and volumes:
You can verify that no containers are running by typing:
The result shows an empty output, which confirms that all of the containers were stopped and removed.
If you want to remove the volumes as well, you can type in:
To remove the network, type in:
As you can see, the commands are identical to what you would normally use with
Docker and Docker Compose. The only noticeable difference is that instead of
docker and docker-compose, you type in podman and podman-compose.
Stop tailing logs from individual Podman containers. Better Stack uses eBPF to capture kernel-level events and aggregate logs from all your containers—whether you're running Podman, Docker, or both.
Podman is a capable containerization technology that offers a viable alternative to Docker for running container workloads. Whether you choose Podman or Docker depends entirely on your specific needs and preferences.
Podman can do most of the things that Docker can do, with the added benefit of not requiring a daemon running in the background. On top of that, Podman offers some features that Docker does not, such as working with Kubernetes manifest files and organizing individual containers into pods.
The final decision is yours. If you require a more lightweight and secure container management solution, Podman might be a better choice. However, Docker may be the way to go if you prioritize a robust ecosystem with extensive community support. Ultimately, both tools offer powerful containerization capabilities to meet your requirements.
To explore Podman further, consider visiting the official Podman website, exploring its documentation, and joining its growing community.
Thanks for reading!
We use cookies to authenticate users, improve the product user experience, and for personalized ads. Learn more.