← Back to Blog

Podman or Docker? We Started the Same Windows 11 VM Under Both on One Dedicated Server

· 14 min read Two plain amber container shapes on a table in front of a dark server rack, a thin amber line sweeping past and a small blank window panel glowing on the wall.

Our Windows 11 desktop runs inside a container on a rented Linux server, and Docker starts it. The setup is in Windows 11 in a Browser on Your Own Linux Server. Anyone who has worked on Red Hat or Fedora will ask the obvious question: why Docker, when Podman needs no root daemon and turns containers into ordinary systemd services?

Before answering, we checked whether the same Windows container would even start under Podman. On October 14, 2026 we ran three Podman versions on that server. Each ran inside a disposable container, so nothing touched the running desktop:

  • Podman 5.7.0, installed with apt install podman on Ubuntu 26.04.
  • Podman 5.8.7, from the project's official image.
  • A 6.2 development build.

All three started the same dockurr/windows 6.06 image with the flags Docker uses, and the container noticed. Its first log line read "Starting Windows for Podman v6.06".

This post covers how to use Podman, what differs from Docker in practice, and how to choose between them.

Download the Podman Windows 11 Server Guide in Markdown. It has the install steps, Quadlet unit files that start Windows and Guacamole at boot, the first-boot password handled as a Podman secret, a Compose-to-Quadlet key map and the checks. Read it before running anything on a server that holds data.

What we tested, and what happened

Scroll to compare columns and rows when the table extends beyond the box.

Check 5.7.0 5.8.7 6.2 dev
Windows 11, as root [1] Started Started Started
Six Quadlet files [2] OK OK OK
Password as a secret [3] Yes Yes Not run
Windows, rootless [4] Not run Failed Not run

The columns are Podman 5.7.0 from the Ubuntu 26.04 archive, 5.8.7 from the project's official image, and a 6.2 development build.

  1. Windows 11, as root: each run reached the Windows download. On the 6.2 build, Microsoft refused the automated download and the container switched to a fallback link.
  2. Six Quadlet files: each version generated services from the guide's six unit files with no errors.
  3. Password as a secret: the first-boot password reached the container as an environment variable.
  4. Windows, rootless: the run failed at the container's first /proc check.

We stopped every Windows run once the download began. None of these runs was a full Windows install under Podman. The install itself happens inside QEMU, and QEMU sees the same /dev/kvm device under either engine. The rootless failure deserves care too. Our test ran Podman inside another container, and the error came from the container reading its own process information. We have not tried rootless Windows on a bare host, so treat it as unproven, not as broken.

What Podman is

Podman's manual calls it "a fully featured container engine that is a simple daemonless tool". It runs the same OCI images from the same registries as Docker. Its command line is deliberately close to Docker's; the same page says "Simply put: alias docker=podman."

There is no background service waiting for commands. For each container, a small program called conmon sits between Podman and the OCI runtime (crun or runc). It holds the container's console and writes its log. Image builds go through Buildah, which Podman "uses internally to create container images".

On Ubuntu 26.04, apt install podman gave us Podman 5.7.0 together with crun 1.21, netavark 1.16.1, aardvark-dns 1.16.0, conmon 2.1.13, Buildah 1.42.1 and passt, the package that provides pasta. The upstream project is further ahead: Podman 6.1.4 shipped on October 12, 2026. A distribution archive often trails the project by a major version, and that matters below.

How to use Podman: the commands you will type

Most of a Docker habit carries over unchanged. These are the places it does not:

Scroll to compare columns and rows when the table extends beyond the box.

Task Docker Podman
Everyday use docker run podman run
Pull an image docker pull nginx podman pull docker.io/library/nginx
Build docker build podman build
Start at boot Restart policy Quadlet unit
One-container secrets Swarm only podman secret create
Several services docker compose Quadlet, a pod, or podman compose
Shared localhost Network namespace podman pod create
Kubernetes YAML Not built in podman kube generate
Clean up docker system prune podman system prune
API for other tools docker.sock podman.socket

