DevOps & Infrastructure
Podman
Docker & Containers
DevOps
Security
Cloud Native

Podman vs Docker: The Enterprise Migration Guide

Docker still dominates developer laptops, but enterprise container platforms are going multi-runtime. A pragmatic look at Podman's daemonless, rootless architecture, what it changes for CI/CD and compliance, and how to migrate without breaking your pipelines.

H
Hrishi Digital Solutions
15 min read
Podman vs Docker: The Enterprise Migration Guide - Hero Image

For most of the last decade, "containers" and "Docker" were the same word. That is no longer true in production. The container image format is an open standard, the runtime that executes it is a swappable component, and a growing number of engineering organisations run one runtime on developer laptops and a different one in CI and production.

Podman is the most common second runtime. It runs the same OCI images, speaks a nearly identical CLI, and takes a fundamentally different architectural position: no central daemon, no root privileges required, no background service consuming memory when you are not building anything.

This article is written for engineering leads and architects deciding whether that difference is worth a migration. It covers what actually changed in the ecosystem, what daemonless and rootless buy you in security and compliance terms, where Podman genuinely wins, where Docker still wins, and the specific commands and gotchas involved in moving.

1. The Container Landscape Shift: Why Docker Isn't the Only Choice

Two things happened that decoupled Docker the company from containers the technology.

Kubernetes removed dockershim. In Kubernetes v1.24, released in May 2022, the dockershim adapter was deleted from the kubelet. Kubernetes had never talked to Docker natively; it used a shim to translate its Container Runtime Interface (CRI) calls into Docker Engine calls. Removing it meant clusters standardised on CRI-native runtimes, overwhelmingly containerd and CRI-O. The practical result is that on a modern managed Kubernetes cluster, whether AKS, EKS or GKE, Docker Engine is not running. Your production containers have not been executed by Docker for years.

That matters because it broke the assumption that your local tool and your production runtime should be the same thing. Once they are already different, the argument for keeping Docker specifically on the workstation becomes a question of developer ergonomics rather than production parity.

Docker Desktop became a licensed product. Docker Desktop requires a paid subscription (Pro, Team or Business) for commercial use in organisations with more than 250 employees or more than US$10 million in annual revenue. Docker Engine on Linux remains free and Apache 2.0 licensed; it is specifically the Desktop application on macOS and Windows that carries per-seat cost. For a 400-person engineering organisation, that is a recurring five-figure line item for a tool that, post-dockershim, no longer matches production anyway.

The usage data shows a market that is consolidating around Docker for developers while quietly diversifying underneath. In the 2025 Stack Overflow Developer Survey, 71.1% of all respondents reported using Docker in the past year, rising to 73.8% among professional developers. Podman sat at 11.1% and 10.9% respectively. Docker's developer mindshare is not in question.

What the developer survey does not capture is the platform and CI layer, where adoption looks different. Vendor and analyst estimates circulating through 2026 put Podman's share of enterprise container runtimes closer to 19% to 23%, and suggest roughly a third of engineering organisations now deliberately run more than one runtime. Treat those specific figures as directional rather than precise, because they come from vendor surveys with undisclosed methodology, not primary research. The direction, however, is consistent with what the architecture change predicts: Docker stays on the laptop, something else runs the pipeline.

2. Architectural Blueprint: Daemonless and Rootless Security

This is the substantive difference, and it is worth understanding precisely rather than as a slogan.

Docker's client-server model

The docker CLI is a thin client. It sends REST calls over a Unix socket at /var/run/docker.sock to dockerd, a long-lived daemon that by default runs as root. Every container is a child of that daemon. The daemon owns image storage, networking, and the container lifecycle.

Two consequences follow:

  • The daemon is a single point of failure. If dockerd dies or is restarted mid-upgrade, every container it parents is affected.
  • Socket access is root access. Anyone who can write to /var/run/docker.sock can ask the daemon to start a container that bind-mounts the host filesystem at / with --privileged. That is a complete host compromise in one API call, with no exploit required. It is the intended behaviour of the API.

