How to Use Podman: The Beginner's Guide to Rootless Containers

CodexFuse Team · 2026-10-31T09:01:47.605Z · 14 min read

Learn how to use Podman: install it on Linux, Mac, or Windows, run your first rootless container, dodge the low-port and SELinux traps, and boot services with Quadlet.

Learning how to use Podman is the fastest way to run containers the secure way: no root daemon sitting on your server, no sudo needed for everyday commands, and a command line that feels like Docker because the verbs are the same. This Podman tutorial is written for beginners who want one complete arc — install, first rootless container, the port rule that trips everyone up, volumes without SELinux pain, Quadlet for boot-time services, and the honest Docker-migration notes nobody writes down.

Podman is a free, open-source container engine maintained by Red Hat that runs OCI containers without a constantly-running root daemon. Every podman command is a short-lived process that starts, does its job, and exits — there is no dockerd equivalent to babysit, patch, or lock down. If you already know Docker, the Docker beginner guide concepts carry straight over, because podman run, podman ps, and podman build work the way your fingers expect.

What is Podman, and how is it different from Docker?

Podman is a daemonless, rootless container engine: your containers run as your user, not as a privileged background service. The practical difference from Docker comes down to three design choices. First, there is no daemon — the podman CLI talks directly to the container runtime and exits, so a crashed container engine cannot take your other containers down with it. Second, rootless is the default: a normal user runs containers inside a user namespace, so a container escape lands in an unprivileged account instead of root. Third, Podman has native concepts Docker never had: pods (groups of containers sharing localhost, Kubernetes-style), first-class systemd integration through Quadlet, and a Docker-compatible API socket so Docker-based tools keep working.

None of this means Docker is obsolete. Docker still owns the biggest tutorial ecosystem, the smoothest desktop experience, and Docker Swarm and Compose workflows your team may already run. The honest split: Docker is the friendliest learning surface, Podman is the safer default on a Linux server you own — especially one you manage over SSH with no console access.

How do I install Podman on Linux, Mac, and Windows?

On Linux, Podman installs from your distro's normal repositories — no third-party repo dance required on modern releases. On a Mac or Windows machine, install Podman and then create a Podman machine, a small Linux virtual machine that actually hosts your containers, because containers are Linux at heart.

PlatformInstall commandNotes
Fedora / RHEL / Rockysudo dnf -y install podmanShips current Podman 6.x on Fedora 45
Ubuntu / Debiansudo apt update && sudo apt install podmanAlso install uidmap if your minimal image lacks it
Arch Linuxsudo pacman -S podmanMan pages for podman-rootless(7) included
macOSbrew install podmanThen podman machine init + podman machine start; podman.io recommends the official installer over Homebrew for stability
WindowsOfficial Podman installer (MSI)Uses a WSLv2 backend; Podman 6's installer offers a user-scoped install with no admin rights needed

Verify with podman --version and then run podman info. That one command answers the questions behind most beginner problems: whether you are rootless, which storage driver you use, which network backend is active, whether SELinux is on, and your cgroups version. On a fresh Linux install you should see rootless mode, the overlay driver, Netavark networking, and cgroups v2.

How do I run my first container with Podman?

Before the first -p flag, learn the one rule that surprises every Docker convert: a rootless container cannot publish a host port below 1024. On a default system, -p 80:80 fails with a permission error — it is a kernel restriction, not a Podman bug. Use a high port like 8080 for your first run; the port rule gets its own section below with the real fixes.

  1. Confirm the install. podman --version should print a 5.x or 6.x version. If you are on macOS or Windows, start your machine first: podman machine start.
  2. Pull and run a throwaway container. podman run --rm docker.io/library/hello-world downloads the tiny hello-world image and proves image pulling works. The --rm flag removes the container when it exits.
  3. Run a real web server on a rootless-safe port. podman run -d --name web -p 8080:80 docker.io/library/nginx starts nginx in the background and maps your host port 8080 to the container's port 80. The -d flag detaches; --name web gives the container a friendly name.
  4. Check it is alive. podman ps lists running containers, and curl localhost:8080 should return nginx's welcome page. podman logs web shows the server's output without attaching to it.
  5. Stop and clean up. podman stop web halts it gracefully and podman rm web removes it. Containers you do not remove linger in podman ps -a and hold their names.