What to know about those rows:

  • Everyday use: ps, logs and exec work the same way, with the same everyday flags.
  • Pull an image: use the full name; the next paragraph explains why.
  • Build: Buildah reads a Containerfile or a Dockerfile.
  • Start at boot: Docker's daemon applies the restart policy. Podman uses a Quadlet unit file or podman-restart.service; both are covered below, along with secrets.
  • Several services: podman compose needs a separate provider.
  • Kubernetes YAML: podman kube generate exports a pod, and podman kube play runs a manifest.
  • Clean up: volumes stay unless you add --volumes.
  • API for other tools: podman.socket is a Docker-compatible API that stands in for /var/run/docker.sock.

Use full image names. Podman can search several registries, and with more than one configured, a short name like nginx is ambiguous. The registries configuration explains that in its default enforcing mode Podman prompts in a terminal and fails in a script. Writing docker.io/library/nginx removes the question. The dockur README already uses docker.io/dockurr/windows.

Root and rootless Podman are separate worlds. sudo podman images and podman images read different stores: the system store, and your own under ~/.local/share/containers. An image pulled in one does not appear in the other. In our test, the rootless store sat exactly where the storage documentation says it would.

The real difference is the daemon

Docker's post-install guide is blunt: "The Docker daemon always runs as the root user", and "The docker group grants root-level privileges to the user." Anyone who can run docker can start a container that mounts the host's disk. On our server, the everyday admin account is deliberately not in the docker group, so every Docker command goes through sudo and leaves a trace.

Docker can run without root. Its rootless mode runs "both the daemon and the container" without root privileges. It is opt-in, and it is a second daemon to manage per user.

Podman starts from the other end. A normal user runs it rootless by default, and the project's README says "rootless containers will never have more privileges than the user that launched them". The rootless tutorial explains the mechanism.

  • ID ranges. Your account gets a range of subordinate user IDs in /etc/subuid. Our admin account's range starts at 100000 and is 65,536 IDs long.
  • The mapping. Inside a rootless container, root is your own user. Container user 1 is host user 100000, and so on up the range. A process that escapes the container lands as you, not as root.

Rootless has limits worth knowing before you plan around it. The project lists them:

  • No ports below 1024 by default. Lowering net.ipv4.ip_unprivileged_port_start changes that for the whole host.
  • Devices reached through a group need an extra flag. --group-add keep-groups passes your supplementary groups into the container, "Currently only available with the crun OCI runtime." This matters for /dev/kvm, which belongs to the kvm group. Our admin account is not a member, so rootless Windows on our server would need that change first.
  • The network stack copies the host's address. Rootless networking uses pasta, and because "pasta copies the IP address of the main interface, connections to that IP from containers do not work."

The networking ground moved in 2026

Podman 5.0, released in March 2024, made pasta the default for rootless networking. Podman 6.0 went further in June 2026. Its release notes list the removals:

  • slirp4netns ("Please use Pasta instead").
  • CNI networking, in favor of Netavark.
  • Hosts that still run cgroups v1.
  • iptables ("Please use nftables instead").

Ubuntu 26.04's Podman 5.7.0 predates all of this, so nothing breaks today. A newer package, a backport or the next LTS will bring it in. Write for it now: skip slirp4netns options in any guide you copy from, and confirm the host runs cgroups v2. Our server reported v2.

Name resolution needs a named network on either engine. Podman's networking tutorial says the default podman network "does not support dns resolution because of backwards compatibility with Docker". Docker's own documentation says the same of its default bridge: only "User-defined bridges provide automatic DNS resolution between containers." The guide creates one named network, win11, so Guacamole can reach Windows by name.

Services at boot: restart policies versus Quadlet

With Docker, the daemon starts containers at boot according to their restart policy. Podman has no daemon to do that. It ships podman-restart.service, which "restarts containers after a system reboot". That works, but the better tool is Quadlet.

Quadlet turns a small unit file into a systemd service. Introduced in Podman 4.4 in February 2023, it reads .container, .pod, .network and .volume files from /etc/containers/systemd/ and generates services at boot or on systemctl daemon-reload. It is documented in podman-systemd.unit(5). The older podman generate systemd command is deprecated in its favor. The heart of our Windows unit looks like this:

