Podman: Rootless Containers as a Docker Alternative
AI generated
$
/etc
Linux
Podman: Rootless Containers as a Docker Alternative
Daemonless architecture and real rootless security in production

Podman promises a compatible replacement for Docker while deliberately dropping the root-owned background daemon and enabling containers that run entirely without root privileges. For hosting environments serving multiple customers on one server, that is not a cosmetic detail but a fundamentally different security model that directly shapes attack surface and blast radius.

11 min read Linux Containers Security

1. Why Podman needs no daemon at all

The Docker daemon dockerd runs permanently as a root process and manages every container centrally through a Unix socket. Anyone who can reach that socket effectively gets root privileges on the host, because the daemon itself executes requested actions with full privileges. This architecture grew historically and is convenient, but on shared hosts or build servers it is a substantial risk, since a compromised container process reaching the socket can potentially take over the entire host.

Podman skips this central daemon entirely. Every podman run invocation starts an independent process that creates containers directly through kernel interfaces, typically with conmon as a lightweight monitor process per container and runc or crun as the OCI compliant runtime underneath. If a container process goes away, only that one container is affected, there is no central single point of failure whose crash drags down every running container at once.


# Podman process tree per container instead of one central daemon
ps -ef | grep -E "conmon|crun"

# No socket, no central authority: every call stands on its own
podman run -d --name shop-cache redis:7-alpine
podman ps

2. How rootless mode actually works

In rootless mode, the entire container process tree runs under the UID of a regular, unprivileged user. This is made possible by user namespaces: inside the container, the process appears as root with UID 0, while on the host that same UID is mapped to an unprivileged range assigned via /etc/subuid and /etc/subgid. Root inside the container is therefore a purely logical construct with no real host privileges attached.

Networking, storage, and cgroup management in rootless mode rely on userspace tools instead of kernel privileges: slirp4netns or the more performant pasta handle networking without root rights, and the fuse-overlayfs storage driver replaces the classic overlay driver that would otherwise require root. The cost is a certain performance overhead compared to real kernel mounts, though for most web workloads that overhead barely registers.


# Check the subuid and subgid range assigned to the current user
grep "$USER" /etc/subuid /etc/subgid

# Run a container as an unprivileged user, root inside the container
# stays a process with a mapped, unprivileged UID on the outside
podman run --rm -it alpine id

3. Concrete security benefits over root containers

The most important effect of rootless mode shows up during a container escape: if an attacker manages to break out of the container, they land on the host not as root, but as the unprivileged user Podman was running under. Even a successful escape does not grant host root, only the restricted rights of a regular account, which drastically limits the blast radius.

On multi tenant hosts, as commonly found in Magento hosting with several customers sharing one machine, this model can be used directly for isolation between customers: each customer gets a dedicated Linux user and runs their containers exclusively under that UID. A compromised container belonging to one customer then cannot, by design, read another customer's files or interfere with their containers, because the operating system's own user isolation kicks in long before any container specific mechanism even comes into play.

4. Compatibility with the Docker CLI and Docker images

Podman deliberately implements the same command line syntax as Docker: podman run, podman build, podman ps, and most flags behave identically, so existing scripts often run unmodified. Anyone who wants an especially frictionless switch can simply set alias docker=podman, and many distributions even ship a dedicated podman-docker package that redirects the docker command system wide to Podman.

Images cause no friction either: Podman pulls standard Docker images from the Docker registry or from private registries without any conversion, since both rely on the shared OCI image format. A Dockerfile builds unchanged with podman build, and the resulting image can afterward be pushed to a registry and reused with Docker just the same. One difference remains around Docker Compose: Podman ships its own implementation via podman-compose or native podman compose support, which is not a hundred percent identical to the original in every detail.


# Build an existing Dockerfile unchanged with Podman
podman build -t shop-app:latest .

# Run a Docker Compose file with Podman
podman compose -f docker-compose.yml up -d

5. systemd integration with podman generate systemd

