Podman and Docker Rootless in DDEV
TL;DR: DDEV now supports Podman and Docker Rootless in DDEV HEAD (coming in v1.25.0). Jump to setup instructions: Linux/WSL2 · macOS · Windows
Note: This support is experimental. Report issues on the DDEV issue tracker.
Table of Contents
- Understanding Docker and Podman
- Key aim: Linux and WSL2 users
- macOS
- Windows
- Running Multiple Container Runtimes
- Switching Runtimes with DDEV
- Which Runtime Should You Choose?
- The Journey to Podman Support
- Supporting DDEV Development
- Conclusion
Understanding Docker and Podman
Open Source Alternatives to Docker Desktop
A common misconception is that Podman is the only open-source alternative to Docker Desktop. This is not true. There are several fully open-source alternatives available on every platform:
- Docker Engine - The original open-source Docker, free to use
- Rancher Desktop - Open source container management with choice of dockerd or containerd
- Lima - Linux virtual machines
- Colima - Container runtime with minimal setup (built on Lima)
- Podman Desktop - GUI for Podman with Docker compatibility
All of these work with DDEV. The main reason to choose Podman specifically is if your organization forbids Docker entirely or if you want rootless operation by default.
Why Choose Podman?
Podman is rootless by default, making it the simplest option for secure container environments. Traditional Docker requires root daemons, which can be a security concern in corporate environments with strict policies. (Note that DDEV is targeted at local development, where there are few risks of specialized attacks using this vector anyway.)
Podman’s rootless approach runs the daemon without elevated privileges:
- No root daemon on the system, only a rootless daemon in userspace
- Container processes cannot access root-owned files
- Reduced attack surface if a container is compromised
While DDEV already runs containers as unprivileged users, Podman eliminates the need for a root daemon entirely.
Why Choose Docker Rootless?
Docker Rootless provides the same security benefits as Podman Rootless while maintaining full Docker compatibility. It runs the daemon without root privileges, offering:
- No root daemon on the system
- Container processes cannot access root-owned files
- Reduced attack surface if a container is compromised
Unlike Podman which is rootless by default, Docker Rootless requires special setup to enable. Choose this option if you want to stay with Docker but need rootless security.
Key aim: Linux and WSL2 users
The primary focus for this article is Linux and WSL2 (we have test coverage for Linux only for now). Most features and configurations are well-tested on these platforms.
Installing Podman
Install Podman using your distribution’s package manager. See the official Podman installation guide for Linux.
Note: Some distributions may have outdated Podman versions. This is the case with Ubuntu 24.04, which has Podman 4.9.3. We require Podman 5.0 or newer for the best experience, because we didn’t have success with Podman 4.x in our automated tests, but you can still use Podman 4.x ignoring the warning on ddev start.
You can also install Podman Desktop if you prefer a GUI.
For more information, see the Podman tutorials.
Installing Docker CLI
Podman provides a Docker-compatible API, which means you can use the Docker CLI as a frontend for Podman. This approach offers several benefits:
- Use familiar
dockercommands while Podman handles the actual container operations - Switch between different container runtimes using Docker contexts
- Maintain compatibility with scripts and tools that expect the
dockercommand
-
Install only the CLI:
# Ubuntu/Debian sudo apt-get install docker-ce-cli # Fedora sudo dnf install docker-ce-cliNote: You don’t need to install
docker-ce(the Docker engine).
Configuring Podman Rootless
This is the recommended configuration for most users.
-
Prepare the system:
# Add subuid and subgid ranges if they don't exist for the current user grep "^$(id -un):\|^$(id -u):" /etc/subuid >/dev/null 2>&1 || sudo usermod --add-subuids 100000-165535 $(whoami) grep "^$(id -un):\|^$(id -u):" /etc/subgid >/dev/null 2>&1 || sudo usermod --add-subgids 100000-165535 $(whoami) # Propagate changes to subuid and subgid podman system migrate # Debian requires setting unprivileged_userns_clone if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then if [ "1" != "$(cat /proc/sys/kernel/unprivileged_userns_clone)" ]; then echo 'kernel.unprivileged_userns_clone=1' | sudo tee -a /etc/sysctl.d/60-rootless.conf sudo sysctl --system fi fi # Fedora requires setting max_user_namespaces if [ -f /proc/sys/user/max_user_namespaces ]; then if [ "0" = "$(cat /proc/sys/user/max_user_namespaces)" ]; then echo 'user.max_user_namespaces=28633' | sudo tee -a /etc/sysctl.d/60-rootless.conf sudo sysctl --system fi fi # Allow privileged port access if needed if [ -f /proc/sys/net/ipv4/ip_unprivileged_port_start ]; then if [ "1024" = "$(cat /proc/sys/net/ipv4/ip_unprivileged_port_start)" ]; then echo 'net.ipv4.ip_unprivileged_port_start=0' | sudo tee -a /etc/sysctl.d/60-rootless.conf sudo sysctl --system fi fiFor more details, see the Arch Linux Wiki.
-
Enable the Podman socket and verify it’s running:
systemctl --user enable --now podman.socket # You should see `/run/user/1000/podman/podman.sock` (the number may vary): ls $XDG_RUNTIME_DIR/podman/podman.sock # You can also check the socket path with: podman info --format '{{.Host.RemoteSocket.Path}}'For more details, see the Podman socket activation documentation.
-
Configure Docker API to use Podman:
# View existing contexts docker context ls # Create Podman rootless context docker context create podman-rootless \ --description "Podman (rootless)" \ --docker host="unix://$XDG_RUNTIME_DIR/podman/podman.sock" # Switch to the new context docker context use podman-rootless # Verify it works docker psFor more details, see Podman rootless tutorial
-
Proceed with DDEV installation.
Podman Rootless Performance Optimization
Podman Rootless is significantly slower than Docker. See these resources:
To improve performance, install fuse-overlayfs and configure the overlay storage driver:
Install fuse-overlayfs:
# Ubuntu/Debian
sudo apt-get install fuse-overlayfs
# Fedora
sudo dnf install fuse-overlayfs
Configure storage:
mkdir -p ~/.config/containers
cat << 'EOF' > ~/.config/containers/storage.conf
[storage]
driver = "overlay"
[storage.options.overlay]
mount_program = "/usr/bin/fuse-overlayfs"
EOF
Warning: If you already have Podman containers, images, or volumes, you’ll need to reset Podman for this change to take effect:
podman system reset
This removes all existing containers, images, and volumes (similar to docker system prune -a).
Configuring Podman Rootful
Rootless Podman is recommended for most users. However, if you need rootful Podman, the setup differs from rootless in two key ways:
-
User group permissions: configure group permissions to allow non-root users to access the rootful socket
-
Socket activation and paths:
# Enable rootful socket (requires sudo) sudo systemctl enable --now podman.socket # Socket path for rootful ls /var/run/podman/podman.sockCompare to rootless:
# Rootless socket (no sudo) systemctl --user enable --now podman.socket # Socket path for rootless ls $XDG_RUNTIME_DIR/podman/podman.sock
Setting Up Docker Rootless
Docker Rootless on Linux offers rootless security with full Docker compatibility.
-
Follow the official Docker Rootless installation guide.
-
Configure system:
# Allow privileged port access if needed if [ -f /proc/sys/net/ipv4/ip_unprivileged_port_start ]; then if [ "1024" = "$(cat /proc/sys/net/ipv4/ip_unprivileged_port_start)" ]; then echo 'net.ipv4.ip_unprivileged_port_start=0' | sudo tee -a /etc/sysctl.d/60-rootless.conf sudo sysctl --system fi fi # Allow loopback connections (needed for working Xdebug) # See https://github.com/moby/moby/issues/47684#issuecomment-2166149845 mkdir -p ~/.config/systemd/user/docker.service.d cat << 'EOF' > ~/.config/systemd/user/docker.service.d/override.conf [Service] Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_DISABLE_HOST_LOOPBACK=false" EOF -
Enable the Docker socket, and verify it’s running:
systemctl --user enable --now docker.socket # You should see `/run/user/1000/docker.sock` (the number may vary): ls $XDG_RUNTIME_DIR/docker.sock -
Configure Docker API to use Docker rootless:
# View existing contexts docker context ls # Create rootless context if it doesn't exist docker context inspect rootless >/dev/null 2>&1 || \ docker context create rootless \ --description "Rootless runtime socket" \ --docker host="unix://$XDG_RUNTIME_DIR/docker.sock" # Switch to the context docker context use rootless # Verify it works docker ps -
Proceed with DDEV installation.
-
Docker Rootless requires no-bind-mounts mode
Docker Rootless has a limitation with bind mounts that affects DDEV. You must enable
no-bind-mountsmode:ddev config global --no-bind-mounts=trueWhy this is needed:
Docker Rootless sets ownership for bind mounts to
rootinside containers. This is a known issue:- Mounting a volume with rootless always assigns ownership to root
- Add ability to mount volume as user other than root
The
rootuser inside the container maps to your host user, but many services will not run as root:- nginx runs as root without problems
- MySQL/MariaDB need extra configuration
- Apache and PostgreSQL will not run as root
Podman Rootless fixes this with the
--userns=keep-idoption, which keeps user IDs the same. Docker Rootless does not have this option.The
no-bind-mountsmode fixes this by using Mutagen for thewebcontainer.
macOS
macOS users can use Podman and Podman Desktop, but setup has its own challenges. Docker Rootless is not available on macOS.
Installing Podman
Install Podman using Homebrew:
brew install podman
Or install Podman Desktop if you prefer a GUI.
For more information, see the official Podman installation guide for macOS and Podman tutorials.
Installing Docker CLI
Podman provides a Docker-compatible API, which means you can use the Docker CLI as a frontend for Podman. This approach offers several benefits:
- Use familiar
dockercommands while Podman handles the actual container operations - Switch between different container runtimes using Docker contexts
- Maintain compatibility with scripts and tools that expect the
dockercommand
brew install docker
Configuring Podman
-
Initialize and start the Podman machine:
# check `podman machine init -h` for more options podman machine init --memory 8192 podman machine startExample output from
podman machine start:~ % podman machine start Starting machine "podman-machine-default" This machine is currently configured in rootless mode. If your containers require root permissions (e.g. ports < 1024), or if you run into compatibility issues with non-podman clients, you can switch using the following command: podman machine set --rootful API forwarding listening on: /var/folders/x3/r1wk89cd3_x0yb_21dgnj53m0000gn/T/podman/podman-machine-default-api.sock The system helper service is not installed; the default Docker API socket address can't be used by podman. If you would like to install it, run the following commands: sudo /opt/homebrew/Cellar/podman/5.7.1/bin/podman-mac-helper install podman machine stop; podman machine start You can still connect Docker API clients by setting DOCKER_HOST using the following command in your terminal session: export DOCKER_HOST='unix:///var/folders/x3/r1wk89cd3_x0yb_21dgnj53m0000gn/T/podman/podman-machine-default-api.sock' Machine "podman-machine-default" started successfully -
Configure Docker CLI to use Podman. Choose one of two approaches:
Option 1: Create a Docker context (recommended, more flexible):
# Create Podman context (path to socket may vary) # Use the socket path from `podman machine start` output docker context create podman-rootless \ --description "Podman (rootless)" \ --docker host="unix:///var/folders/x3/r1wk89cd3_x0yb_21dgnj53m0000gn/T/podman/podman-machine-default-api.sock" # Switch to the new context docker context use podman # Verify it works docker psThis approach uses Docker contexts to switch between different container runtimes without modifying system sockets. This is more flexible if you want to use multiple Docker providers.
Option 2: Use the default Docker socket (simpler, but less flexible):
# Install podman-mac-helper # Use the command from `podman machine start` output sudo /opt/homebrew/Cellar/podman/5.7.1/bin/podman-mac-helper install podman machine stop podman machine start # Verify it works docker ps -
Proceed with DDEV installation.
-
Handle privileged ports (<1024):
Podman on macOS cannot bind to privileged ports (80/443). Configure DDEV to use unprivileged ports:
ddev config global --router-http-port=8080 \ --router-https-port=8443Note: switching to rootful mode with
podman machine set --rootful --user-mode-networking=falsedoesn’t help with privileged ports because the--user-mode-networking=falseflag is not supported on macOS (it’s only available for WSL).
Windows
Windows users can use Podman Desktop, but setup has its own challenges. Docker Rootless is not available on traditional Windows (it works in WSL2, see the Linux and WSL2 section).
Installing Podman
Install Podman Desktop, which includes Podman.
Alternatively, install Podman directly following the official Podman installation guide for Windows.
For more information, see the Podman tutorials.
The setup and configuration follow similar patterns to the Linux/WSL2 setup, but with Podman Desktop managing the VM for you. Follow the Linux and WSL2 instructions.
Running Multiple Container Runtimes
You can run Docker and Podman sockets simultaneously and switch between them using Docker contexts.
For example, here’s a system with four active Docker contexts:
$ docker context ls
NAME DESCRIPTION DOCKER ENDPOINT
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock
podman Podman (rootful) unix:///var/run/podman/podman.sock
podman-rootless * Podman (rootless) unix:///run/user/1000/podman/podman.sock
rootless Rootless runtime socket unix:///run/user/1000/docker.sock
Switch between them with:
docker context use "<context-name>"
Note: Running both Docker and Podman in rootful mode at the same time may cause network conflicts. See Podman and Docker network problem on Fedora 41.
Switching Runtimes with DDEV
DDEV automatically detects your active container runtime. To switch:
-
Stop DDEV projects:
ddev poweroff -
Switch Docker context or change the
DOCKER_HOSTenvironment variable -
Start your project:
ddev start
Which Runtime Should You Choose?
Runtime Comparison
| Feature | Standard Docker | Docker Rootless | Podman Rootful | Podman Rootless |
|---|---|---|---|---|
| Platform Support | All | Linux, WSL2 | All | All |
| Rootless Daemon | ❌ | ✅ | ❌ | ✅ |
| Has automated testing in DDEV | ✅ | ✅ | ❌ | ✅ |
| Mutagen | ✅ | ✅ | ✅ | ✅ |
| Bind Mounts | ✅ | ❌, requires no-bind-mounts | ✅ | ✅ (with --userns=keep-id) |
| Performance | Excellent | Moderate (because of no-bind-mounts) | Slow compared to Docker | Slow compared to Docker |
| Privileged Ports (<1024) | Works by default | Requires sysctl config | Works by default | Requires sysctl config or may not work |
| Setup Complexity | Simple | Moderate | Moderate | Moderate |
| Maturity | Most mature | Experimental | Experimental | Experimental |
| Recommended For | Most users | Docker users needing rootless | Organizations that forbid Docker | Organizations that forbid Docker |
Recommendations
Use of the many standard Docker providers if:
- You’re comfortable with the most widely used container runtime
- You don’t have rootless security requirements
This is the recommended option for the vast majority of users.
Use Podman Rootless if:
- Your organization forbids Docker
- You want rootless security by default
Use Podman Rootful if:
- Your organization forbids Docker
- You want traditional container permissions without user namespace mapping overhead
Use Docker Rootless if:
- You need full Docker compatibility
- You want rootless security without changing runtimes
The Journey to Podman Support
Supporting Podman and Docker Rootless required major changes to DDEV’s Docker integration:
- Switched to official Docker client library (#5787): DDEV previously used an unofficial library to communicate with the Docker API. We migrated to Docker’s official client library for better compatibility and long-term support.
- Replaced direct CLI calls with proper API usage (#7189): DDEV used to call
docker context inspectdirectly, which doesn’t work with Podman. We switched to using thedocker/clilibrary to handle context operations properly. - Modernized SSH authentication (#7511): The
ddev auth sshcommand used to calldocker rundirectly. We rewrote it to use the Docker API, making it compatible with alternative runtimes. - Optimized API call performance (#7587): DDEV’s Docker API logic was inefficient, making redundant calls without caching. We restructured the code to cache data and reduce unnecessary API requests.
- Removed legacy docker-compose features (#7642): Podman refuses to work with deprecated
linksandexternal_linksdirectives indocker-composefiles. We removed these legacy features and modernized DDEV’s compose file generation. - Added Podman and Docker Rootless support (#7702): DDEV now detects and supports Podman (rootful and rootless) and Docker Rootless. We added handling for Podman-specific limitations and enabled rootless environments to work without root privileges.
- Added support for SELinux environments (#7939): Podman has SELinux enabled by default on Fedora and some other distributions. We added support for SELinux by configuring volume mounts with the appropriate labels.
These changes enabled Podman and Docker Rootless support. These features were developed together because Podman’s primary use case is rootless operation. Once DDEV could handle rootless runtimes, supporting both became natural. They share the same security model and similar technical constraints.
Supporting DDEV Development
This Podman and Docker Rootless support was made possible by community financial support. The changes required hundreds of hours of development, code reviews, and testing.
DDEV relies on support from individuals and organizations who use it. With Podman rootless support, DDEV now works in corporate environments where Docker Desktop is not allowed. If you or your organization uses DDEV, please consider sponsoring the project to help keep DDEV free and open source.
Conclusion
DDEV now supports Podman and Docker Rootless as experimental features. This opens DDEV to corporate environments where traditional Docker is not allowed.
DDEV automatically detects your runtime and handles the complexity. Whether you choose Podman for rootless security, Docker Rootless for compatibility, or standard Docker, setup is straightforward.
This article was edited and refined with assistance from Claude Code.