This is why membership of the docker group is functionally equivalent to passwordless sudo, and why mounting the Docker socket into a container (a common pattern for CI runners and for tools that need to build images) is such a persistent finding in security reviews.

Podman's fork-exec model

Podman has no daemon. When you run podman run, the CLI process forks, sets up namespaces and cgroups itself, and execs conmon, which supervises the OCI runtime (crun or runc) directly. The container is a child of your user session, not of a privileged system service.

In rootless mode, the whole tree runs as your unprivileged user. Podman uses user_namespaces(7) with subordinate UID and GID ranges from /etc/subuid and /etc/subgid to map container UID 0 to your real unprivileged UID on the host. A process that believes it is root inside the container is your ordinary user account outside it. A container escape lands the attacker in an unprivileged session, not on a root shell.

There is no socket that grants root, because there is no privileged daemon listening on one. This removes the socket-hijacking and privilege-escalation path entirely rather than mitigating it with permissions.

Worth stating plainly for balance: Docker also supports rootless mode, and has since version 20.10. The difference is defaults and friction. Docker rootless is an opt-in deployment mode that requires deliberate setup and carries its own caveats; Podman rootless is what you get when you install the package and run a command as a normal user. In an organisation of any size, the secure default is the one that actually ships.

Why compliance auditors care

For teams working against SOC 2, PCI-DSS, ISO 27001, or the ACSC Essential Eight, the daemonless model maps cleanly onto controls you are already being assessed on:

  • Least privilege. Developers and CI runners build and run containers without root and without membership of a root-equivalent group.
  • Restricting administrative privileges (Essential Eight mitigation strategy). Removing the docker group removes a standing privilege escalation path from every developer workstation and build agent.
  • Auditability. Container processes appear in the host process tree under the invoking user, and are attributable through normal auditd and systemd journal records rather than being anonymised behind a shared daemon.

If your organisation handles Australian government or health data, this is the argument that tends to land. It is much easier to evidence "no user on this host has a path to root through the container tooling" than to write a compensating control around the Docker socket.

3. Enterprise Advantages: Production and CI/CD

Licensing and resource overhead

Podman is Apache 2.0 licensed with no per-seat commercial terms, on Linux, macOS and Windows. Podman Desktop, the GUI, is likewise open source. For workstation fleets above the Docker Desktop threshold, this is a direct and easily quantified saving.

The resource story is less discussed but matters on developer machines. Docker Desktop runs a Linux VM and a daemon continuously, typically holding 1.5GB to 2GB of RAM whether or not you have containers running. Podman on Linux consumes nothing when idle, because there is no resident process. On macOS and Windows, Podman still needs a VM (podman machine), but it can be stopped and started per project rather than running at login.

Kubernetes alignment

Podman understands Kubernetes YAML natively, which is a genuinely different workflow rather than a convenience:

# Run a multi-container pod locally from a real Kubernetes manifest
podman kube play ./k8s/deployment.yaml

# Generate a manifest from containers you built interactively
podman kube generate my-container > k8s/deployment.yaml

# Tear it back down
podman kube down ./k8s/deployment.yaml

Podman's pod concept is the same shared-namespace grouping Kubernetes uses, so a pod that runs locally behaves the way it will in the cluster. Developers can validate manifests, init containers, shared volumes and inter-container networking without a local cluster, and without maintaining a parallel Compose file that drifts from the real deployment descriptor. (podman play kube remains available as an alias for the older command form.)

CI/CD without Docker-in-Docker

This is where the architecture pays for itself most obviously.

Building images inside a containerised CI runner traditionally means one of two bad options: mount the host Docker socket into the job, or run Docker-in-Docker with --privileged. The first gives every pipeline job root on the build host. The second disables most of the kernel isolation that made the container a boundary in the first place, and it means a compromised dependency in any build script has a straightforward path out.

Because Podman needs no daemon and no privileged socket, a CI job can build and run containers as an unprivileged user inside an unprivileged container:

# GitHub Actions: rootless image build, no privileged mode, no socket mount
jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: quay.io/podman/stable:latest
      options: --user podman
    steps:
      - uses: actions/checkout@v4
      - name: Build and push
        run: |
          podman build -t registry.example.com/app:${GITHUB_SHA} .
          podman push registry.example.com/app:${GITHUB_SHA}