[Container]
ContainerName=win11
Image=docker.io/dockurr/windows:6.06
Network=win11.network
Environment=VERSION=11
Environment=CPU_CORES=8
Environment=RAM_SIZE=16G
Secret=win11-password,type=env,target=PASSWORD
AddDevice=/dev/kvm
AddDevice=/dev/net/tun
AddCapability=NET_ADMIN
PublishPort=127.0.0.1:8006:8006
PublishPort=127.0.0.1:3389:3389/tcp
Volume=/srv/win11/storage:/storage
StopTimeout=120

[Install]
WantedBy=multi-user.target

Run through Quadlet's dry-run mode, that file became a service whose start command is a plain podman run --name win11 ... --stop-timeout 120 --network win11 --device /dev/kvm .... It also gained a dependency on the win11 network's own service. You get what any systemd service gets: systemctl status, journalctl logs, restart rules and ordering against other units.

A pod starts its members. The guide runs Guacamole as one pod of three containers. The generated pod service carried Wants= lines for all three, so starting the pod starts the set. The web app's unit adds Requires= on the other two, so it does not start without them.

Secrets and the first-boot password

dockur installs Windows unattended, so it needs the account password at first boot. In the Docker setup we kept it in a separate env file and loaded it with Compose's format: raw. Without that, Compose treats a $ in the password as a variable.

Docker has a secrets feature, but Docker secrets "are only available to swarm services, not to standalone containers". Compose secrets work without Swarm, but they arrive as a file "mounted as a file in /run/secrets/" inside the container, and dockur reads an environment variable.

Podman's secrets work for a single container and can be delivered either way. In our test, podman secret create took the value from standard input, and --secret win11-password,type=env,target=PASSWORD handed it to the container as PASSWORD. Know what the default protects. The default file driver keeps the secret in "a read-protected file"; the pass driver keeps it GPG-encrypted. Either way, delete the secret and the unit's Secret= line once Windows reaches the desktop.

One more difference, measured rather than read. A test value containing $ and a double quote came through Podman's --env-file exactly as written.

Compose: keep it, wrap it, or convert it

podman compose is "a thin wrapper around an external compose provider such as docker-compose or podman-compose". With neither installed, ours listed the three places it had looked and stopped. You have three routes:

  • Point Docker Compose at Podman's API. podman system service offers "a compatibility layer offering support for the Docker v1.40 API". Our test socket answered Docker's _ping check. The podman-compose README notes the cost: you can use "unmodified docker-compose" against the socket, "but in this case you lose the process-model".
  • Use podman-compose. It is a Python implementation of the Compose spec on Podman, at version 1.6.0 since June 2026.
  • Convert to Quadlet. For a few long-lived services, this is the Podman-native answer, and most Compose keys have a direct counterpart: devices becomes AddDevice=, cap_add becomes AddCapability=, ports becomes PublishPort=, and stop_grace_period becomes StopTimeout=. The guide has the full map.

What is specific to Windows 11

dockur/windows lists its requirement as "Docker or Podman on a Linux host with KVM support". It warns that "Docker Desktop on Linux, macOS, and Windows 10 does not currently provide KVM access to containers and is therefore not supported." On a server you want Docker Engine or Podman, not Docker Desktop.

Out of the box it gives Windows 2 cores, 4 GB of memory and a 64 GB disk; environment variables raise all three.

Run the Windows container as root. It needs the KVM device, a TUN device and NET_ADMIN to set up its own network address translation. Rootless means adding your account to the kvm group and passing --group-add keep-groups. dockur's base project, qemus/qemu, also notes that with "user-mode networking (for example when running under Podman)" every extra port must be listed in USER_PORTS. That is a second network layer to reason about, for a container that already holds a full operating system.

Microsoft will refuse repeated downloads. The 6.2 run was the fourth Windows download request from our server's address that day. It logged "Microsoft blocked the automated download request based on your IP address", tried another method, was refused again, and then switched to a static link. If you test repeatedly, download the ISO once. dockur accepts a URL in VERSION or a local file bound to /custom.iso.

The license is still yours to sort out. The README is explicit: "You are responsible for ensuring that you have a valid Windows license and that your use complies with Microsoft's licensing terms."

