Struggling with Linux container security and the inherent root privileges often associated with Docker? Many tech-savvy professionals worry about granting developers root access via the docker group. This article dives deep into Podman’s game-changing rootless architecture, offering a powerful alternative to traditional daemon-based container engines. Discover how Podman eliminates the central daemon, maps container root to unprivileged host users, and integrates seamlessly with systemd via Quadlet. Learn to deploy containers and pods with enhanced security, safeguarding your Linux systems without compromising on functionality.
Anyone in the docker group can effectively become root because the Docker daemon runs as root and obeys commands sent through its Unix socket. Podman takes a different approach by eliminating the central daemon and supporting rootless containers, pods, and Quadlet services managed through systemd.
Imagine a developer needs to run containers on a shared build server, so you add their account to the docker group. That seemingly harmless change can give them root-level access to the host.
For example, they could run docker run -v /:/host and access or modify host files because the Docker daemon (dockerd) runs with root privileges and executes requests received through its socket.
Docker’s own documentation explicitly warns that membership in the docker group grants root-level privileges, making it a significant concern in security-conscious environments. While Docker Engine does provide a rootless mode, it is not the default and is not always enabled on production servers.
Podman removes the daemon entirely, runs containers as child processes of the user who starts them, and uses Linux user namespaces to map container root to an unprivileged host user.
It also supports Kubernetes-style pods and integrates with systemd through Quadlet, making it possible to manage containers as native services.
In this guide, we’ll look at how Podman’s rootless architecture works, how it compares with Docker from a security perspective, and how to run containers, pods, and systemd-managed services without giving users unnecessary root-level access.
Demystifying Container Runtimes: Podman’s Daemonless vs. Docker’s Daemon-Centric Approach
Both Docker Engine and Podman run the same OCI-compliant container images. The important difference is what sits between your command and the container, how those components are managed, and which user owns them.
Understanding the Core Architecture: Podman’s Rootless Design
With Docker Engine, the docker command acts as a client. It sends API requests through /var/run/docker.sock to the root-owned dockerd daemon. Docker then uses containerd and an OCI runtime such as runc to create and manage containers.
By default, this entire control path is tied to the root-owned Docker daemon, which is why access to its socket effectively provides root-level control over the host.
If dockerd stops, Docker normally loses control of its containers as well. Docker’s live-restore feature can keep containers running while the daemon is unavailable, but it does not remove the daemon from the architecture.
Podman takes a different approach. There is no central daemon waiting for client requests. The podman command directly creates and starts the container, using conmon (container monitor) to keep watch over the container process and an OCI runtime, usually crun, to create the container.
Because each container has its own monitor process, Podman does not require a shared daemon to remain running after the container has started. In rootless mode, these processes can run under the regular user’s account rather than through a root-owned service.
This daemonless design also fits naturally with systemd and Quadlet, allowing containers and pods to be managed as user or system services without introducing a permanently running container daemon.
Getting Started with Rootless Containers: Installing and Configuring Podman
Now that you know what changes under the hood, let’s install Podman on the lab server (192.168.122.248) as the regular tecmint user with sudo access. There is no daemon to enable afterwards.
On Rocky Linux, AlmaLinux, and RHEL, Podman is available through the AppStream repository, so dnf can install it directly:
sudo dnf install -y podmanOn Ubuntu and Debian, refresh the package index first so apt installs the latest packaged version:
sudo apt update
sudo apt install -y podmanThe exact Podman version depends on the distribution and release. For example, Ubuntu 26.04 LTS currently packages Podman 5.7.0, while the upstream Podman documentation is already in the 6.1 series.
The commands and rootless workflow used in this guide are compatible with Podman 5 and 6.
Essential Configuration: Subordinate User IDs for Linux Namespaces
Rootless Podman uses Linux user namespaces to map container users to unprivileged host users. For this mapping to work normally, the user needs subordinate UID and GID ranges in /etc/subuid and /etc/subgid. Podman checks these files when creating rootless containers.
Check whether the tecmint account already has the required ranges:
grep tecmint /etc/subuid /etc/subgidYou should see output similar to:
/etc/subuid:tecmint:100000:65536
/etc/subgid:tecmint:100000:65536If either file has no entry for tecmint, add a subordinate UID and GID range:
sudo usermod --add-subuids 100000-165535 \
--add-subgids 100000-165535 tecmintThen migrate the user’s existing Podman state:
podman system migrateThe migration step is important when changing subordinate ID ranges because rootless Podman can keep a pause process and existing user namespace alive with the previous mappings. podman system migrate stops the relevant containers and pause process so the new /etc/subuid and /etc/subgid mappings can take effect.
At this point, tecmint has everything needed to start using Podman in rootless mode, without belonging to the privileged docker group.
Podman in Action: Practical Rootless Container Workloads and Systemd Integration
With Podman installed, the clearest way to see the difference is to run workloads you would normally run with Docker and then inspect which host user owns the resulting processes.
Demonstrating Linux Container Security: Rootless Process Ownership
Start an Alpine container as tecmint without sudo, then look at its process from the host side:
podman run -d --name sleeper docker.io/library/alpine sleep 600
ps -o user,pid,cmd -C sleepYou should see output similar to:
USER PID CMD
tecmint 4821 sleep 600Now check the user from inside the container:
podman exec sleeper whoamiThe command prints:
rootThis is the important distinction: root inside a rootless container is not root on the host. Podman uses a user namespace to map the container’s root user to an unprivileged host identity. From the host’s perspective, the sleep process belongs to tecmint.
For comparison, start the same workload through Docker’s root-owned daemon:
sudo docker run -d --name sleeper-docker docker.io/library/alpine sleep 600
ps -o user,pid,cmd -C sleepThe Docker container’s process appears as owned by root on the host.
Rootless Podman therefore significantly reduces the impact of a container compromise or escape: an attacker breaking out of the container does not automatically gain host-root privileges. They initially inherit the privileges of the host user running the container. That still matters, so the tecmint account should have only the permissions it actually needs.
Clean up both test containers before continuing:
podman rm -f sleeper
sudo docker rm -f sleeper-dockerOrchestrating Multiple Containers: Grouping a Web Server and Database in a Pod
The first example ran a single container, but real applications often consist of multiple containers that need to communicate. Docker typically handles this with a user-defined network, while Podman can place related containers in a pod that shares a network namespace.
Create a pod and publish port 8080 on the host to port 80 inside the pod:
podman pod create --name webstack -p 8080:80Now start MariaDB and Nginx inside the same pod:
podman run -d --pod webstack --name db -e MARIADB_ROOT_PASSWORD=ChangeMe123 docker.io/library/mariadb:11
podman run -d --pod webstack --name web docker.io/library/nginx:alpineCheck the pod:
podman pod psYou should see output similar to:
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
3f9c2a71b0de webstack Running 40 seconds ago 8a1d5e2c4f90 3Seeing three containers even though you started only two is normal. Podman creates an additional infra container for the pod. It holds the pod’s shared network namespace open so that the other containers can share it.
Because the containers in a pod share the same network namespace, they can reach each other through localhost. For example, the Nginx container could connect to MariaDB at:
127.0.0.1:3306This example does not configure Nginx to use MariaDB, so the connection is only demonstrating the networking model. In a real application, the web container would need application-level configuration and credentials to communicate with the database.
Podman can also generate Kubernetes-compatible YAML from the pod:
podman kube generate webstack > webstack.yamlYou can then use that YAML to recreate the workload:
podman kube play webstack.yamlThis gives Podman a useful bridge between local containers and Kubernetes-style workload definitions. Docker provides other mechanisms for defining multi-container applications, such as Compose, but it does not use the same Podman kube generate and kube play workflow for exporting and recreating pods.
Managing Containers with Systemd: Leveraging Quadlet for Persistence
The pod from Example 2 won’t return after a reboot, because no daemon restarts it. Podman hands that job to systemd through Quadlet, which turns a .container file in your home directory into a systemd unit:
mkdir -p ~/.config/containers/systemdOn Ubuntu and Debian, create the unit file with nano:
nano ~/.config/containers/systemd/webapp.containerOn Rocky Linux, AlmaLinux, and RHEL, minimal installs ship with vi rather than nano, so use vi there:
vi ~/.config/containers/systemd/webapp.containerThen add the following configuration to the file:
# ~/.config/containers/systemd/webapp.container
[Unit]
Description=Nginx web container managed by Quadlet
[Container]
Image=docker.io/library/nginx:alpine
ContainerName=webapp
PublishPort=8081:80
AutoUpdate=registry
[Service]
Restart=always
[Install]
WantedBy=default.targetHere:
Imageuses the full registry path, whichAutoUpdaterequires.ContainerNamereplaces the default namesystemd-webapp.PublishPortuses port 8081 to avoid clashing with the Example 2 pod.AutoUpdate=registrylets podman auto-update pull newer images, enhancing container lifecycle management.Restart=alwaysrestarts the container whenever it stops.WantedBy=default.targetstarts it with your user session, replacingsystemctl enable.
Save the file and exit the editor. Since a rootless user’s systemd instance stops at logout, enable lingering first, then load and start the generated unit:
sudo loginctl enable-linger tecmint
systemctl --user daemon-reload
systemctl --user start webapp.serviceIf start fails with Unit webapp.service not found, Quadlet rejected the file, and /usr/libexec/podman/quadlet -dryrun -user prints the exact error. Once it starts, test a failure by killing the container and checking the service a few seconds later:
podman kill webapp
systemctl --user status webapp.service --no-pagerOutput:
● webapp.service - Nginx web container managed by Quadlet
Loaded: loaded (/home/tecmint/.config/containers/systemd/webapp.container; generated)
Active: active (running) since Tue 2026-10-06 11:42:17 IST; 4s agoThe 4s ago timestamp shows systemd started a fresh container without root.
Podman vs. Docker: Choosing the Right Container Platform for Your Linux Environment
After those three examples, the choice usually depends on your environment. Docker Engine remains easier when your team relies on Docker Desktop, Docker Swarm, or BuildKit-specific features, since those tools are built around the Docker daemon.
Podman fits better on shared servers, RHEL-based fleets, and anywhere an audit asks who can become root. The podman-docker package provides a docker command that calls Podman, and tools expecting a Docker socket, such as CI runners, can use Podman’s API socket after systemctl --user enable --now podman.socket. This allows for seamless migration of existing Docker workflows to a more secure Linux container orchestration setup.
One limitation catches most people. Rootless containers can’t bind ports below 1024, so -p 80:80 fails with rootlessport cannot expose privileged port 80. Either publish a high port behind a reverse proxy, or lower the limit with sudo sysctl net.ipv4.ip_unprivileged_port_start=80 and save it in /etc/sysctl.d/. Note that lowering this limit might have broader system security implications.
Conclusion: Embracing Secure Containerization with Podman on Linux
Podman offers a robust and secure alternative for container management on Linux, running the same images as Docker Engine but without a root daemon. By mapping container root to a normal user and integrating seamlessly with systemd through Quadlet, Podman addresses critical security concerns prevalent in traditional Docker setups. For shared servers, development environments, and any system prioritizing enhanced Linux security best practices, adopting Podman’s rootless containers significantly mitigates risk without sacrificing functionality or altering daily commands dramatically.
If you’re working toward Red Hat certification, our RHCSA (RHEL 10 / EX200) course on Pro tecmint covers the RHEL skills rootless containers depend on, including systemd and SELinux.
We’d love to hear how your team runs containers today. Have you moved production workloads to rootless Podman, or is Swarm keeping you on Docker? Share your Quadlet files or migration errors in the comments.
FAQ
<h4>Question 1: What is the primary security advantage of Podman's rootless containers over Docker?</h4>
<p><strong>Answer 1:</strong> Podman eliminates the need for a root-owned daemon, meaning <code>root</code> inside a container is mapped to an unprivileged user on the host via <a href='https://example.com/linux-namespaces'>Linux user namespaces</a>. This significantly reduces the blast radius of a container escape, as an attacker would only gain the privileges of the unprivileged host user, not full root access. This is a critical feature for enhancing <a href='https://example.com/linux-container-security'>Linux container security</a>, especially in multi-user environments.</p>
<h4>Question 2: How does Podman manage containers without a central daemon?</h4>
<p><strong>Answer 2:</strong> Unlike Docker, Podman does not rely on a persistent central daemon (<code>dockerd</code>). Instead, the <code>podman</code> command directly spawns containers as child processes of the user, each monitored by <code>conmon</code>. This daemonless architecture not only enhances security but also integrates naturally with <code>systemd</code> through Quadlet, allowing containers to be managed as standard user or system services. A recent example of its power is its growing adoption in modern CI/CD pipelines, allowing for secure, daemonless builds.</p>
<h4>Question 3: Can Podman replace Docker in existing CI/CD pipelines or for Kubernetes development?</h4>
<p><strong>Answer 3:</strong> Yes, largely. The <code>podman-docker</code> package provides a Docker-compatible CLI, and Podman offers a Docker API socket that tools expecting Docker can connect to (<code>systemctl --user enable --now podman.socket</code>). Furthermore, Podman's <code>podman kube generate</code> command can convert pods into Kubernetes-compatible YAML, bridging local container development with Kubernetes deployments. This makes Podman an excellent choice for <a href='https://example.com/kubernetes-development-linux'>Kubernetes development on Linux</a> and seamless integration into CI/CD workflows.</p>