That five-step arc is the whole Podman loop for daily use: pull, run, inspect, stop, remove. Everything else in this guide — rootless setup, volumes, Quadlet, compose — is about making that loop production-worthy.

Why can't my rootless container bind to port 80?

Rootless containers cannot bind host ports below 1024 because the Linux kernel reserves those ports for processes with the CAP_NET_BIND_SERVICE capability, and a rootless container deliberately does not have it. When you try podman run -p 80:80 as a normal user, you get a clear refusal — recent Podman even tells you to lower net.ipv4.ip_unprivileged_port_start or pick a larger port.

You have three honest fixes, in order of preference. The cleanest is to publish a high port (-p 8080:80) and put a reverse proxy or firewall redirect in front of it — this is how most self-hosters run web apps anyway. The second is to lower the kernel limit with sudo sysctl net.ipv4.ip_unprivileged_port_start=80 (and make it permanent in /etc/sysctl.d/), accepting that any local user can then bind those ports. The third is to run just that one container as root with sudo podman run, accepting that it uses root's separate container store. Pick the proxy route unless you have a reason not to.

How do I set up rootless Podman correctly?

On modern distros rootless Podman works out of the box; you verify it rather than build it. Check that your user has a UID/GID range assigned in /etc/subuid and /etc/subgid — you should see a line like yourname:100000:65536, meaning the kernel can map 65,536 subordinate IDs into your containers. If the entry is missing, add it with sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER and then run podman system migrate.

Rootless works through Linux user namespaces: inside the container your process believes it is root (UID 0), while on the host it is plainly your unprivileged user plus the mapped subuid range. Your images and containers live under ~/.local/share/containers — completely separate from root's store, which is why an image you pulled as yourself seems to "disappear" under sudo podman. Two commands pay for themselves here: podman unshare, which drops you into your container user namespace for debugging file ownership, and podman info --format, which confirms rootless mode before you chase phantom bugs.

How do I manage images, containers, and volumes with Podman?

Image and container management uses the Docker-shaped verbs you already know, pointed at Podman's own storage. podman search nginx queries registries, podman pull docker.io/library/nginx downloads, podman images lists what is local, podman rmi deletes. Use full registry paths when it matters — Podman resolves short names through your configured registries, and explicit docker.io/… or quay.io/… avoids ambiguity.

Volumes have one trap that bites on Fedora, RHEL, and other SELinux-enforcing distros: a bind mount without a label flag gives you a silent "permission denied" inside the container. Append :Z to the volume flag (-v ./data:/data:Z) so Podman relabels the content for container use. The uppercase :Z means "this volume is private to this container"; lowercase :z means "shared between containers" — pick :Z unless you know you need sharing. Named volumes (podman volume create pgdata) sidestep the whole issue and are the safer default for databases.

For everyday container hygiene, lean on four commands: podman ps -a to see everything including the stopped ones, podman exec -it web /bin/sh to get a shell inside a running container, podman inspect web for the full JSON truth about a container, and podman system prune when your disk fills with dangling images. These are the same verbs any Docker tutorial teaches — the muscle memory transfers intact.

What is Quadlet, and how do I start containers with systemd?

Quadlet is Podman's built-in way to run containers as systemd services: you describe the container in a small unit file, and systemd starts it at boot, restarts it on failure, and logs it like any other service. It is the current, supported path — the older podman generate systemd command that generated long ExecStart lines is legacy and should not be used for new setups.

A rootless user writes a .container file in ~/.config/containers/systemd/. Here is a complete, minimal one for the nginx container from earlier:

[Unit]
Description=My nginx web server
After=network-online.target

[Container]
Image=docker.io/library/nginx
PublishPort=8080:80
Volume=webdata:/usr/share/nginx/html:Z

[Service]
Restart=always

[Install]
WantedBy=default.target

Then activate it with three commands:

systemctl --user daemon-reload
systemctl --user start web.container
systemctl --user enable web.container

