Run Podman containers in rootless mode on VPS

Use Podman Instead of Docker on VPS (Rootless Containers)

June 2026 · 7 min read

Podman is a container engine that is almost 100% command-compatible with Docker, but with a crucial difference — there is no daemon running as root continuously and it can run in rootless mode. This significantly reduces the attack surface on a VPS exposed to the internet. If a container is compromised, an attacker gains only regular user privileges, not root access to the entire system. This article covers installation and real-world usage, from replacing docker commands all the way to running containers as systemd services.

Install Podman

# Ubuntu / Debian
sudo apt update && sudo apt install -y podman

# CentOS / RHEL / Rocky
sudo dnf install -y podman

# Check version
podman --version
podman info | grep -i rootless

To use docker commands without modifying existing scripts, set up an alias or install the podman-docker package:

sudo apt install -y podman-docker   # Creates /usr/bin/docker pointing to podman
# or
echo 'alias docker=podman' >> ~/.bashrc && source ~/.bashrc

Run Containers in Rootless Mode

No need to use sudo — run as a regular user directly. Podman uses user namespaces to map UIDs inside the container with the host's subuid:

# Run nginx without root
podman run -d --name web -p 8080:80 docker.io/library/nginx:alpine

# See running containers
podman ps

# Check subuid/subgid assigned to your user
cat /etc/subuid /etc/subgid | grep $USER
Note: In rootless mode, you cannot directly bind ports below 1024 (like 80/443). Map to a higher port instead, such as 8080, then place a reverse proxy in front. Alternatively, adjust net.ipv4.ip_unprivileged_port_start=80 in sysctl if needed.

Use podman-compose Instead of docker-compose

# Install podman-compose
sudo apt install -y podman-compose      # or: pip3 install podman-compose

# Use the same docker-compose.yml file
podman-compose up -d
podman-compose ps
podman-compose down

Newer versions of Podman also support podman compose (calling external providers) and Kubernetes YAML via podman kube play, making migration to Kubernetes smoother in the future.

Run Containers as systemd Services (Quadlet)

Podman's strength is its systemd integration — containers start after reboot, restart on crash, and appear in journald like regular services. The recommended new approach is Quadlet:

# ~/.config/containers/systemd/web.container
[Container]
Image=docker.io/library/nginx:alpine
PublishPort=8080:80
Volume=%h/site:/usr/share/nginx/html:ro,Z

[Service]
Restart=always

[Install]
WantedBy=default.target

# Reload and start
systemctl --user daemon-reload
systemctl --user start web.service

# Keep running even after logout (lingering)
sudo loginctl enable-linger $USER
Tip: Always use enable-linger for rootless services that need to run continuously. Otherwise, the container will stop when the user logs out — this is a common gotcha that makes people think "Podman doesn't restart after reboot."

Verify Podman is Working Correctly

Confirm that rootless mode is active, containers are running, and systemd is managing the service:

# 1. Confirm rootless mode (should show rootless: true)
podman info --format '{{.Host.Security.Rootless}}'

# 2. Container is running and healthy
podman ps --format 'table {{.Names}} {{.Status}} {{.Ports}}'

# 3. Test that the service responds
curl -I http://localhost:8080

# 4. Check systemd user service
systemctl --user status web.service

# 5. View logs via journald
journalctl --user -u web.service --since "10 min ago"
Tip: If podman ps is empty after reboot even though you set up the service, check loginctl show-user $USER | grep Linger — it must show Linger=yes. This is the #1 reason rootless containers seem to "disappear" after a reboot.

Common Issues & Troubleshooting

Error: "short-name did not resolve to an alias"

Podman does not guess registries like Docker does. You must specify the full registry or use the complete image name:

# Instead of using just nginx, provide the full name
podman pull docker.io/library/nginx:alpine
# or edit /etc/containers/registries.conf to include unqualified-search-registries = ["docker.io"]

Error: "rootlessport cannot expose privileged port"

Rootless mode cannot bind ports below 1024. Use a higher port and proxy in front, or enable unprivileged ports:

echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee /etc/sysctl.d/99-podman.conf
sudo sysctl --system

Container Stops After Logout

You have not enabled lingering for the user. Run sudo loginctl enable-linger $USER and the container will continue running even when you disconnect from SSH.

Volume Permission Denied (SELinux)

On RHEL/Rocky systems with SELinux enabled, add :Z to the end of the volume mount to relabel files:

podman run -v /data:/app/data:Z ...

Frequently Asked Questions

Can Podman replace Docker in all cases?

Almost always. Commands like run/build/pull/ps/exec are identical, and it reads the same Dockerfile/compose files. The main differences are the lack of a daemon and rootless mode, which requires adjustments for privileged ports and sockets that some tools (like Traefik) expect to find at docker.sock — Podman provides a compatible socket if enabled.

Is Podman faster or slower than Docker?

Runtime performance is very similar because both use runc/crun under the hood. Rootless mode may have slight overhead with networking (slirp4netns/pasta), but it does not impact typical workloads. Plus, you save RAM because there is no daemon running constantly.

What do I need to do to migrate from Docker to Podman?

Mostly just change commands and install podman-docker to map docker commands. Migrate containers that used --restart to Quadlet/systemd instead. All images pull directly from the same registries.