0x74 0x68 0x65 0x72 0x65 0x20 0x61 0x72 0x65 0x20 0x31 0x30 0x20 0x74 0x79 0x70 0x65 0x73 0x20 0x6f 0x66 0x20 0x70 0x65 0x6f 0x70 0x6c 0x65 0x74 0x68 0x6f 0x73 0x65 0x20 0x77 0x68 0x6f 0x20 0x75 0x6e 0x64 0x65 0x72 0x73 0x74 0x61 0x6e 0x64 0x20 0x62 0x69 0x6e 0x61 0x72 0x79 0x20 0x61 0x6e 0x64 0x20 0x74 0x68 0x6f 0x73 0x65 0x20 0x77 0x68 0x6f 0x20 0x64 0x6f 0x6e 0x74
01110100 01101000 01100101 01110010 01100101 00100000 01100001 01110010 01100101 00100000 00110001 00110000 00100000 01110100 01111001 01110000 01100101 01110011 0xdeadbeef 0xcafebabe 0x1337
0x74 0x68 0x6f 0x73 0x65 0x20 0x77 0x68 0x6f 0x20 0x75 0x6e 0x64 0x65 0x72 0x73 0x74 0x61 0x6e 0x64 0x20 0x62 0x69 0x6e 0x61 0x72 0x79 0x20 0x61 0x6e 0x64 0x20 0x74 0x68 0x6f 0x73 0x65 0x20 0x77 0x68 0x6f 0x20 0x64 0x6f 0x6e 0x74 0x74 0x68 0x65 0x72 0x65 0x20 0x61 0x72 0x65 0x20 0x31 0x30 0x20 0x74 0x79 0x70 0x65 0x73 0x20 0x6f 0x66 0x20 0x70 0x65 0x6f 0x70 0x6c 0x65
01010111 01100101 00100000 01101000 01100001 01100011 01101011 00100000 01110100 01101000 01100101 00100000 01110000 01101100 01100001 01101110 01100101 01110100 01100110 00110000 01111000 01100110 00110100 01100100 01100101
0x6e 0x65 0x76 0x65 0x72 0x20 0x67 0x6f 0x6e 0x6e 0x61 0x20 0x64 0x72 0x6f 0x70 0x20 0x79 0x6f 0x75 0x72 0x20 0x73 0x68 0x65 0x6c 0x6c 0x20 0x6e 0x65 0x76 0x65 0x72 0x20 0x67 0x6f 0x6e 0x6e 0x61 0x20 0x6b 0x69 0x6c 0x6c 0x20 0x79 0x6f 0x75 0x72 0x20 0x74 0x68 0x72 0x65 0x61 0x64 0x20 0x6e 0x65 0x76 0x65 0x72 0x20 0x67 0x6f 0x6e 0x6e 0x61 0x20 0x6c 0x6f 0x73 0x65 0x20 0x79 0x6f 0x75 0x72 0x20 0x70 0x61 0x63 0x6b 0x65 0x74 0x20 0x61 0x6e 0x64 0x20 0x64 0x65 0x73 0x65 0x72 0x74 0x20 0x79 0x6f 0x75 0x72 0x20 0x71 0x75 0x65 0x75 0x65
Published on

Docker vs Podman: Which One for Your Homelab?

Docker vs Podman: Which One for Your Homelab?

Welcome back! In the previous post we containerized the todo app with Docker and Docker Compose. If you are running this on a personal server or homelab, you may have come across Podman as an alternative and wondered what the actual difference is. This post breaks it down.


What They Have in Common

Both Docker and Podman are container runtimes that implement the OCI (Open Container Initiative) specification. That means:

  • Images built with Docker run on Podman and vice versa
  • Dockerfiles work with both (podman build understands the same syntax)
  • Docker Hub images pull and run identically on both
  • Docker Compose files are compatible with Podman Compose

For the todo app we built, switching from Docker to Podman requires almost no changes.


The Core Difference: The Daemon

Docker uses a persistent background daemon (dockerd) that runs as root. Every docker command you run communicates with that daemon over a Unix socket. The daemon owns all the containers and images on the system.

Podman is daemonless. There is no background process. When you run podman run, it forks a child process directly and that process becomes the container. When the container stops, nothing lingers.


Rootless Containers

Because Docker's daemon runs as root, any user with access to the Docker socket effectively has root on the host. This is a known attack surface. If an attacker can write to the Docker socket, they can mount the host filesystem into a container and escape.

Podman supports rootless containers: containers that run entirely as your regular user, with no root involvement. The container process runs under your UID, and user namespaces handle the mapping between container root and your unprivileged user.

For a homelab where you do not want a compromised container to be able to touch the rest of your system, rootless Podman is a real security improvement.

