Learn to install Podman and configure rootless containers as a secure Docker alternative. Includes Docker Compose migration, systemd integration, and troubleshooting common permission issues.
Prerequisites
- Root access to the server
- Basic knowledge of Linux command line
- Understanding of container concepts
What this solves
Podman provides a secure, daemon-less alternative to Docker that runs containers without root privileges. This tutorial shows you how to install Podman, configure rootless containers, migrate from Docker Compose, and integrate with systemd for production deployments.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you get the latest versions of Podman and its dependencies.
sudo apt update && sudo apt upgrade -y
Install Podman and dependencies
Install Podman along with essential tools for rootless container operations and Docker Compose compatibility.
sudo apt install -y podman podman-compose podman-docker uidmap slirp4netns fuse-overlayfs
Configure user namespaces
Set up subordinate user and group IDs to enable rootless container operations. These ranges define the user namespace mapping for your containers.
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
Verify the configuration was applied correctly:
grep $USER /etc/subuid /etc/subgid
Configure container registries
Set up default container registries and search order. This configuration determines where Podman looks for container images.
[registries.search]
registries = ['docker.io', 'quay.io', 'registry.fedoraproject.org']
[registries.insecure]
registries = []
[registries.block]
registries = []
Configure storage settings
Create a user-specific storage configuration for optimal rootless container performance.
mkdir -p ~/.config/containers
[storage]
driver = "overlay"
runroot = "/run/user/1000/containers"
graphroot = "/home/$USER/.local/share/containers/storage"
[storage.options]
additionalimagestores = ["/usr/share/containers/storage"]
[storage.options.overlay]
mountopt = "nodev,metacopy=on"
Enable lingering for systemd integration
Enable user lingering to allow your containers to start at boot without requiring an active login session.
sudo loginctl enable-linger $USER
loginctl show-user $USER
Configure Podman for Docker compatibility
Set up the Podman socket to work with Docker Compose and other Docker-compatible tools.
systemctl --user enable --now podman.socket
systemctl --user status podman.socket
Create a Docker-compatible socket link:
sudo mkdir -p /run/docker
sudo ln -sf /run/user/$UID/podman/podman.sock /run/docker/docker.sock
Migrate Docker Compose to Podman
Test with a sample Docker Compose file
Create a test compose file to verify Podman works with your existing Docker Compose workflows.
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
environment:
- NGINX_HOST=example.com
redis:
image: redis:alpine
ports:
- "6379:6379"
Create sample content
Add some test content for the nginx container to serve.
mkdir -p html
echo 'Hello from Podman!
' > html/index.html
Run with Podman Compose
Start the services using podman-compose, which provides Docker Compose compatibility.
podman-compose up -d
podman-compose ps
Systemd integration and auto-start
Generate systemd unit files
Create systemd service files for your containers to enable automatic startup and management.
podman run -d --name my-app --restart=always -p 8080:80 nginx:alpine
podman generate systemd --new --files --name my-app
Install and enable the service
Move the generated unit file to the user systemd directory and enable it.
mkdir -p ~/.config/systemd/user
mv container-my-app.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now container-my-app.service
Verify systemd integration
Check that your container service is running and will start automatically on boot.
systemctl --user status container-my-app.service
systemctl --user list-unit-files | grep container
Container registry authentication
Configure registry authentication
Set up authentication for private container registries like Docker Hub or your organization's registry.
podman login docker.io
podman login quay.io
For automated authentication, store credentials securely:
echo $REGISTRY_PASSWORD | podman login --username $REGISTRY_USER --password-stdin docker.io
Test private image pulls
Verify that authentication works by pulling a private image or pushing to your registry.
podman pull docker.io/your-username/private-repo:latest
podman images
Verify your setup
Run these commands to confirm Podman is working correctly with rootless containers:
podman --version
podman info
podman run --rm hello-world
podman run --rm -p 8080:80 nginx:alpine &
curl http://localhost:8080
podman ps
podman stop --all
Check that systemd integration is working:
systemctl --user status podman.socket
loginctl show-user $USER | grep Linger
alias docker=podman to your shell profile for seamless compatibility.Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Permission denied when starting containers | User namespaces not configured | Run sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER and reboot |
| Cannot pull images from Docker Hub | Registry not in search list | Add docker.io to registries.conf search list |
| Containers don't start after reboot | User lingering not enabled | Run sudo loginctl enable-linger $USER |
| Port binding fails with "permission denied" | Trying to bind privileged ports (<1024) | Use ports above 1024 or configure net.ipv4.ip_unprivileged_port_start |
| Docker Compose commands fail | Podman socket not running | Run systemctl --user enable --now podman.socket |
| Storage permission errors | Incorrect storage configuration | Check ~/.config/containers/storage.conf ownership and permissions |
Next steps
- Install and configure Ollama for local AI models on Linux servers - Run AI workloads in Podman containers
- Configure nginx reverse proxy for Podman containers - Set up production web services
- Deploy Podman containers with Kubernetes YAML - Use Podman's Kubernetes compatibility
- Secure Podman containers with SELinux and AppArmor - Advanced container security
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Usage function
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Install and configure Podman for rootless containers"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " --no-docker Skip Docker compatibility setup"
echo ""
exit 1
}
# Parse arguments
SETUP_DOCKER_COMPAT=true
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
;;
--no-docker)
SETUP_DOCKER_COMPAT=false
shift
;;
*)
echo -e "${RED}Error: Unknown option $1${NC}"
usage
;;
esac
done
# Logging functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Cleanup function for rollback
cleanup() {
log_error "Installation failed. Performing cleanup..."
# Remove podman if partially installed
if command -v podman &> /dev/null; then
if [[ "$PKG_MGR" == "apt" ]]; then
sudo apt-get remove -y podman podman-compose podman-docker 2>/dev/null || true
else
sudo $PKG_INSTALL remove -y podman podman-compose podman-docker 2>/dev/null || true
fi
fi
# Remove user configs
rm -rf ~/.config/containers 2>/dev/null || true
rm -rf ~/.config/systemd/user/container-*.service 2>/dev/null || true
exit 1
}
trap cleanup ERR
# Check if running as root
if [[ $EUID -eq 0 ]]; then
log_error "This script should not be run as root for security reasons"
exit 1
fi
# Check for sudo access
if ! sudo -n true 2>/dev/null; then
log_error "This script requires sudo access"
exit 1
fi
log_info "Starting Podman rootless container installation"
# Auto-detect distribution
echo "[1/10] Detecting Linux distribution..."
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
UPDATE_CMD="apt update && apt upgrade -y"
PACKAGES="podman podman-compose podman-docker uidmap slirp4netns fuse-overlayfs"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
UPDATE_CMD="dnf update -y"
PACKAGES="podman podman-compose podman-docker shadow-utils slirp4netns fuse-overlayfs"
;;
fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
UPDATE_CMD="dnf update -y"
PACKAGES="podman podman-compose podman-docker shadow-utils slirp4netns fuse-overlayfs"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
UPDATE_CMD="yum update -y"
PACKAGES="podman shadow-utils slirp4netns fuse-overlayfs"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
log_success "Detected $PRETTY_NAME"
else
log_error "Cannot detect Linux distribution"
exit 1
fi
# Update system packages
echo "[2/10] Updating system packages..."
sudo $UPDATE_CMD
log_success "System packages updated"
# Install Podman and dependencies
echo "[3/10] Installing Podman and dependencies..."
sudo $PKG_INSTALL $PACKAGES
log_success "Podman installed successfully"
# Configure user namespaces
echo "[4/10] Configuring user namespaces..."
if ! grep -q "^${USER}:" /etc/subuid; then
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
log_success "User namespaces configured"
else
log_warn "User namespaces already configured"
fi
# Verify namespace configuration
if grep -q "^${USER}:" /etc/subuid && grep -q "^${USER}:" /etc/subgid; then
log_success "Namespace configuration verified"
else
log_error "Failed to configure user namespaces"
exit 1
fi
# Configure container registries
echo "[5/10] Configuring container registries..."
mkdir -p ~/.config/containers
cat > ~/.config/containers/registries.conf << 'EOF'
[registries.search]
registries = ['docker.io', 'quay.io', 'registry.fedoraproject.org']
[registries.insecure]
registries = []
[registries.block]
registries = []
EOF
chmod 644 ~/.config/containers/registries.conf
log_success "Container registries configured"
# Configure storage settings
echo "[6/10] Configuring storage settings..."
USER_ID=$(id -u)
cat > ~/.config/containers/storage.conf << EOF
[storage]
driver = "overlay"
runroot = "/run/user/${USER_ID}/containers"
graphroot = "/home/${USER}/.local/share/containers/storage"
[storage.options]
additionalimagestores = ["/usr/share/containers/storage"]
[storage.options.overlay]
mountopt = "nodev,metacopy=on"
EOF
chmod 644 ~/.config/containers/storage.conf
log_success "Storage settings configured"
# Enable lingering for systemd integration
echo "[7/10] Enabling user lingering..."
sudo loginctl enable-linger $USER
if loginctl show-user $USER | grep -q "Linger=yes"; then
log_success "User lingering enabled"
else
log_warn "Failed to verify user lingering"
fi
# Configure Podman socket
echo "[8/10] Configuring Podman socket..."
systemctl --user enable --now podman.socket
if systemctl --user is-active --quiet podman.socket; then
log_success "Podman socket enabled and started"
else
log_error "Failed to start Podman socket"
exit 1
fi
# Setup Docker compatibility
if [[ "$SETUP_DOCKER_COMPAT" == "true" ]]; then
echo "[9/10] Setting up Docker compatibility..."
USER_ID=$(id -u)
# Create Docker-compatible socket link
sudo mkdir -p /run/docker
sudo rm -f /run/docker/docker.sock 2>/dev/null || true
sudo ln -sf "/run/user/${USER_ID}/podman/podman.sock" /run/docker/docker.sock
# Wait a moment for socket to be available
sleep 2
if [[ -S "/run/user/${USER_ID}/podman/podman.sock" ]]; then
log_success "Docker compatibility configured"
else
log_warn "Docker socket not yet available, may need user re-login"
fi
else
echo "[9/10] Skipping Docker compatibility setup..."
log_info "Docker compatibility setup skipped"
fi
# Final verification
echo "[10/10] Performing final verification..."
# Test Podman installation
if podman --version > /dev/null 2>&1; then
PODMAN_VERSION=$(podman --version)
log_success "Podman installed: $PODMAN_VERSION"
else
log_error "Podman installation verification failed"
exit 1
fi
# Test rootless operation
if podman info --format "{{.Host.Security.Rootless}}" 2>/dev/null | grep -q "true"; then
log_success "Rootless mode verified"
else
log_warn "Rootless mode verification failed - may require re-login"
fi
# Test basic container operation
if timeout 30 podman run --rm alpine:latest echo "Test successful" > /dev/null 2>&1; then
log_success "Container operation test passed"
else
log_warn "Container test failed - may require re-login or image pull"
fi
log_success "Podman installation completed successfully!"
echo ""
echo -e "${BLUE}Next steps:${NC}"
echo "1. Log out and log back in to ensure all changes take effect"
echo "2. Test with: podman run hello-world"
echo "3. For Docker Compose compatibility: podman-compose up -d"
echo "4. Generate systemd services: podman generate systemd --help"
echo ""
echo -e "${YELLOW}Note:${NC} You may need to restart your session for all features to work properly"
Review the script before running. Execute with: bash install.sh