Unlike Docker, which keeps containers alive permanently through its own daemon, Podman deliberately leaves lifecycle management to systemd, the init system already running on most Linux distributions anyway. The podman generate systemd command turns a running container into a matching systemd unit file complete with start, stop, and restart logic, so containers start automatically at boot and get restarted by systemd itself after a crash, with no extra wrapper service involved.

These generated units can be registered both as system wide root services and, which matters especially for rootless mode, as user units under systemctl --user. That means the entire container lifecycle runs inside the session of an unprivileged user, including logging via journalctl and resource limits managed through the cgroups the user manager owns.


# Turn a running container into a systemd unit file
podman generate systemd --new --files --name shop-cache

# Register and activate the unit file for the current user
mkdir -p ~/.config/systemd/user
mv container-shop-cache.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now container-shop-cache.service

6. Quadlet: describing containers declaratively as systemd units

Since Podman 4.4, Quadlet has increasingly replaced generated units: instead of manually starting a container and generating a unit from it afterward, a .container file describes the desired state declaratively, similar to a Kubernetes manifest, and systemd itself generates the matching service unit at runtime. Configuration changes now happen directly in a versionable text file rather than through an imperative generate step after every restart.

Quadlet files live under ~/.config/containers/systemd/ for rootless setups or /etc/containers/systemd/ for root operation, and get translated into regular units automatically by podman-system-generator at systemd startup. For production setups this is the recommended path, since the entire configuration can be versioned in Git and rolled out through configuration management, instead of relying on manually generated unit files that go stale.


# ~/.config/containers/systemd/shop-cache.container
[Unit]
Description=Redis cache for the Magento shop
After=network-online.target

[Container]
Image=docker.io/library/redis:7-alpine
PublishPort=127.0.0.1:6379:6379
Volume=shop-cache-data.volume:/data

[Service]
Restart=always

[Install]
WantedBy=default.target

7. Practical example: running Magento containers rootless

A realistic setup splits a Magento stack across several rootless Podman containers under one dedicated system user UID: PHP FPM, Nginx as a reverse proxy, and Redis for session and cache. Because ports below 1024 cannot be bound by default in rootless mode, Nginx listens internally on a high port, while a root owned, system wide reverse proxy such as another Nginx instance or Traefik terminates the actual port 443 externally and forwards traffic internally to the unprivileged container.

File access between host and container hinges on the UID mapping: since root inside the container maps to an unprivileged host UID, volume mounts need podman unshare chown to bring ownership onto the correctly mapped UID, so that PHP FPM inside the container actually has write access to var and pub/media, without the host user outside the user namespace needing root rights at all.


# Prepare the Magento directory for the rootless container namespace
podman unshare chown -R 82:82 var pub/media

# Start the PHP FPM container rootless with mounted Magento files
podman run -d --name shop-php \
  --userns=keep-id \
  -v "$(pwd):/var/www/html:Z" \
  --network shop-net \
  magento-php-fpm:8.4

8. Pods as bundles of several containers

Beyond the single container, Podman also implements the pod concept, borrowed directly from the Kubernetes unit of the same name: multiple containers inside a pod share one network namespace and therefore localhost, so PHP FPM and Nginx inside the same pod can talk to each other over 127.0.0.1 without needing a separate container network with DNS resolution. Only the pod itself exposes ports externally, individual containers inside it stay invisible from outside.

This model makes a later migration to Kubernetes considerably easier, because a Podman pod structurally matches a Kubernetes pod and can even be exported directly into a Kubernetes manifest with podman generate kube. For smaller hosting setups without full orchestration, a single rootless pod per customer or project remains a pragmatic, well isolated solution regardless.


# Create a pod with a shared network for the Magento stack
podman pod create --name shop-pod -p 127.0.0.1:8080:8080

podman run -d --pod shop-pod --name shop-php magento-php-fpm:8.4
podman run -d --pod shop-pod --name shop-nginx nginx:1.27-alpine

9. Limits of rootless mode and when root containers still make sense

Rootless mode has real limits too: some network features, such as binding low ports directly inside the container, certain VLAN configurations, or macvlan networks with a dedicated MAC address, require genuine kernel privileges and either fail entirely in rootless mode or only work through workarounds. The performance overhead of fuse-overlayfs compared to native kernel mounts also becomes measurable on very IO heavy workloads, for example database containers with high write throughput.

