Tools: Podman on SLES 16: Installation, Storage, and First Rootless Container (2026 Guide)

Tools: Podman on SLES 16: Installation, Storage, and First Rootless Container (2026 Guide)

Podman on SLES 16: Installation, Storage, and First Rootless Container (2026 Guide)

Why This Matters

Prerequisites

Why Two Disks?

Step 1: Mount the Second Disk

Step 2: Install Podman

Step 3: Set Up Shared Multi-User Storage

Why Subordinate UIDs Matter

Step 4: Run Your First Container

Step 5: Verify Rootless Operation

Podman vs Docker: Key Differences

Try It: Parse JSON Without Installing Anything

What's happening here:

What's Next Quick one-liner: Install Podman on SLES 16 from the installation DVD, set up shared multi-user storage on a dedicated disk, and run your first rootless container — no Docker required. (This guide targets SLES 16. The concepts apply to SLES 15 as well, but I've only verified the steps on SLES 16.) Docker isn't the only container runtime. On SUSE Linux Enterprise Server, SLES ships Podman as the native container tool through its official Containers module — and it has genuine advantages over Docker. The biggest one: rootless by default. With Docker, the daemon runs as root. That means a container escape could give an attacker root access to your host. Podman runs containers under your regular user account — no root daemon, no single point of failure. There's also no daemon at all. Podman is daemonless — each container runs as a direct child process. Simpler, more secure, easier to debug. If you're in an enterprise environment running SLES, Podman is the natural fit. It's supported by SUSE, available on the installation DVD, and doesn't require third-party repositories. This guide gets you from a fresh SLES install to running your first container. Before we start — if you're setting this up on a VM, add a second virtual disk. Container images accumulate fast. A few images later, your root partition fills up and everything breaks. Keeping container storage on a separate disk means: This is a production best practice worth building into your habits from day one. If you added a second disk, set it up before installing Podman. On this VM the layout looks like: vda (40 GB) is the OS disk. vdb (20 GB) is the dedicated disk for container storage. Format vdb with XFS (SLES default): Create the mount point: Add to /etc/fstab for persistence: Mount everything from fstab and verify: Podman ships on the SLES 16 installation DVD. Enable the DVD repository and install: This is where most guides stop — they just say "install and go." But in a real environment, multiple people will run rootless containers. Each user's container images and layers are stored separately (rootless Podman stores everything under each user's home directory by default). If you don't plan for this, each user's ~/.local fills up their own home partition independently and unpredictably. Here is how I like to set up Podman in an enterprise environment. Each user gets their own isolated space on a shared disk, and access is controlled through a group: Create the shared storage directory and the podman group: The 2775 permission is important — the setgid bit means any subdirectory created inside /var/lib/containers/storage automatically inherits the podman group. That keeps things consistent as you add users. Add users who should have container access: In rootless mode, container processes run in their own user namespace. Their UIDs get remapped to different UIDs on your host. This is a Linux kernel feature (user_namespaces(7)) and works identically for both rootless Docker and rootless Podman. The mapping looks like this: The subuid_start is defined in /etc/subuid: SLES assigns 100000 by default. This means a container process running as UID 1000 lands on your host as UID 100999 (100000 + 1000 - 1). If your bind-mounted directory is owned by you (1000), that container process cannot write to it. I prefer explicit ranges so the math is clean and there's no guessing. First, remove any existing subordinate UID/GID entries, then set the new range: This gives 65,536 subordinate UIDs and GIDs. Now the mapping is clean and predictable: Log out and back in so the group change takes effect. Verify: You should see podman in the list. Create your personal storage directory: Create Podman's per-user storage config: This file tells rootless Podman to store your images, containers, and layers on the shared disk instead of filling up your home directory. You should see /var/lib/containers/storage/sysadmin. Now run a container as your regular user: Podman ran this rootless — no root daemon, no extra configuration. Check the container ran successfully: This is a good moment to clarify the difference between podman ps and podman ps -a: If you run podman ps right now, it returns nothing — the hello container printed its message and exited immediately. It worked perfectly, it just isn't running anymore. podman ps -a shows the full history, including containers that completed and stopped. This is the key difference from Docker. In rootless mode, there's no persistent background daemon waiting for commands. Check the status: You'll see the podman.service exists but is inactive: This is normal — it's a socket-activated service. It starts only when needed (for example, when Podman Desktop or other tools connect to it) and shuts down when idle. Unlike Docker, there's no dockerd process sitting at PID 1 consuming resources all day. Verify storage is on the shared disk: You should see your second disk, not the root partition. The CLI is fully compatible — most docker commands work with podman. You can set the alias: Instead of the usual hello-world, let's verify rootless Podman with something useful. Create a sample JSON file: Normally you'd need to install jq to parse and pretty-print this JSON. With Podman, the tool comes with the container: But the output is completely blank. No error, no JSON — nothing. That's because podman run doesn't pass standard input into the container by default. The jq process started, received nothing, and exited silently. To pipe data in, you need the -i flag: Now the output — beautifully formatted JSON: No installation. No sudo zypper install jq. No repository configuration. The jq binary lives inside the container, and you used it without touching your host system. You've got Podman installed with proper shared storage and running rootless containers on SLES 16. Coming up: running your first real workload — pulling images, managing container lifecycles, and understanding the differences between podman run flags you'll use every day. Author: David Tio