Docker has experimental rootless mode, but it requires additional setup and is not the default. With Podman, rootless is the default.


Systemd Integration

In a homelab you probably want containers to start on boot, restart on failure, and behave like normal services. With Docker you typically rely on restart: always in Compose or write a separate systemd unit to manage it.

Podman has first-class systemd integration. You can generate a systemd unit file directly from a running container:

podman generate systemd --new --name todo-api > ~/.config/systemd/user/todo-api.service
systemctl --user enable --now todo-api

This creates a proper systemd service that starts the container on login, restarts on failure, and respects systemctl stop. The --user flag means it runs under your user session with no root required.

For server deployments managed by systemd (which is most modern Linux distros), this is cleaner than Docker's approach.


Pods

Podman introduced the concept of a pod borrowed from Kubernetes. A pod is a group of containers that share a network namespace, meaning they can communicate over localhost without any special networking configuration.

Our todo app is a natural fit for a pod. The API and UI share a pod, communicate internally, and only the UI port needs to be exposed externally.

podman pod create --name todo-app -p 3000:80 -p 8000:8000
podman run -d --pod todo-app --name todo-api todo-api:latest
podman run -d --pod todo-app --name todo-ui todo-ui:latest

This mirrors how Kubernetes works. If you ever move from a homelab to a Kubernetes cluster, your mental model transfers directly.


Docker Compose vs Podman Compose

If you want to keep using the docker-compose.yml format we set up in the previous post, Podman Compose is a community project that interprets Compose files using Podman under the hood.

pip install podman-compose
podman-compose up --build

Most Compose syntax is supported. The gaps are mostly around advanced networking and Docker-specific extensions.

Red Hat also maintains Quadlet, a newer way to define containers as systemd unit files with a declarative syntax. Worth looking at if you are running Fedora, RHEL, or any systemd-first distro.


Installing Podman

On most Linux distros:

# Fedora / RHEL / CentOS
sudo dnf install podman

# Ubuntu / Debian
sudo apt install podman

# Arch
sudo pacman -S podman

On macOS, Podman runs a lightweight Linux VM under the hood:

brew install podman
podman machine init
podman machine start

Migrating the Todo App to Podman

Because we used standard OCI images and a Compose file, the migration is minimal. From the repo root:

# Build the images
podman build -f todo-api/Dockerfile -t todo-api:latest .
podman build -f todo-ui/Dockerfile --build-arg VITE_API_URL=http://localhost:8000 -t todo-ui:latest .

# Run with Podman Compose
podman-compose up -d

Or if you want the systemd approach for a server:

# Create a pod
podman pod create --name todo-app -p 8000:8000 -p 3000:80

# Start containers in the pod
podman run -d --pod todo-app \
  --env-file .env \
  -v todo_data:/app/data \
  --name todo-api todo-api:latest

podman run -d --pod todo-app \
  --name todo-ui todo-ui:latest

# Generate systemd units
mkdir -p ~/.config/systemd/user
podman generate systemd --new --files --name todo-api
podman generate systemd --new --files --name todo-ui
mv container-todo-api.service container-todo-ui.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now container-todo-api container-todo-ui

Both containers now start on login, restart on failure, and are managed by systemd like any other service on the box.


When to Stick With Docker

Podman is not universally better. There are real reasons to stay on Docker.

Ecosystem tooling. Docker has been around longer. Tools like Portainer, Watchtower, and many CI platforms have deeper Docker integration. Podman support is improving but not always at parity.

Docker Desktop. On macOS or Windows with a need for a graphical interface, Docker Desktop is more polished than the Podman equivalent. For local dev on a laptop the convenience is real.

Team familiarity. If everyone knows Docker and your CI already uses it, introducing Podman adds overhead with uncertain benefit. The security win from rootless mode matters more on a server than on a developer laptop.

docker compose v2. Docker Compose v2 is now baked into the Docker CLI as docker compose (no hyphen). It is faster and better maintained than the old docker-compose v1, and Podman Compose has not fully caught up on all features.


The Summary

DockerPodman
ArchitectureDaemon-basedDaemonless
Default permissionsRoot daemonRootless
Systemd integrationManualNative
Kubernetes alignmentLimitedPod model matches K8s
Ecosystem / toolingLargerGrowing
Compose supportNative (v2)podman-compose (community)
macOS/Windows GUIDocker Desktop (polished)Podman Desktop (improving)

For a homelab server running Linux with systemd, Podman wins on security and integration. For local development or team environments where the Docker ecosystem matters, Docker is the easier choice. Both are legitimate tools.


Up Next

The next posts move from rolling our own auth to using a dedicated identity provider. We will look at Keycloak, the heavyweight option with enterprise features, and Authentik, the self-hosted alternative that is easier to stand up in a homelab.