Quadlet reads the file at boot (and on every daemon-reload) and generates the real systemd service for you — systemctl --user status web shows it running. Quadlet units come in several flavors beyond .container: .volume ensures a named volume exists, .network creates a Podman network, .kube deploys from Kubernetes YAML, .pod defines a pod, and .build builds an image from a Containerfile. All normal systemd options — dependencies, resource limits, timers — work inside these files because everything outside the Podman-specific section passes straight through to systemd.

How do I use Podman on macOS and Windows?

On macOS and Windows, podman machine init followed by podman machine start creates and boots a lightweight Linux VM that actually runs your containers. The podman CLI on your host then talks to the Podman service inside that VM, so every command in this guide works unchanged — the VM is an implementation detail you can mostly ignore.

Two details matter on a Mac. First, the default VM uses Apple's native Hypervisor Framework, and you can size it at creation time (podman machine init --cpus 2 --memory 4096 --disk-size 100). Second, podman machine ssh drops you into the VM for debugging — useful because commands like podman unshare are unavailable on the remote client. On Windows the machine is backed by WSLv2; Podman 6's installer even offers a user-scoped install that needs no admin rights. If you want a graphical view of the same engine, Podman Desktop is the official GUI — the Portainer beginner guide covers the browser-based alternative that also manages Podman.

How do I move my Docker workflow to Podman?

Most Docker commands work verbatim under Podman — change the binary name and keep your flags, because CLI compatibility is a deliberate design goal. Compose files are the one workflow with a decision attached: install the standalone podman-compose (for example brew install podman-compose on a Mac, or your distro package on Linux), which reads docker-compose.yml files directly, or point the classic docker-compose binary at Podman's Docker-compatible API socket. There is no Docker Swarm in Podman; pods and Quadlet are the intended replacements for simple orchestration, and podman play kube runs a Kubernetes YAML file as a pod for workloads heading to a real cluster.

Two migration aids, with one caution. First, Podman listens on a Docker-compatible socket, so Docker-based tooling and SDKs keep working against it — this is why DOCKER_HOST tricks exist in migration guides. Second, the famous alias docker=podman is a comfort blanket for your fingers during the switch, never the default you teach on a fresh Linux install: typed muscle memory is fine, but scripts and teammates should call podman explicitly so the rootless, daemonless behavior is never a surprise.

Podman vs Docker: which one should I choose?

Neither wins outright — they win different rooms. This table is the decision, not a popularity vote.

CriterionPodmanDocker
ArchitectureDaemonless — each command is a short-lived processClient talks to the dockerd daemon
Default privilegeRootless for normal users via user namespacesRootful daemon by default (rootless mode is opt-in)
Boot-time servicesNative via Quadlet systemd unitsRestart policies on the daemon
Multi-container unitsPods — containers sharing localhost, Kubernetes-styleCompose networks; pods do not exist
Compose filespodman-compose or docker-compose via socketNative docker compose
Kubernetes pathpodman play kube runs K8s YAML as a podKind/minikube, separate tooling
macOS / WindowsPodman machine VM (CLI-first)Docker Desktop (GUI-first)
Learning resourcesGrowing; Red Hat docs are excellentThe largest container tutorial ecosystem on earth
Best fitLinux servers you own; security-first shops; systemd shopsLaptops; teams already standardized; Swarm users

A practical rule that survives contact with reality: learn container concepts with the Docker guide if you are brand new, then run your own Linux servers on Podman rootless with Quadlet. And if your workload lives on a VPS, the server it runs on matters as much as the engine — see what is VPS hosting for picking the machine underneath.

What does Podman 6 change for beginners?

Podman 6.0.0, released in June 2026, is a modernization release: it permanently removes the legacy stack that had been deprecated since Podman 4. The removals change what you install alongside Podman and which old tutorials you can trust.

What changedPodman 5.x (legacy)Podman 6 (current)
Container networkingCNI pluginsNetavark (sole backend)
Rootless networkingslirp4netnsPasta
Firewall backendiptablesnftables (required)
State databaseBoltDBSQLite
Cgroupsv1 supportedv2 required
Companion versionsMixedBuildah 1.44, Skopeo 1.23, Netavark/Aardvark 2.0

