Skip to main content

Command Palette

Search for a command to run...

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

Published
9 min read

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

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.)


Why This Matters

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.


Prerequisites

  • 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

Why Two Disks?

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:

  • 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

This is a production best practice worth building into your habits from day one.


Step 1: Mount the Second Disk

If you added a second disk, set it up before installing Podman.

Find the disk:

$ lsblk

On this VM the layout looks like:

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

vda (40 GB) is the OS disk. vdb (20 GB) is the dedicated disk for container storage.

Format vdb with XFS (SLES default):

$ sudo mkfs.xfs /dev/vdb

Create the mount point:

$ sudo mkdir -p /var/lib/containers

Add to /etc/fstab for persistence:

$ echo '/dev/vdb /var/lib/containers xfs defaults 0 0' | sudo tee -a /etc/fstab

Mount everything from fstab and verify:

$ sudo mount -a
$ df -Th /var/lib/containers

Step 2: Install Podman

Podman ships on the SLES 16 installation DVD. Enable the DVD repository and install:

$ sudo zypper mr -e SLES
$ sudo zypper install -y podman fuse-overlayfs

Verify:

$ podman --version
podman version 5.4.2

Step 3: Set Up Shared Multi-User Storage

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:

As root (or sudo):

Create the shared storage directory and the podman group:

$ 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

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:

$ sudo usermod -aG podman sysadmin

Why Subordinate UIDs Matter

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:

Inside containerOn your host
UID 0 (root)Your user UID (e.g. 1000)
UID 1subuid_start
UID Nsubuid_start + N - 1

The subuid_start is defined in /etc/subuid:

$ grep sysadmin /etc/subuid
sysadmin:100000:65536

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:

$ 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

This gives 65,536 subordinate UIDs and GIDs. Now the mapping is clean and predictable:

Inside containerOn your host
01000 (sysadmin)
1100001
1000101000

Verify:

$ cat /etc/subuid
sysadmin:100001:65536

$ cat /etc/subgid
sysadmin:100001:65536

As each user:

Log out and back in so the group change takes effect. Verify:

$ groups

You should see podman in the list.

Create your personal storage directory:

$ mkdir -p /var/lib/containers/storage/sysadmin

Create Podman's per-user storage config:

$ 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

This file tells rootless Podman to store your images, containers, and layers on the shared disk instead of filling up your home directory.

Verify:

$ podman info | grep graphRoot

You should see /var/lib/containers/storage/sysadmin.


Step 4: Run Your First Container

Now run a container as your regular user:

$ podman run quay.io/podman/hello:latest

You should see:

Embracing and extending the Podman community...

================================================================
                       Podman Podman Podman
================================================================

... (Podman hello output) ...

Have a great day!

Podman ran this rootless — no root daemon, no extra configuration.

Check the container ran successfully:

$ 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

This is a good moment to clarify the difference between podman ps and podman ps -a:

CommandWhat it shows
podman psOnly running containers
podman ps -aAll containers — running, stopped, or exited

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.


Step 5: Verify Rootless Operation

This is the key difference from Docker. In rootless mode, there's no persistent background daemon waiting for commands. Check the status:

$ systemctl status podman

You'll see the podman.service exists but is inactive:

○ podman.service - Podman API Service
     Loaded: loaded (/usr/lib/systemd/system/podman.service; disabled; preset: disabled)
     Active: inactive (dead)
TriggeredBy: ○ podman.socket

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:

$ df -Th /var/lib/containers/storage/sysadmin

You should see your second disk, not the root partition.


Podman vs Docker: Key Differences

FeaturePodmanDocker
DaemonNone (daemonless)dockerd runs as root
Default userRootlessRoot
CLI compatibilityDrop-in replacement (alias docker=podman)
systemd integrationNative (Quadlet)Requires extra config
SLES supportOn the installation DVDThird-party repository
SecurityNo root daemon, SELinux-readyRoot daemon exposure

The CLI is fully compatible — most docker commands work with podman. You can set the alias:

$ alias docker=podman

Try It: Parse JSON Without Installing Anything

Instead of the usual hello-world, let's verify rootless Podman with something useful.

Create a sample JSON file:

$ cat > ~/sample.json << 'EOF'
{"name":"David","company":"Transcend Solutions","role":"DevOps Engineer","skills":["Docker","Kubernetes","Linux"],"location":"Singapore","experience_years":15}
EOF

Normally you'd need to install jq to parse and pretty-print this JSON. With Podman, the tool comes with the container:

$ cat ~/sample.json | podman run ghcr.io/jqlang/jq '.'

The image pulls:

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

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:

$ cat ~/sample.json | podman run -i ghcr.io/jqlang/jq '.'

Now the output — beautifully formatted JSON:

{
  "name": "David",
  "company": "Transcend Solutions",
  "role": "DevOps Engineer",
  "skills": [
    "Docker",
    "Kubernetes",
    "Linux"
  ],
  "location": "Singapore",
  "experience_years": 15
}

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.

What's happening here:

FlagPurpose
-iKeep stdin open so jq can read the piped JSON
'.'The jq filter — . means "print everything, formatted"

What's Next

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, Tutorial Series: Levelling Podman Word Count: ~1,500

More from this blog

fosstechnotes

23 posts