The same pattern applies to GitLab CI and Tekton. Buildah, Podman's sibling tool, goes further and builds OCI images with no runtime at all, which is the usual choice for pipelines that only ever build and push.

4. Feature Matrix: Podman vs Docker

Dimension Docker Podman
Architecture Client-server; persistent dockerd daemon Daemonless; fork-exec, containers are children of the user session
Default privilege Daemon runs as root; rootless is opt-in Rootless by default for non-root users
Attack surface /var/run/docker.sock is root-equivalent No privileged socket; escape lands in an unprivileged namespace
Licensing Engine free (Apache 2.0); Desktop requires paid subscription above 250 staff or US$10M revenue Apache 2.0 throughout, including Podman Desktop, no per-seat cost
Idle overhead Daemon (and VM on macOS/Windows) resident continuously Zero on Linux when idle; VM startable on demand elsewhere
Kubernetes integration Compose-centric; no native manifest support Native pods, podman kube play and kube generate
Service management Daemon-managed restart policies Native systemd via Quadlet; containers are real system units
Compose support First-class, native Docker Compose v2 works against the Podman socket; podman-compose also available
Swarm / built-in orchestration Docker Swarm included Not supported; Kubernetes is the intended target
Ecosystem maturity Largest tooling and documentation base Strong on Linux; smaller ecosystem, some third-party tools assume a daemon

5. Adoption Patterns and Real-World Use Cases

Podman's enterprise traction is concentrated where the security model is a procurement requirement rather than a preference. Financial services, healthcare, defence and government sectors move first, partly because Red Hat ships Podman as the default container tool in RHEL and partly because "no root-equivalent group on production hosts" is a control those organisations are already required to evidence.

The dominant pattern is not replacement. It is deliberate hybrid:

  • Developer workstations: Docker Desktop or Podman Desktop, chosen for onboarding speed and familiarity. New starters know Docker; fighting that has a real cost.
  • CI/CD pipelines: Podman or Buildah, because this is where privileged Docker-in-Docker creates the sharpest risk and where the licensing saving on build agents is unambiguous.
  • Linux production hosts: Podman with Quadlet for workloads that are not on Kubernetes, giving you systemd-managed containers with standard logging, restart and dependency handling.
  • Kubernetes clusters: containerd or CRI-O, decided by your cloud provider rather than by you.

This is a sensible allocation, not a compromise. The OCI image built on a laptop runs identically under all four. Standardising the image is what matters; standardising the tool that launches it was always a weaker requirement than it appeared.

If you are already planning a broader platform change, sequencing matters. Container runtime migration pairs naturally with work you are doing anyway, such as a move to Azure or a cloud replatform or an ASP.NET Core modernisation, because you are touching the deployment pipeline in both cases.

6. Practical Migration Guide: Moving from Docker to Podman

CLI compatibility, and its limits

Podman deliberately implements the Docker CLI surface. The standard first step is real and it works:

alias docker=podman

Most day-to-day commands (build, run, ps, logs, exec, pull, push, images, rm) behave identically. Where this breaks is anything that assumes a daemon: scripts that poke /var/run/docker.sock directly, tools that call the Docker Engine API, Swarm commands, and a handful of third-party integrations that shell out expecting Docker-specific output. Inventory those before you commit to a date.

Enabling the Podman socket for Docker Compose v2

Podman ships a Docker-API-compatible socket, which lets the real docker compose binary drive Podman. Enable it per user:

# Start the user-level API socket and keep it enabled across reboots
systemctl --user enable --now podman.socket

# Allow the socket to survive logout on servers
loginctl enable-linger "$USER"

# Point Docker-compatible clients at it
export DOCKER_HOST="unix://${XDG_RUNTIME_DIR}/podman/podman.sock"

# Verify
podman --remote info | head
docker compose up -d

Note the distinction from Docker: this socket runs as your unprivileged user and grants exactly your privileges, not root.

Generating systemd units with Quadlet