Tags: Podman, SLES 16, SUSE, Containers, Rootless, Linux, Enterprise, DevOps, TutorialSeries: Levelling Podman

Word Count: ~1,500 Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Code Block

Copy

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sr0 11:0 1 1024M 0 rom vda 254:0 0 40G 0 disk ├─vda1 254:1 0 8M 0 part ├─vda2 254:2 0 38G 0 part /var │ /usr/local │ /opt │ /home │ /srv │ /root │ /boot/grub2/i386-pc │ /boot/grub2/x86_64-efi │ /.snapshots │ / └─vda3 254:3 0 2G 0 part [SWAP] vdb 254:16 0 20G 0 disk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sr0 11:0 1 1024M 0 rom vda 254:0 0 40G 0 disk ├─vda1 254:1 0 8M 0 part ├─vda2 254:2 0 38G 0 part /var │ /usr/local │ /opt │ /home │ /srv │ /root │ /boot/grub2/i386-pc │ /boot/grub2/x86_64-efi │ /.snapshots │ / └─vda3 254:3 0 2G 0 part [SWAP] vdb 254:16 0 20G 0 disk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sr0 11:0 1 1024M 0 rom vda 254:0 0 40G 0 disk ├─vda1 254:1 0 8M 0 part ├─vda2 254:2 0 38G 0 part /var │ /usr/local │ /opt │ /home │ /srv │ /root │ /boot/grub2/i386-pc │ /boot/grub2/x86_64-efi │ /.snapshots │ / └─vda3 254:3 0 2G 0 part [SWAP] vdb 254:16 0 20G 0 disk $ sudo mkfs.xfs /dev/vdb $ sudo mkfs.xfs /dev/vdb $ sudo mkfs.xfs /dev/vdb $ sudo mkdir -p /var/lib/containers $ sudo mkdir -p /var/lib/containers $ sudo mkdir -p /var/lib/containers $ echo '/dev/vdb /var/lib/containers xfs defaults 0 0' | sudo tee -a /etc/fstab $ echo '/dev/vdb /var/lib/containers xfs defaults 0 0' | sudo tee -a /etc/fstab $ echo '/dev/vdb /var/lib/containers xfs defaults 0 0' | sudo tee -a /etc/fstab $ sudo mount -a $ df -Th /var/lib/containers $ sudo mount -a $ df -Th /var/lib/containers $ sudo mount -a $ df -Th /var/lib/containers $ sudo zypper mr -e SLES $ sudo zypper install -y podman fuse-overlayfs $ sudo zypper mr -e SLES $ sudo zypper install -y podman fuse-overlayfs $ sudo zypper mr -e SLES $ sudo zypper install -y podman fuse-overlayfs $ podman --version $ podman --version $ podman --version podman version 5.4.2 podman version 5.4.2 podman version 5.4.2 $ sudo mkdir -p /var/lib/containers/storage $ sudo groupadd podman $ sudo chown root:podman /var/lib/containers/storage $ sudo chmod 2775 /var/lib/containers/storage $ sudo mkdir -p /var/lib/containers/storage $ sudo groupadd podman $ sudo chown root:podman /var/lib/containers/storage $ sudo chmod 2775 /var/lib/containers/storage $ sudo mkdir -p /var/lib/containers/storage $ sudo groupadd podman $ sudo chown root:podman /var/lib/containers/storage $ sudo chmod 2775 /var/lib/containers/storage $ sudo usermod -aG podman sysadmin $ sudo usermod -aG podman sysadmin $ sudo usermod -aG podman sysadmin $ grep sysadmin /etc/subuid sysadmin:100000:65536 $ grep sysadmin /etc/subuid sysadmin:100000:65536 $ grep sysadmin /etc/subuid sysadmin:100000:65536 $ sudo sed -i "/^sysadmin:/d" /etc/subuid $ sudo sed -i "/^sysadmin:/d" /etc/subgid $ sudo usermod --add-subuids 100001-165536 sysadmin $ sudo usermod --add-subgids 100001-165536 sysadmin $ sudo sed -i "/^sysadmin:/d" /etc/subuid $ sudo sed -i "/^sysadmin:/d" /etc/subgid $ sudo usermod --add-subuids 100001-165536 sysadmin $ sudo usermod --add-subgids 100001-165536 sysadmin $ sudo sed -i "/^sysadmin:/d" /etc/subuid $ sudo sed -i "/^sysadmin:/d" /etc/subgid $ sudo usermod --add-subuids 100001-165536 sysadmin $ sudo usermod --add-subgids 100001-165536 sysadmin $ cat /etc/subuid sysadmin:100001:65536 $ cat /etc/subgid sysadmin:100001:65536 $ cat /etc/subuid sysadmin:100001:65536 $ cat /etc/subgid sysadmin:100001:65536 $ cat /etc/subuid sysadmin:100001:65536 $ cat /etc/subgid sysadmin:100001:65536 $ mkdir -p /var/lib/containers/storage/sysadmin $ mkdir -p /var/lib/containers/storage/sysadmin $ mkdir -p /var/lib/containers/storage/sysadmin $ mkdir -p ~/.config/containers $ cat > ~/.config/containers/storage.conf << 'EOF' [storage] driver = "overlay" graphroot = "/var/lib/containers/storage/sysadmin" [storage.options.overlay] mount_program = "/usr/bin/fuse-overlayfs" EOF $ mkdir -p ~/.config/containers $ cat > ~/.config/containers/storage.conf << 'EOF' [storage] driver = "overlay" graphroot = "/var/lib/containers/storage/sysadmin" [storage.options.overlay] mount_program = "/usr/bin/fuse-overlayfs" EOF $ mkdir -p ~/.config/containers $ cat > ~/.config/containers/storage.conf << 'EOF' [storage] driver = "overlay" graphroot = "/var/lib/containers/storage/sysadmin" [storage.options.overlay] mount_program = "/usr/bin/fuse-overlayfs" EOF $ podman info | grep graphRoot $ podman info | grep graphRoot $ podman info | grep graphRoot $ podman run quay.io/podman/hello:latest $ podman run quay.io/podman/hello:latest $ podman run quay.io/podman/hello:latest Embracing and extending the Podman community... ================================================================ Podman Podman Podman ================================================================ ... (Podman hello output) ... Have a great day! Embracing and extending the Podman community... ================================================================ Podman Podman Podman ================================================================ ... (Podman hello output) ... Have a great day! Embracing and extending the Podman community... ================================================================ Podman Podman Podman ================================================================ ... (Podman hello output) ... Have a great day! $ podman ps -a $ podman ps -a $ podman ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2671631a4e8a quay.io/podman/hello:latest /usr/local/bin/po... 26 seconds ago Exited (0) 26 seconds ago stoic_sammet CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2671631a4e8a quay.io/podman/hello:latest /usr/local/bin/po... 26 seconds ago Exited (0) 26 seconds ago stoic_sammet CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2671631a4e8a quay.io/podman/hello:latest /usr/local/bin/po... 26 seconds ago Exited (0) 26 seconds ago stoic_sammet $ systemctl status podman $ systemctl status podman $ systemctl status podman ○ podman.service - Podman API Service Loaded: loaded (/usr/lib/systemd/system/podman.service; disabled; preset: disabled) Active: inactive (dead) TriggeredBy: ○ podman.socket ○ podman.service - Podman API Service Loaded: loaded (/usr/lib/systemd/system/podman.service; disabled; preset: disabled) Active: inactive (dead) TriggeredBy: ○ podman.socket ○ podman.service - Podman API Service Loaded: loaded (/usr/lib/systemd/system/podman.service; disabled; preset: disabled) Active: inactive (dead) TriggeredBy: ○ podman.socket $ df -Th /var/lib/containers/storage/sysadmin $ df -Th /var/lib/containers/storage/sysadmin $ df -Th /var/lib/containers/storage/sysadmin $ alias docker=podman $ alias docker=podman $ alias docker=podman $ cat > ~/sample.json << 'EOF' {"name":"David","company":"Transcend Solutions","role":"DevOps Engineer","skills":["Docker","Kubernetes","Linux"],"location":"Singapore","experience_years":15} EOF $ cat > ~/sample.json << 'EOF' {"name":"David","company":"Transcend Solutions","role":"DevOps Engineer","skills":["Docker","Kubernetes","Linux"],"location":"Singapore","experience_years":15} EOF $ cat > ~/sample.json << 'EOF' {"name":"David","company":"Transcend Solutions","role":"DevOps Engineer","skills":["Docker","Kubernetes","Linux"],"location":"Singapore","experience_years":15} EOF $ cat ~/sample.json | podman run ghcr.io/jqlang/jq '.' $ cat ~/sample.json | podman run ghcr.io/jqlang/jq '.' $ cat ~/sample.json | podman run ghcr.io/jqlang/jq '.' Trying to pull ghcr.io/jqlang/jq:latest... Getting image source signatures Copying blob e27c450974af done | Copying blob ee0085cc4ebc done | Copying config 3bada1936a done | Writing manifest to image destination Trying to pull ghcr.io/jqlang/jq:latest... Getting image source signatures Copying blob e27c450974af done | Copying blob ee0085cc4ebc done | Copying config 3bada1936a done | Writing manifest to image destination Trying to pull ghcr.io/jqlang/jq:latest... Getting image source signatures Copying blob e27c450974af done | Copying blob ee0085cc4ebc done | Copying config 3bada1936a done | Writing manifest to image destination $ cat ~/sample.json | podman run -i ghcr.io/jqlang/jq '.' $ cat ~/sample.json | podman run -i ghcr.io/jqlang/jq '.' $ cat ~/sample.json | podman run -i ghcr.io/jqlang/jq '.' { "name": "David", "company": "Transcend Solutions", "role": "DevOps Engineer", "skills": [ "Docker", "Kubernetes", "Linux" ], "location": "Singapore", "experience_years": 15 } { "name": "David", "company": "Transcend Solutions", "role": "DevOps Engineer", "skills": [ "Docker", "Kubernetes", "Linux" ], "location": "Singapore", "experience_years": 15 } { "name": "David", "company": "Transcend Solutions", "role": "DevOps Engineer", "skills": [ "Docker", "Kubernetes", "Linux" ], "location": "Singapore", "experience_years": 15 } - SLES 16 (minimal or server installation) - DVD repository enabled (see Add a DVD as a Local Zypper Repository) — or an active SCC subscription - Two virtual disks (recommended): 40 GB for the OS, 20 GB for container storage - 15-20 minutes - Container data is isolated from the OS - You can resize or wipe container storage without touching the OS - Backups are simpler — back up the container disk separately