LearnRHCSA (EX200)Containers

Capstone: Rootless Web Service

podman - a hands-on Linux lab on a real virtual machine.

Build, deploy, and persist a rootless web container as a systemd user service. Bind mount with :Z for SELinux, map a port, enable linger, and verify it survives reboot. This is the exam pattern for container tasks.

Capstone: Rootless Web Service

Your progress is tracked automatically. Just type commands naturally and run check-progress to see your status.

The exam objective

> "Deploy a rootless container as a systemd service with persistent storage."

This capstone combines every container skill into the exact pattern tested on the RHCSA exam. You will pull an image, run it rootless with port mapping and a bind mount, generate a systemd unit, enable linger, and verify the entire stack survives a restart. This is the container question.

The exam pattern

Every RHCSA container task follows the same structure. Memorize these steps:

1. Pull the image
2. Create a host directory for persistent storage
3. Run the container: rootless, detached, named, port mapped, bind mount with :Z
4. Verify the service works (curl)
5. Generate a systemd user unit with --new
6. Install, daemon-reload, enable
7. Enable linger
8. Stop and remove the original container
9. Start via systemd
10. Verify again (curl)

If any step is missing, the grader catches it. Here is each step in detail.

Step-by-step walkthrough

1. Pull the image

podman pull registry.access.redhat.com/ubi9/httpd-24

Always use the full registry path. Do not rely on short names.

2. Prepare host storage

mkdir -p ~/web-content
echo "<h1>RHCSA Container Lab</h1>" > ~/web-content/index.html

The directory must exist before you run the container with a bind mount.

3. Run the container

podman run -d --name web -p 8080:80 \
  -v ~/web-content:/var/www/html:Z \
  registry.access.redhat.com/ubi9/httpd-24

Breaking down the flags:

Notice: no sudo. This is rootless. The container runs as your regular user.

4. Verify the service

curl http://localhost:8080

You should see your custom HTML. If you see a default page or an error, the bind mount or :Z flag is wrong.

5. Generate the systemd unit

mkdir -p ~/.config/systemd/user
podman generate systemd --new --name web > ~/.config/systemd/user/container-web.service

6. Install and enable

systemctl --user daemon-reload
systemctl --user enable container-web

7. Enable linger

loginctl enable-linger $(whoami)

8. Hand over to systemd

podman stop web
podman rm web
systemctl --user start container-web

9. Final verification

curl http://localhost:8080
systemctl --user is-active container-web
loginctl show-user $(whoami) | grep Linger

All three checks must pass: curl returns your content, service is active, linger is yes.

The checklist

Before moving on from a container exam question, verify every item:

Common mistakes in order of frequency

1. Missing :Z - Bind mount exists but SELinux blocks access. Container runs, content is wrong. 2. Missing enable-linger - Works while logged in, fails after reboot. 3. Missing --new - Generated unit references a dead container ID after reboot. 4. Using sudo - Task says rootless. Images in root store, user store is empty. 5. Missing daemon-reload - Unit file exists but systemd does not see it. 6. Wrong port - -p 80:8080 instead of -p 8080:80. Host port comes first.

Lab

Deploy a rootless web service from scratch. This lab tests the complete exam pattern.

Task 1: Pull the image and run with bind mount

podman pull registry.access.redhat.com/ubi9/httpd-24
mkdir -p ~/web-content
echo "<h1>Capstone Lab</h1>" > ~/web-content/index.html
podman run -d --name web -p 8080:80 \
  -v ~/web-content:/var/www/html:Z \
  registry.access.redhat.com/ubi9/httpd-24

Verify with curl http://localhost:8080. You should see "Capstone Lab".

Task 2: Verify persistent storage works

# Modify content on the host
echo "<h1>Updated Content</h1>" > ~/web-content/index.html
curl http://localhost:8080

The updated content should appear immediately. The bind mount is live, not a copy.

Task 3: Generate and install the systemd unit

mkdir -p ~/.config/systemd/user
podman generate systemd --new --name web > ~/.config/systemd/user/container-web.service
systemctl --user daemon-reload
systemctl --user enable container-web

Verify: systemctl --user is-enabled container-web should return "enabled".

Task 4: Enable linger and hand over to systemd

loginctl enable-linger $(whoami)
podman stop web
podman rm web
systemctl --user start container-web

The original container is gone. systemd now manages a new container.

Task 5: Verify the full stack survives

Run every check from the checklist:

curl http://localhost:8080
systemctl --user is-active container-web
systemctl --user is-enabled container-web
loginctl show-user $(whoami) | grep Linger
ls -Z ~/web-content/

Expected results: curl returns your content, service is active and enabled, linger is yes, SELinux labels show container_file_t. Run check-progress to confirm all tasks pass.

Reflection: On the exam, this entire sequence takes about 5 minutes once you have practiced it. The key is the order: run, verify, generate, install, linger, hand over, verify again. Every step builds on the previous one.

Quick Reference

Practice Capstone: Rootless Web Service in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.

More lessons in Containers