Red Hat Certified System Administrator (RHCSA) #14 Managing containers: Podman, systemd integration (quadlet)

9 min read

If the 21-post RHEL hands-on track had you working directly with user management, storage, systemd, and SELinux, the natural next step is putting that intuition to the test in a certification exam. This post covers the last pillar of the RHCSA exam objectives: containers. RHEL’s container engine is not Docker but Podman, and RHCSA expects you to go beyond simply launching a container — you must be able to register that container with systemd so it starts automatically at boot, end to end, by hand.

This area doesn’t involve many commands, but two pitfalls decide whether you pass: rootless execution and systemd integration via quadlet. Both are easy to trip over, so in this post we’ll nail the exact procedure by typing it out ourselves.

Why Podman #

Since RHEL 8, Podman has been the default container engine in place of Docker. Podman is nearly command-compatible with Docker, so dropping podman in where you’d write docker mostly just works. But the real reasons RHCSA chose Podman are twofold.

  • There’s no daemon. Docker sends commands to a dockerd daemon running as root, whereas Podman launches container processes directly without any daemon. That makes managing containers with systemd a natural fit.
  • It supports rootless properly. A regular user can launch their own containers without root. RHCSA usually asks you to run a rootless container as a specific regular user, so this matters.

First, let’s check that the package is installed.

# Install Podman (usually included by default on RHEL 9)
sudo dnf install -y container-tools

# Check the version
podman --version

Registry login and working with images #

The official images RHEL provides live in the Red Hat registry. For registries that require authentication, log in first.

# Log in to the Red Hat registry (enter your account credentials)
podman login registry.access.redhat.com

# Login info is stored in a per-user authentication file
#   $XDG_RUNTIME_DIR/containers/auth.json

After logging in, search for and pull an image.

# Search for an image
podman search ubi9

# Pull an image (ubi9 from registry.access.redhat.com)
podman pull registry.access.redhat.com/ubi9/ubi

# List images pulled locally
podman images

podman search finds images in the configured registries, and podman pull stores the pulled image locally. On the exam it’s safer to specify the image name with the full registry path. A short name may pull the wrong image depending on the registry search order.

Running containers and basic commands #

Running a container in the background while mapping ports, volumes, and environment variables is the heart of the RHCSA container objective.

# Run in the background (-d), map a port (-p), assign a name (--name)
podman run -d --name web -p 8080:80 \
  registry.access.redhat.com/ubi9/httpd-24

# List running containers
podman ps

# Show all containers, including stopped ones
podman ps -a

# Check logs
podman logs web

# Run a command inside the container (enter a shell)
podman exec -it web /bin/bash

# Stop and remove
podman stop web
podman rm web

-d runs detached (in the background), -p host_port:container_port maps a port, and --name assigns a name so it’s easier to work with. With podman exec -it you can step inside a running container to check its state.

Volume mapping #

When you remove a container its internal data is gone, so for data you need to keep, mount a host directory as a volume.

# Prepare a host directory
mkdir -p ~/web-content
echo "hello from host" > ~/web-content/index.html

# Map a volume (-v host_path:container_path:Z)
podman run -d --name web -p 8080:80 \
  -v ~/web-content:/var/www/html:Z \
  registry.access.redhat.com/ubi9/httpd-24

You mount with -v host_path:container_path, and the trailing :Z is an easy RHCSA pitfall. RHEL has SELinux enabled, so adding :Z makes Podman automatically set the container_file_t context on that host directory. Leave it out and the container can’t access the volume, producing a permission error.

Passing environment variables #

# Pass environment variables (-e KEY=VALUE)
podman run -d --name db \
  -e POSTGRESQL_USER=appuser \
  -e POSTGRESQL_PASSWORD=secret \
  -e POSTGRESQL_DATABASE=appdb \
  -p 5432:5432 \
  registry.redhat.io/rhel9/postgresql-15

You pass environment variables with -e KEY=VALUE. Container images typically take their initial setup (user, password, database name) through such variables. Which variables an image requires is documented in the image’s documentation, so on the exam you just plug in the values the question gives you.

Rootless containers #

RHCSA almost always asks you to run a rootless container as a regular user. The key is simple: while logged in as that user, run podman commands without sudo.

# When root switches to a specific user, enter a login shell
sudo su - appuser

# Now run rootless as appuser (without sudo)
podman run -d --name web -p 8080:80 \
  registry.access.redhat.com/ubi9/httpd-24

# Only appuser's containers are visible
podman ps

Rootless comes with two caveats.

  • You can’t map ports below 1024 directly to the host. A rootless user is unprivileged, so using port 80 as the host port like -p 80:80 fails. That’s why the example above uses -p 8080:80 with a port at or above 1024.
  • Always switch users with a login shell. You must include the dash, as in su - user, so that the user’s environment variables (XDG_RUNTIME_DIR and the like) are set up correctly. Without it, rootless Podman won’t work.

systemd integration with quadlet #

This is the very task that decides whether you pass in the RHCSA container area. “Turn this container into a systemd service that starts automatically at boot” is a recurring question. From RHEL 9.3 on, quadlet is the recommended approach.