For such cases, running Podman as root remains a valid option that is still noticeably safer than Docker, since even root mode has no permanent, privileged daemon, and each container process tree is managed independently. The pragmatic recommendation for hosting environments is to establish rootless containers as the default and reserve root operation for cases where concrete technical requirements demand it, rather than running everything privileged out of pure habit.

Criterion Docker Podman Rootless Podman Root
Daemon dockerd, permanently as root no daemon, one process tree per container no daemon, one process tree per container
Root rights to start a container required via socket access not required required
Risk on container escape effectively host root possible only rights of the unprivileged user host root possible
Storage driver overlay2, kernel based fuse-overlayfs, userspace overlay2, kernel based
systemd integration via external wrapper tools native via generate systemd or Quadlet native via generate systemd or Quadlet

Mironsoft

Server administration, Docker hosts, and performance tuning

Linux servers nobody on the team really understands anymore?

We handle setup, hardening, and performance tuning of Linux servers and Docker hosts for Magento deployments, documented and traceable instead of grown and unclear.

Server Audit

Review the existing server configuration for security gaps and performance bottlenecks.

Docker Host Setup

Set up and secure production-ready Docker environments for Magento cleanly.

Monitoring & Tuning

Measure resource usage and tune systemd, kernel, and services with purpose.

10. Summary

Podman Rootless

Architecture

No central daemon, one process tree per container

Security gain

A container escape lands as an unprivileged user, not as root

Compatibility

Docker CLI, Dockerfile, and OCI images work unchanged

Operations

podman generate systemd or Quadlet for autostart and restart

11. FAQ: Podman Rootless

1Is Podman a full replacement for Docker?
For most use cases, yes. The CLI is largely identical, and Dockerfiles and images work unchanged. Differences mostly show up in specific Docker Compose features and in certain network modes that are restricted under rootless mode.
2Why is the missing daemon a security advantage?
With Docker, anyone with access to the daemon socket effectively gets root rights on the host, because the daemon itself runs as root. Podman has no central, privileged process that could serve as a single attack point for the whole host.
3What does root inside the container actually mean in rootless mode?
Root inside the container is a purely logical construct within a user namespace. On the host, that same UID maps to an unprivileged range assigned via subuid and subgid, so no real host root rights exist.
4Can I use existing Docker Compose files with Podman?
Largely yes, via podman compose or podman-compose. Most Compose directives behave identically, though individual detail features such as certain build contexts or network drivers can differ and should be tested before production use.
5How do I start a Podman container automatically at boot?
podman generate systemd creates a matching systemd unit that can be registered either as a root service or as a user unit under systemctl --user. Alternatively, a Quadlet .container file describes the container declaratively, and systemd generates the unit itself.
6What is the difference between generate systemd and Quadlet?
generate systemd turns an already running container into a unit file once, following an imperative approach. Quadlet describes the desired state declaratively in a text file, from which systemd generates the unit itself at runtime, which is easier to version and roll out automatically.
7Why can a rootless container not bind port 80 directly?
Ports below 1024 have traditionally been reserved for privileged processes on Linux. A rootless container runs without those privileges and therefore binds a high port instead, which a root owned reverse proxy forwards externally.
8Is Podman suitable for multi tenant hosting with several customers?
Yes, that is exactly what rootless mode is well suited for: each customer gets a dedicated Linux user and runs their containers exclusively under that UID, so the operating system's own user isolation adds extra protection between customers.
9What is a Podman pod and what is it used for?
A pod bundles several containers under a shared network namespace, letting them communicate over localhost. The concept structurally matches a Kubernetes pod and makes a later migration to an orchestration platform easier.
10When should I still run Podman in root mode?
Whenever concrete technical requirements demand it, such as special network modes like macvlan with a dedicated MAC address, or very IO heavy workloads where the fuse-overlayfs overhead becomes noticeable. Even in root mode, Podman stays safer than Docker thanks to the missing daemon.