For Linux servers, this is the feature that makes Podman a better production fit than Docker rather than merely an equivalent one. Quadlet, merged into Podman 4.4, lets you declare a container as a systemd unit and have systemd generate and manage the service.

Create ~/.config/containers/systemd/app.container (or /etc/containers/systemd/ for system-wide):

[Unit]
Description=Line of business API
After=network-online.target

[Container]
Image=registry.example.com/app:1.4.2
PublishPort=8080:8080
Environment=ASPNETCORE_ENVIRONMENT=Production
Volume=app-data:/var/lib/app:Z
HealthCmd=curl -f http://localhost:8080/health || exit 1
HealthInterval=30s

[Service]
Restart=always

[Install]
WantedBy=default.target

Then:

systemctl --user daemon-reload
systemctl --user start app.service
journalctl --user -u app.service -f

The container is now a first-class systemd service with standard dependency ordering, restart policy, journal integration and health checking. Note that the older podman generate systemd command is deprecated. It still receives urgent bug fixes and has no announced removal date, but it gets no new features, and Quadlet is the supported path for new work. Do not build a migration around the deprecated command.

Common gotchas

Three issues account for most of the friction, and all three are consequences of rootless operation rather than bugs.

Ports below 1024. An unprivileged user cannot bind privileged ports, so podman run -p 80:80 fails. Either publish a high port and front it with a reverse proxy (the right answer for production), or lower the threshold host-wide:

echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee /etc/sysctl.d/99-podman.conf
sudo sysctl --system

Volume permissions and UID mapping. Files written by a rootless container land on the host owned by a subordinate UID, not by you, so they can look unreadable. Use --userns=keep-id to map your UID straight through, and podman unshare to operate on those files from inside the user namespace:

podman run --userns=keep-id -v "$PWD/data:/data:Z" myimage
podman unshare chown -R 1000:1000 ./data

The :Z suffix applies the SELinux label the container needs, which is mandatory on RHEL, Fedora and derivatives and harmless elsewhere.

Image storage is per user. Rootless images live in ~/.local/share/containers, so images pulled as your user are invisible to root and to other users. Build agents that switch users mid-pipeline will appear to lose their cache. Decide early which user owns the build.

7. Strategic Recommendation

Podman is not a straight upgrade, and treating it as one is how migrations go badly. It is a different set of trade-offs that suits some parts of a delivery pipeline much better than others.

For CTOs and engineering leads, the defensible position looks like this:

  1. Do not rip out Docker Desktop first. It is the highest-friction, lowest-value change. Developer familiarity is worth real money.
  2. Start with CI/CD. Replacing privileged Docker-in-Docker with rootless Podman or Buildah removes your sharpest container security exposure, costs nothing in licensing, and is invisible to developers if the pipeline keeps passing.
  3. Use Quadlet for non-Kubernetes Linux workloads. If you are running containers under a hand-written systemd unit or a restart policy today, Quadlet is a clear improvement in operability.
  4. Standardise the image, not the tool. Enforce OCI images, a single registry, and reproducible builds. That is the contract that matters; which binary launches the image locally is a preference.
  5. Revisit Docker Desktop licensing at renewal, not before. Once CI runs on Podman and your developers have seen Podman Desktop, the workstation conversation becomes a straightforward cost comparison rather than a change-management exercise.

If your organisation is subject to Essential Eight assessment, SOC 2, or government security requirements, the rootless argument is likely to be decisive on its own. If you are a small team on Docker Desktop under the licensing threshold with no compliance driver, there is no urgency here and switching for its own sake is not a good use of a sprint.

Getting help with the assessment

We work with Australian businesses and government agencies on container platform decisions, CI/CD security, and Linux production hosting, including the migration work described above. If you want a review of your current container pipeline and a costed migration path, get in touch for a consultation or read more about our technical consulting and cloud security review services.

Related reading: Deploying n8n on Azure for production walks through a containerised production deployment end to end, and cloud migration reality in 2025 covers the planning side of platform change.

Podman
Docker & Containers
DevOps
Security
Cloud Native
H

Hrishi Digital Solutions

Expert digital solutions provider specializing in modern web development, cloud architecture, and digital transformation.

Contact Us →