Aspire drives an engine
Aspire is an orchestrator, not a container engine. When your AppHost declares a container resource - a Postgres, a Redis, a custom image - Aspire does not start it directly. It connects to a container engine running on your machine and asks it to do the work. That engine is either Docker or Podman.
If you want the conceptual picture of what containers even are inside an Aspire app - dependencies it runs for you versus the images your own services ship as - read Aspire and containers first. This article is the layer below that: the actual engine, how Aspire reaches it, and what changes depending on which one you pick.
The reason both work is a shared contract. Aspire speaks the Docker-compatible API - a well-known socket that accepts a fixed set of HTTP commands ("pull this image," "start this container"). Docker exposes that socket natively. Podman exposes a compatible one too. As long as something on your machine answers on that socket, Aspire can drive it.
Docker, the default
Docker is the runtime most Aspire setups assume, and the one most guides are written against. On a developer machine that usually means Docker Desktop (on macOS or Windows) or Docker Engine (on Linux). Either way, a background daemon - dockerd - listens on the Docker socket, and Aspire connects straight to it.
With Docker running, there is nothing to configure in Aspire - it finds the socket and uses it. The one wrinkle worth knowing is licensing: Docker Desktop requires a paid subscription for larger companies under Docker's terms. That single fact is what pushes a lot of teams to look at Podman, even when Docker is working fine technically.
Podman, the alternative
Podman is a fully supported engine for Aspire, and a natural fit for teams that want a daemonless, rootless runtime or that want to avoid Docker Desktop licensing. Aspire drives Podman through the same podman command line you would use yourself, so the main requirement is simply that Podman is installed and reachable. On macOS and Windows that means starting Podman's managed Linux VM; on Linux it runs natively.
On macOS and Windows, Podman runs containers inside a small managed Linux VM - podman machine - just as Docker Desktop does. On Linux it runs directly on the host. Once podman info succeeds and the executable is on your PATH, Podman looks like Docker to Aspire and your AppHost runs unchanged. (Some Docker-API clients such as Testcontainers also want Podman's compatible socket enabled with systemctl --user enable --now podman.socket, but Aspire itself drives the CLI.)
How Aspire finds the engine
Aspire decides which engine to use in two steps: it auto-detects what is installed, and you can override that with a single setting.
- Auto-detection on PATH. Aspire searches your system PATH for a
dockerorpodmanexecutable and uses what it finds. If both are installed, Docker is preferred by default. - An explicit override. Set
ASPIRE_CONTAINER_RUNTIMEtopodman(ordocker) to force the choice. Older preview docs and many third-party guides use the earlierDOTNET_ASPIRE_CONTAINER_RUNTIMEname - if one does nothing on a current build, try the unprefixed form.
In the common case you set nothing - one engine is installed, Aspire finds it on PATH, and uses it. The override matters when both engines are present (Docker wins unless you say otherwise) or when you want to be explicit. The PATH detail is the one that bites in WSL: if podman works in your shell but Aspire reports "container runtime could not be found," the usual cause is that podman is a shell alias rather than a real executable on PATH.
Switching between them
Because both engines speak the same API and Aspire targets that API, switching is a machine-level change, not an application-level one. Your AppHost - the AddPostgres, the AddContainer, the references - does not change at all. What changes is which engine is running and which one Aspire is pointed at.
This is the real payoff of Aspire's engine-agnostic design: a developer on Docker Desktop and a teammate on rootless Podman can work in the same repository, run the same aspire run, and never touch the AppHost. The engine is an environment detail, not a property of the project.
Differences that bite
"Drop-in compatible" is true ninety-five percent of the time. The remaining five percent is worth knowing in advance, because it is where a working Docker setup behaves differently under Podman.
- Privileged ports. Rootless Podman cannot bind host ports below 1024 without extra configuration, because that is a privileged operation. If a container resource maps to port 80 or 443 on the host, either map a high port instead or adjust the system's unprivileged-port setting.
- Volume permissions. Rootless containers map your user into the container via user namespaces, so files written to a bind-mounted folder can end up owned by an unexpected UID. Named volumes (as opposed to host bind mounts) sidestep most of this.
- The runtime has to be discoverable. The most common Podman-with-Aspire failure is Aspire not finding the engine: the
podman machinewas never started, or - especially in WSL -podmanexists only as a shell alias. Aspire searches your PATH for a real executable, so use a binary or a symlink, not an alias. - Image defaults. Podman can be configured with multiple registries and does not assume Docker Hub as aggressively. Fully qualifying image names (
docker.io/library/postgres) avoids ambiguity when an image fails to resolve.
None of these are dealbreakers - each has a one-line fix - but they are the issues that turn "it works on my machine" into a half-hour of confusion if you do not know to look for them.
CI and the team
The engine choice is not only a laptop decision. It also shows up wherever your Aspire app's containers run during automated testing. Many CI environments ship Docker preinstalled, which makes Docker the path of least resistance there. Others - particularly Red Hat-based and security-hardened runners - default to Podman, where rootless execution is an advantage for running untrusted build steps.
The practical guidance is to make the engine explicit in shared environments rather than relying on whatever happens to be present. Pin the runtime setting in your CI configuration, ensure the socket is available before the Aspire step runs, and you get the same behavior on every machine. Because the AppHost is engine-agnostic, this is purely environment setup - there is no second code path to maintain for "the Podman build."
Which should you use?
For Aspire specifically, both are first-class - so the decision is about your constraints, not about Aspire.
Reach for Docker when you want the path with the least setup and the most tutorials, when your team already runs Docker Desktop, or when your CI and tooling assume it. It remains the default for a reason: it is well-trodden and everything is documented against it.
Reach for Podman when Docker Desktop licensing is a factor for your organization, when you want rootless and daemonless containers for security, or when you are on a Red Hat-family distribution where Podman is the native, pre-installed engine. The trade-off is a little more first-time setup - the socket, occasionally the port and volume details from above - in exchange for an engine many teams find a better long-term fit.
The reassuring part is that this is a low-stakes choice. Aspire's whole design here is that the engine sits behind a standard interface, so you can start on one and move to the other without rewriting anything. Pick the one that fits your environment today, and know the door to the other stays open.