Knowledgebase › AlmaLinux SELinux — the gotchas you'll hit on your first VPS

AlmaLinux SELinux — the gotchas you'll hit on your first VPS

SELinux is on by default on AlmaLinux (and RHEL, Rocky, CentOS Stream). It enforces a mandatory access control policy on top of regular Unix permissions — even if a file is mode 0644 and owned by the right user, SELinux can still deny access if the security context doesn't match. This article covers the most common SELinux speed bumps on a new AlmaLinux VPS and how to fix them without just turning the whole thing off.

Is SELinux on?

getenforce
# Enforcing  → on, blocking violations
# Permissive → on, logging only (not blocking)
# Disabled   → off

On a fresh AlmaLinux VPS, expect Enforcing.

The two SELinux gotchas you'll hit first

1. nginx / Apache can't read files in a non-standard location

Move your website to /srv/www/yoursite, the web server can't open it, and the error log says "permission denied" even though the files are 0644 and owned by the right user. SELinux labels matter — files in /srv/ have a different context than files in /var/www/.

Fix:

# See the current context:
ls -Z /srv/www/yoursite/

# Apply the web-content context to the directory tree:
semanage fcontext -a -t httpd_sys_content_t "/srv/www/yoursite(/.*)?"
restorecon -Rv /srv/www/yoursite

# Verify nginx/Apache can now read it.

If semanage isn't installed: dnf install -y policycoreutils-python-utils.

2. A service won't bind to a non-standard port

You change nginx to listen on port 8080. SELinux blocks the bind because port 8080 isn't in the http_port_t set by default.

# See what ports are tagged for HTTP:
semanage port -l | grep http_port_t

# Add 8080 to the allowed list:
semanage port -a -t http_port_t -p tcp 8080

# Now restart nginx — it'll bind cleanly.

Diagnosing any SELinux denial

Two tools matter: ausearch (read the audit log) and sealert (translate denials into human-readable explanations).

# Recent denials:
ausearch -m AVC -ts recent

# Get a friendly explanation of each one:
dnf install -y setroubleshoot-server
sealert -a /var/log/audit/audit.log

sealert output usually ends with a specific command to allow the denied behavior — copy/paste it, run it, done.

The "I just want this one thing to work" pattern

If a service is being denied something obscure and you want a clean targeted fix rather than reading policy docs:

# Run the service in permissive mode temporarily:
audit2allow -a -M myapp_local
semodule -i myapp_local.pp

This generates a local policy module from recent denials and loads it. The denials stop, the service works. Read the generated policy before installing it — if it permits something obviously wrong (write access to /etc/shadow), don't install.

The "turn it off" option (and why you shouldn't)

Yes, you can do:

# Temporary (until next reboot):
setenforce 0

# Permanent:
nano /etc/selinux/config
# Set SELINUX=permissive (or =disabled)
reboot

This works and removes the friction. But you're also removing a real defense-in-depth layer that has caught real exploits — most notably, SELinux contained Heartbleed and Shellshock on RHEL/Alma systems where it was enforcing. The two-minute fix for a specific denial is almost always the right move over disabling system-wide.

If you must disable for some reason (a closed-source app that won't work otherwise), at least leave SELinux on in permissive mode — it'll log what would have been blocked, so you can see what's happening even if it doesn't enforce.

SELinux + Docker / Podman

Podman (preferred on AlmaLinux over Docker) handles SELinux labels automatically for bind mounts when you use the :Z or :z suffix:

podman run -v /data:/data:Z imagename
# :Z = relabel the host volume for use by this container only
# :z = relabel for shared use across containers

Without the suffix, the container often can't write to bind- mounted directories on AlmaLinux. Symptom: container starts, immediately fails on "permission denied" to its data dir.

Booleans — the lazy fix that's often correct

Many common patterns (allow httpd to send mail, allow httpd to make network connections to a database, allow tomcat to read from user homes) are pre-defined as SELinux booleans — just on/off switches.

# List all booleans:
getsebool -a | less

# Turn one on (permanently):
setsebool -P httpd_can_network_connect 1

If your "SELinux is blocking me" Google searches lead to a setsebool command, that's the cleanest fix — single toggle, no custom policy module.

Quick reference

GoalCommand
See current mode getenforce
Switch to permissive (live) setenforce 0
See file contexts ls -Z <path>
Reset file contexts to default restorecon -Rv <path>
Add a port to a service's allowed set semanage port -a -t <type> -p tcp <port>
List booleans getsebool -a
Toggle a boolean setsebool -P <name> 1
Read denials ausearch -m AVC -ts recent
Friendly denial explanation sealert -a /var/log/audit/audit.log

SELinux frustrates most people in the first hour, then becomes invisible once you know the patterns. Two days in, you'll forget it's there.

Also Read

« « Back

Powered by WHMCompleteSolution