Podman and Docker Rootless in DDEV
The DDEV community has requested Podman and Docker Rootless support for years. This support is now available as an experimental feature. It allows DDEV to work in corporate environments where Docker Desktop is not allowed due to security policies or licensing restrictions.
This required major changes to how DDEV works with container runtimes. We rebuilt core infrastructure and fixed compatibility issues that existed since DDEV’s start.
Note: Podman and Docker Rootless support is experimental. While it works for most use cases, you may encounter issues. Please report any problems on the DDEV issue tracker.
Table of Contents
- The Journey to Podman Support
- Understanding Docker and Podman
- Installing Podman
- Configuring Podman for DDEV
- Setting Up Docker Rootless with DDEV
- Running Multiple Container Runtimes
- Switching Runtimes with DDEV
- Which Runtime Should You Choose?
- Supporting DDEV Development
- Conclusion
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.
These changes enabled Podman and Docker Rootless support.
Understanding Docker and Podman
Why Choose Rootless?
Running containers without root privileges is more secure. Traditional Docker and rootful Podman need elevated privileges, which creates security risks in corporate environments where strict security policies apply.
Rootless alternatives (Podman Rootless and Docker Rootless) run containers without root access. This means:
- No root daemon running on your system
- Container processes cannot access root-owned files
- Reduced attack surface if a container is compromised
Podman is rootless by default. Docker Rootless requires special setup.
The Socket Requirement for DDEV
DDEV needs a running Podman socket. The socket lets DDEV use the Docker API to talk to Podman, so DDEV can support both Docker and Podman with the same code.
Installing Podman
Podman Installation Options
Podman can be installed with or without a GUI:
Without GUI:
With Podman Desktop GUI:
For more information, see the Podman tutorials.
Installing Docker CLI
DDEV requires the docker CLI client for ddev utility test (this may change in the future). If you’re using Podman without Docker, install only the CLI:
macOS:
brew install docker
Linux (Ubuntu/Debian example):
-
Install only the CLI:
sudo apt-get install docker-ce-cliNote: You don’t need to install
docker-ce(the Docker engine).
Configuring Podman for DDEV
After installing Podman, enable the API socket so DDEV can connect.
Podman Rootless on Linux
This is the recommended configuration for most users.
-
Enable and start the Podman socket:
systemctl --user enable --now podman.socket -
Verify the socket is running:
ls $XDG_RUNTIME_DIR/podman/podman.sockYou should see
/run/user/1000/podman/podman.sock(the number may vary).You can also check the socket path with:
podman info --format '{{.Host.RemoteSocket.Path}}' -
Configure Docker to use Podman. Choose one of these methods:
Option A: Create Docker context (if you have
dockerCLI installed):# 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 psOption B: Set environment variable in your shell profile (
~/.bashrcor~/.zshrc):export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
For additional details, see the Podman socket activation documentation.
Podman Rootful on Linux
Rootful Podman requires configuring user group permissions, similar to Docker’s Linux post-installation steps.
-
Create the
podmangroup:sudo groupadd podman -
Add your user to the group:
sudo usermod -aG podman $USER -
Log out and log back in, or activate the changes immediately:
newgrp podmanIf you’re in a VM, restart it for changes to take effect.
-
Configure Podman socket permissions:
# Configure permissions for /run/podman sudo tee /etc/tmpfiles.d/podman.conf > /dev/null <<EOF d /run/podman 0770 root podman EOF # Configure socket to run with podman group sudo mkdir -p /etc/systemd/system/podman.socket.d sudo tee /etc/systemd/system/podman.socket.d/override.conf > /dev/null <<EOF [Socket] SocketMode=0660 SocketUser=root SocketGroup=podman EOF # Apply changes sudo systemd-tmpfiles --create sudo systemctl daemon-reload -
Enable and start the socket:
sudo systemctl enable --now podman.socket -
Verify the socket:
ls /var/run/podman/podman.sockOr check with:
sudo podman info --format '{{.Host.RemoteSocket.Path}}' -
Configure Docker to use Podman. Choose one of these methods:
Option A: Create Docker context (if you have
dockerCLI installed):# View existing contexts docker context ls # Create Podman rootful context docker context create podman \ --description "Podman (rootful)" \ --docker host="unix:///var/run/podman/podman.sock" # Switch to the new context docker context use podman # Verify it works docker psOption B: Set environment variable in your shell profile (
~/.bashrcor~/.zshrc):export DOCKER_HOST=unix:///var/run/podman/podman.sock
Common Podman Issues
Based on testing with Arch Linux, here are solutions to common problems. These issues are documented in the Podman rootless tutorial and Arch Wiki.
Issue: UID/GID mapping errors when starting DDEV:
unable to copy from source docker://ddev/ddev-utilities:latest: copying system image from manifest list: writing blob: adding layer with blob “sha256:43c4264…”: unpacking failed (error: exit status 1; output: potentially insufficient UIDs or GIDs available in user namespace (requested 0:42 for /etc/shadow): Check /etc/subuid and /etc/subgid if configured locally and run “podman system migrate”: lchown /etc/shadow: invalid argument)
Solution: Configure subuid and subgid mappings:
# Add subuid and subgid ranges
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $(whoami)
# Migrate existing Podman data
podman system migrate
See the Arch Wiki Podman page for more details.
Issue: Permission denied when binding to privileged ports:
Error response from daemon: rootlessport cannot expose privileged port 80, you can add ‘net.ipv4.ip_unprivileged_port_start=80’ to /etc/sysctl.conf (currently 1024), or choose a larger port number (>= 1024): listen tcp 127.0.0.1:80: bind: permission denied
Solution: Allow unprivileged port binding:
# Create config
echo 'net.ipv4.ip_unprivileged_port_start=0' | sudo tee -a /etc/sysctl.d/60-rootless.conf
# Apply without rebooting
sudo sysctl -p /etc/sysctl.d/60-rootless.conf
Podman Rootless Performance Optimization
Podman Rootless is 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
# Arch Linux
sudo pacman -S fuse-overlayfs
Configure storage:
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).
Setting Up Docker Rootless with DDEV
Docker Rootless offers rootless security with full Docker compatibility.
Installation
-
Follow the official Docker Rootless installation guide.
-
For daemon configuration, see the Docker daemon documentation.
-
Configure system for privileged port access:
# Allow binding to ports below 1024 echo 'net.ipv4.ip_unprivileged_port_start=0' | sudo tee -a /etc/sysctl.d/60-rootless.conf # Apply without rebooting sudo sysctl -p /etc/sysctl.d/60-rootless.conf -
Enable and start the Docker socket:
systemctl --user enable --now docker.socket -
Verify the socket:
ls $XDG_RUNTIME_DIR/docker.sockYou should see
/run/user/1000/docker.sock(the number may vary). -
Configure Docker context (if needed):
# View existing contexts docker context ls # Create rootless context 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 -
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.
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?
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
Use standard Docker if:
- You’re comfortable with the most widely used container runtime
- You don’t have rootless security requirements
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.