Happy Friday! Today we are tackling the single most confusing topic in modern RHEL administration.
We are going to deploy a containerized service. Sounds easy, right? podman run and you're done?
Wrong.
Today, you must deploy a Rootless Container that integrates fully with the host system. This changes the rules of everything: networking, file permissions, and startup behavior. If you miss one flag, it fails silently.
You need to deploy an Nginx web server container.
The Constraints (The Nightmare):
web_dev. You are NOT allowed to run Podman as root/sudo./home/web_dev/html.The Map:
Rootless containers break standard rules. Use these maps to find your way back:
man podman-generate-systemd (Or man podman-systemd.unit for Quadlets)man loginctl (The secret to reboot survival)man podman-run (Look for the :Z option)You need to navigate 4 specific traps to make this work. How do you solve them?
Nginx wants to listen on port 80 inside the container. You map it to host port 80 (-p 80:80). It fails immediately "Permission Denied."
You map the volume: -v /home/web_dev/html:/usr/share/nginx/html. The container starts, but Nginx gives a 403 Forbidden because SELinux blocked access to the host files.
:?)You generated the Systemd unit file. You enabled it with systemctl --user enable container-nginx. You reboot the server. The container does not start. It only starts when you SSH in as web_dev.
loginctl command must you run to allow this user's services to start at boot without an active session?The container is running on a high port (e.g., 8080). You opened port 8080 in firewall-cmd, but external users still can't connect.
Good luck!
Hello
To answer the questions:
Trap 1:
The option of -p 8080:80 would allow for a non-root user to map from port 8080 on the host to the port 80 on the container.
Ports on the host numbered from 1-1023 are priveleged ports which require root level access to bind to a process.
Trap 2:
Adding an uppercase Z to the end of the volume mount as follows will resolve the SE Linux permissions issue:
-v /home/web_dev/html:/usr/share/nginx/html:Z
This will allow the container exclusive acccess to the bind mount. Using a lowercase z would permit other containers to share access to the bind mount.
Trap 3:
Using loginctl enable-linger will allow for the containerized service to run on system start or reboot, even if the user is not logged in.
Trap 4:
Running the command podman port <container-id> will show the port mapping for a running container. If a host was not specified, the container is assigned the broadcast address (0.0.0.0). This means that the container is accessible from all networks on the host machine.
It is also possible to assign a specific network on the host machine to the port mapping on a container by prefixing the port mapping with an specific network on the host. For example:
-p 192.168.0.100:8080:80 <container-id>
This would allow you to fine tune your firewall rules.
@Ad_astra good job covering all the traps !
Hi
Just trying my hands to untrap these traps.
Trap 1: The Network Block
Nginx wants to listen on port 80 inside the container. You map it to host port 80 (-p 80:80). It fails immediately "Permission Denied."
Why? And what port must you use on the host side to fix this without becoming root?
**On Linux, binding to privileged ports (<1024) requires root privileges. Rootless containers cannot bind host port 80 directly.Using a non-privileged port (≥1024) on the host side, e.g. 8080.
# podman run -d -p 8080:80 nginx
Nginx still listens on port 80 inside the container, but externally you connect via `http://host:8080`.
Trap 2: The SELinux Wall
You map the volume: -v /home/web_dev/html:/usr/share/nginx/html. The container starts, but Nginx gives a 403 Forbidden because SELinux blocked access to the host files.
The Fix: What one letter must you add to the end of your volume string to tell SELinux to automatically relabel the content for the container? (e.g., :?)
**This option ':Z' makes it private relabeling (exclusive to this container).
# podman run -d -p 8080:80 -v /home/web_dev/html:/usr/share/nginx/html:Z nginx
Trap 3: The Ghost Reboot
You generated the Systemd unit file. You enabled it with systemctl --user enable container-nginx. You reboot the server. The container does not start. It only starts when you SSH in as web_dev.
The Fix: What loginctl command must you run to allow this user's services to start at boot without an active session?
**Enable lingering for the user so their services can start at boot without an active login.
# loginctl enable-linger web_dev
Hence `systemd --user` for `web_dev` will run at boot, and the container auto-starts.
Trap 4: The Firewall (Bonus)
The container is running on a high port (e.g., 8080). You opened port 8080 in firewall-cmd, but external users still can't connect.
The Fix: Since rootless containers use slirp4netns/pasta, they don't always hit the standard tables. How do you verify the container is actually bound to the public interface?
**Using anyone of the below commands can shows whether the container is listening on `0.0.0.0:8080` (public) or only `127.0.0.1:8080` (local).
#ss -ltnp | grep 8080
#podman port <container_id>
Trap 1: The Network Block (Privileged Ports)
podman run -p 80:80 nginx ,, Fails with Permission denied because ports < 1024 are privileged and rootless Podman containers cannot acquire this capability.
So we need to use an unprivileged host port (≥1024), like 8080:
podman run -p 8080:80 nginx
Trap 2: The SELinux Wall
-v /home/web_dev/html:/usr/share/nginx/html
Container runs, but Nginx returns 403 Forbidden,, because SELinux labels /home/web_dev/html as user_home_t, which containers are not allowed to read.
We need to add :Z to the volume mount:
-v /home/web_dev/html:/usr/share/nginx/html:Z
To relabels the content to container_file_t , and makes it accessible only to this container
Trap 3: The Ghost Reboot (User Systemd Units)
Container does not start until web_dev logs in,,, because user systemd instances only start when the user has a login session.
So we need to enable lingering for the user:
loginctl enable-linger web_dev
to starts the user systemd instance at boot, runs containers without an interactive login
Trap 4: The Firewall
Container is running, and port 8080 is open in firewall-cmd ,, but external clients still cannot connect.
In this case we must confirm the port is actually bound to the public interface, not just localhost.
From the host: ss -tulnp | grep 8080
0.0.0.0:8080 or :::8080 >>>> this indicate that public
127.0.0.1:8080 → localhost only >>>external access fails
From Podman itself: podman port nginx
if the result as 80/tcp -> 0.0.0.0:8080 ,, so its good
If it shows 127.0.0.1, recreate the container with explicit binding:
-p 0.0.0.0:8080:80
Red Hat
Learning Community
A collaborative learning environment, enabling open source skill development.