Desktop licensing, briefly

On a Linux server you run Docker Engine, whose source code is Apache 2.0 licensed. The commercial terms people worry about apply to Docker Desktop. Docker's license page, last updated August 26, 2026, says Desktop is free for:

  • small businesses, defined as "fewer than 250 employees AND less than $10 million in annual revenue";
  • personal use;
  • education;
  • non-commercial open source projects.

Larger organizations and government entities need a paid subscription. Podman and Podman Desktop are both Apache 2.0. On a Mac or a Windows laptop, Podman also runs a Linux virtual machine. The machine documentation explains why: "containers are Linux". Podman 6 dropped support for Intel Macs and Windows 10.

How to choose

Scroll to compare columns and rows when the table extends beyond the box.

Your situation Better fit
Compose stack in production [1] Docker
Services as systemd units [2] Podman
Per-user services [3] Podman, rootless
Tools needing docker.sock [4] Docker, or Podman's socket
A Swarm cluster [5] Docker
Toward Kubernetes [6] Podman
Desktop above the free tier [7] Podman Desktop
Windows VM on a server [8] Either, as root
  1. Compose stack in production: a working stack has nothing to convert, and Podman's socket is the fallback.
  2. Services as systemd units: Quadlet units give you journald logs and systemd ordering.
  3. Per-user services: when several people need their own services on one server, there is no shared root daemon to hand out.
  4. Tools needing docker.sock: Podman's Docker-compatible API covers the common calls.
  5. A Swarm cluster: Swarm is built into Docker Engine.
  6. Toward Kubernetes: podman kube generate and podman kube play give you manifests to start from.
  7. Desktop above the free tier: at a company above Docker's free tier, the choice is Podman Desktop or a paid Docker plan.
  8. Windows VM on a server: both run the same image, with the same flags and the same KVM device.

We are staying on Docker on this server, for now. It runs seventeen containers defined in Compose files. Those include the Windows desktop, Guacamole, the AI tools and a couple of test environments. Converting them would mean re-testing each one, with no failure today that Podman fixes.

The risk Podman removes, a root daemon, we contain another way: the admin account stays out of the docker group, and every published port binds to loopback behind Cloudflare Access.

We would choose Podman for any of these:

  • a new server built for one job;
  • a server shared by people who each need their own services;
  • the day we want every service managed by systemd rather than by Compose.

The guide is written so that switch is an afternoon of unit files, not a rebuild. Kubernetes is a different question; Kubernetes, Docker or Podman for Full Windows in a Container covers it.

What we did not test

  • A complete Windows install under Podman. Every run stopped at the download.
  • Rootless Windows on a bare host.
  • The Guacamole pod running end to end. Its units were generated and checked, not started.
  • The 6.1.4 release itself. We used a 6.2 development build, whose image shipped with a broken storage configuration line that we removed before testing.

Fact-check notes and sources

Related reading

This post is informational, not security-consulting or licensing advice. Check Microsoft's license terms for running Windows in a virtual machine, and Docker's terms for Docker Desktop. Mentions of Podman, Docker, Red Hat, Fedora, Ubuntu, Microsoft, Apache Guacamole, Cloudflare and dockur are nominative fair use. No affiliation is implied.

← Back to Blog

Accessibility Options

Text Size
High Contrast
Reduce Motion
Reading Guide
Link Highlighting
Accessibility Statement

J.A. Watte is committed to ensuring digital accessibility for people with disabilities. This site conforms to WCAG 2.1 and 2.2 Level AA guidelines.

Measures Taken

  • Semantic HTML with proper heading hierarchy
  • ARIA labels and roles for interactive components
  • Color contrast ratios meeting WCAG AA (4.5:1)
  • Full keyboard navigation support
  • Skip navigation link
  • Visible focus indicators (3:1 contrast)
  • 44px minimum touch/click targets
  • Dark/light theme with system preference detection
  • Responsive design for all devices
  • Reduced motion support (CSS + toggle)
  • Text size customization (14px–20px)
  • Print stylesheet

Feedback

Contact: jwatte.com/contact

Full Accessibility Statement • Privacy Policy

Last updated: April 2026