With quadlet, you write the container definition in a .container file, and systemd reads it and automatically generates a service. For a regular (rootless) user, you put the file under ~/.config/containers/systemd/.

# Create the quadlet directory for the rootless user
mkdir -p ~/.config/containers/systemd

Now write the .container file. The file name becomes the service name. For example, web.container becomes web.service.

# ~/.config/containers/systemd/web.container
[Container]
Image=registry.access.redhat.com/ubi9/httpd-24
PublishPort=8080:80
Volume=%h/web-content:/var/www/html:Z
Environment=KEY=VALUE

[Install]
WantedBy=default.target

Here’s what each key means.

  • Image=. The image to use (specifying the full registry path is recommended)
  • PublishPort=. Port mapping (equivalent to podman run -p)
  • Volume=. Volume mapping (equivalent to -v). %h expands to the user’s home directory, and :Z is added because of SELinux
  • Environment=. Environment variables (equivalent to -e)
  • [Install] WantedBy=. The auto-start target. It specifies which target systemd attaches this service to

After creating the file, have systemd read the new definition and start the service.

# Re-read the quadlet definitions to generate the service (rootless uses --user)
systemctl --user daemon-reload

# Start the service (file name web.container → service name web.service)
systemctl --user start web.service

# Check the status
systemctl --user status web.service

There are two spots where people commonly get stuck.

  • A quadlet service is not enabled through systemctl --user enable directly. Auto-start is controlled by the .container file’s [Install] WantedBy=, and daemon-reload picks it up. Trying to enable it manually can give you a “can’t enable a static service” error.
  • After editing a definition, you must run daemon-reload again for the change to take effect.

Auto-start at boot with loginctl enable-linger #

A rootless user’s systemd services by default run only while that user is logged in. When the user logs out, their user services shut down with them, so to keep the container up right after boot or while logged out, you need to enable linger.

# Enable linger for the user (requires root)
sudo loginctl enable-linger appuser

# Or the user can run it themselves
loginctl enable-linger

# Check the status (Linger must be yes)
loginctl show-user appuser | grep Linger

With enable-linger on, the user’s systemd instance starts at boot even when they aren’t logged in, and the rootless container service attached to it starts automatically along with it. Omitting this step in a “start the rootless container automatically at boot” question means the container won’t come up after a reboot and you lose points.

The whole flow, summarized #

Here is the complete procedure for auto-starting a rootless container with systemd at boot, all at once.

# 1) Enter a login shell as the target user
sudo su - appuser

# 2) Create the quadlet directory and .container file
mkdir -p ~/.config/containers/systemd
vi ~/.config/containers/systemd/web.container

# 3) Apply the definition to systemd and start it
systemctl --user daemon-reload
systemctl --user start web.service

# 4) Enable linger so it auto-starts at boot
loginctl enable-linger

Exam points #

Here are the points that decide your score in the RHCSA container area.

  • For rootless, enter a login shell with su - user, then run podman without sudo. Forgetting the dash and breaking the environment variables is a frequent mistake.
  • Rootless can’t use host ports below 1024. Map to 1024 or above, like -p 8080:80.
  • Add :Z to volumes. It’s for the SELinux context; leave it out and the container can’t access the volume.
  • For rootless, put quadlet files in ~/.config/containers/systemd/*.container. The file name is the service name.
  • Don’t enable a quadlet service. Auto-start is set by [Install] WantedBy=, applied with systemctl --user daemon-reload.
  • loginctl enable-linger is mandatory for auto-start at boot. Skip this one line and the container disappears after a reboot.

In particular, quadlet’s [Install] section and loginctl enable-linger are the two core scoring points of RHCSA container questions. Don’t stop at launching the container — always confirm that the container is still alive after a reboot.

Wrap-up #

What we nailed in this post:

  • Podman is a daemonless, rootless container engine. Highly command-compatible with Docker and natural for systemd integration
  • Working with images. Log in with podman login registry.access.redhat.com, manage images with search, pull, and images
  • Running containers. run -d -p -v -e for background, port, volume, and environment variables; ps, logs, exec, stop, rm for operations
  • Rootless. Enter a login shell as a regular user and run without sudo, ports at 1024 or above, :Z on volumes
  • quadlet integration. Put a [Container] definition in ~/.config/containers/systemd/*.container, then systemctl --user daemon-reload + start
  • Auto-start at boot. loginctl enable-linger keeps the rootless service running even after logout

Container integration with systemd extends into a broader operational context in the RHEL advanced track. Within the RHCSA scope, the goal is to achieve auto-start after reboot with quadlet and linger.

Next: exam tips #

With that we’ve made a full circle through all of RHCSA’s exam objectives. Tools and scripts, boot and operations, storage and file systems, packages and networking, users and security, and now containers — all learned by hand. What’s left is to sharpen the operational intuition to finish these tasks accurately within 2.5 hours.

In #15 Exam tips, time management, and commonly missed patterns, we’ll cover the order to tackle problems, a checking routine so you never miss making changes persistent, how to find man pages quickly, and the patterns candidates repeatedly get wrong — a strategy to comfortably clear the 210-point passing line.

X