The beginner takeaway is simple: never copy networking or storage configs from pre-2026 Podman guides, and treat any tutorial still installing CNI plugins or slirp4netns as stale. Podman 6 also dropped support for Intel Macs and Windows 10, patched CVE-2026-57231, and gave Quadlet a major overhaul including REST API support. If your distro still ships Podman 5.8, that is a fine place to learn — just know the 6.x networking stack is where everything is heading.

What are the beginner mistakes to avoid?

MistakeSymptomFix
Publishing port 80 rootlesspermission denied / rootlessport cannot expose privileged portUse a high port (8080) behind a proxy, or lower net.ipv4.ip_unprivileged_port_start
Bind mount without :Z on SELinuxSilent "permission denied" inside the container-v ./data:/data:Z (or use a named volume)
Mixing sudo podman with rootlessImages "disappear"; two separate storesStay rootless for daily use; root and user stores live apart by design
alias docker=podman as the defaultScripts and teammates hit surprising behaviorUse the alias for your fingers only; write podman in scripts
Following pre-2026 networking guidesInstructions to install CNI or slirp4netnsPodman 6 removed them — Netavark + Pasta + nftables is the stack
Using podman generate systemdLong generated units, no Quadlet featuresWrite a .container Quadlet file; the generate command is legacy

Sources

  • Official Podman documentation — Getting Started with Podman, podman-run man page, Quadlet systemd unit reference, and Podman installation instructions (docs.podman.io, podman.io; fetched 2026-10-31)
  • "Introducing Podman v6.0.0" — official Podman blog announcement, June 2026 (blog.podman.io)
  • Linuxiac — "Podman 6.0 Lands with Breaking Changes, AMD GPUs Support," June 2026 (linuxiac.com)
  • how2shout — "Podman 6.0 Is Here, and It Rips Out a Lot of Legacy Networking," June 2026 (how2shout.com)
  • ComputingForGeeks — "Podman Quadlet on Fedora 45 / 44: systemd Containers," October 2026 (computingforgeeks.com)
  • tutorials.technology — "Podman Rootless Containers: Docker Without Root or Daemon (2026)," May 2026
  • dargslan.com — "Podman Complete Guide: Rootless Containers 2026," March 2026
  • podman-rootless(7) man page — Arch Linux and Debian manpages (man.archlinux.org, manpages.debian.org)
  • linuxoperatingsystem.net — "Podman Command: 9 Essential Examples and Rootless Mistakes," October 2026
  • podman-desktop.io — "Podman: from Docker networks to pods, kube play, and going rootless," September 2026
  • palomanel.dev — "Switching to Podman," October 2026

Frequently asked questions

Is Podman a drop-in replacement for Docker?

For everyday CLI use, yes — run, ps, build, pull, and exec work the same, and Podman even serves a Docker-compatible API socket. The gaps are Docker Swarm (Podman uses pods and Quadlet instead) and native docker compose (use podman-compose or point docker-compose at Podman's socket). Try your existing commands with the binary name swapped before assuming anything is missing.

Do I need sudo to run Podman?

No — that is the entire point. A normal user runs rootless containers with no elevation, and your containers, images, and volumes live in your own store under ~/.local/share/containers. Only reach for sudo podman when you explicitly want a rootful container, and remember it uses root's completely separate store.

Can I run my docker-compose files with Podman?

Yes. The standalone podman-compose tool reads docker-compose.yml files directly and is the simplest path. Alternatively, the classic docker-compose binary works against Podman's Docker-compatible socket. For new multi-service setups on a server, consider describing each service as a Quadlet .container unit instead — systemd then handles boot order and restarts.

Why does Podman refuse to publish port 80?

Because your container is rootless and the kernel will not let unprivileged processes bind ports below 1024. Publish a high port like 8080 and put a reverse proxy in front, lower net.ipv4.ip_unprivileged_port_start via sysctl if you accept the tradeoff, or run just that container as root. The error message itself usually suggests the sysctl.

Should I learn Docker or Podman first?

Learn container concepts with whichever has the tutorial you like — a Docker guide for beginners is the gentler on-ramp because the ecosystem is enormous. Then switch your own Linux servers to Podman rootless with Quadlet, where the security model and systemd integration earn their keep. The commands you learned transfer one for one.

Featured on Twelve Tools Launch Llama Tools Launch your MCP