🇺🇸 JULY 4TH SALE! 20% OFF on All Plans - Limited Time! Use JULY4TH20 Claim Offer →

How to Fix SELinux Permission Denied Errors

Published On: 20 June 2026

Objective

This guide shows you how to diagnose and fix SELinux permission denied errors step by step. By the end, you will know how to:

  • Read SELinux denial logs and understand what they mean
  • Identify the exact cause of a denial (wrong context, boolean, or port)
  • Apply the correct fix without disabling SELinux
  • Verify the fix works in enforcing mode

You configured a service. It should work. Instead you get a permission denied error even though the file permissions look correct. SELinux is blocking it silently in the background. This is one of the most common problems on RHEL systems and one of the most misunderstood. Most people disable SELinux to make the error go away. That is the wrong fix. Here is the right one.

First: Confirm SELinux Is Actually the Problem

Before touching SELinux, confirm it is the cause. Temporarily set SELinux to permissive mode. In permissive mode, SELinux logs denials but does not enforce them.

setenforce 0

Now test your service or action again. Two possible outcomes:

  • It works in permissive mode: SELinux is the cause. Proceed with this guide.
  • It still fails in permissive mode: SELinux is not the cause. Check standard file permissions, firewall rules, or service misconfiguration instead.

Once confirmed, set SELinux back to enforcing before making any fixes:

setenforce 1

Never leave SELinux in permissive mode on a production system. Fix the actual issue instead.

Step 1: Find the Denial in the Audit Log

Every SELinux denial is logged in /var/log/audit/audit.log. Use ausearch to find recent AVC (Access Vector Cache) denials:

# Show denials from the last 10 minutes
ausearch -m avc -ts recent

# Show all denials from today
ausearch -m avc -ts today

A typical denial looks like this:

type=AVC msg=audit(1234567890.123:456): avc: denied { read } for
pid=1234 comm="httpd" name="index.html"
scontext=system_u:system_r:httpd_t:s0
tcontext=unconfined_u:object_r:user_home_t:s0
tclass=file permissive=0

How to read this output:

  • comm="httpd": the process that was denied (Apache web server)
    • This tells you which service is having the problem
  • denied { read }: the action that was blocked
    • Other common actions: write, execute, open, getattr
  • scontext=httpd_t: the SELinux context of the process making the request
    • This is the subject: who is trying to do something
  • tcontext=user_home_t: the SELinux context of the target file
    • This is the object: what is being accessed
    • Apache expects to read files labeled httpd_sys_content_t, not user_home_t
  • permissive=0: SELinux is in enforcing mode and actively blocked this action

The problem in this example is clear: the file has the wrong label. Apache is trying to read it but the context does not match what Apache is allowed to access.

Step 2: Get a Human-Readable Explanation

The sealert tool translates audit log entries into plain language and suggests fixes. Install it if it is not already present:

dnf install setroubleshoot-server -y

Then run it against the audit log:

sealert -a /var/log/audit/audit.log

What sealert gives you:

  • A plain-language description of what was denied and why
  • A confidence-rated list of suggested fixes (the top suggestion is usually correct)
  • The exact commands to run to apply each fix
  • An explanation of what each fix does

Read the suggestions carefully. Understand what a fix does before running it. The top suggestion is usually right but not always.

Step 3: Identify Which Fix Applies

SELinux permission denied errors have 3 common causes. Each has a different fix.

Cause 1: Wrong File Context (most common)

The file or directory has the wrong SELinux label. This happens when:

  • You create files in a non-standard location
  • You copy files from another directory without preserving context
  • You move content that had a different original context
  • A filesystem relabel reverted a context change made with chcon

Check the current context of a file:

ls -lZ /var/www/html/index.html

If the context shows anything other than httpd_sys_content_t for a web file, it needs relabeling.

Fix A: Restore the default context (for standard locations)

# Restore context based on built-in file location rules
restorecon -Rv /var/www/html/

# Verify the context is now correct
ls -lZ /var/www/html/index.html

Fix B: Set a custom context that persists after relabel (for non-standard locations)

# Step 1: Add a persistent policy rule for the custom directory
semanage fcontext -a -t httpd_sys_content_t "/webdata(/.*)?"

# Step 2: Apply the rule to existing files
restorecon -Rv /webdata

# Step 3: Verify
ls -lZ /webdata

Important distinction:

  • Use Fix A when files are in a standard location like /var/www/html
  • Use Fix B when files are in a custom location like /webdata or /srv/web
    • Using only restorecon without semanage fcontext on a non-standard path will work until the next system relabel, then revert
    • Always use both commands together for a permanent fix

Cause 2: SELinux Boolean Is Disabled

Booleans are on/off switches that control what a service is permitted to do beyond its default policy. Many behaviors are disabled by default for security reasons.

List all booleans related to a service:

getsebool -a | grep httpd

Common booleans you will encounter:

  • Apache (httpd):
    • httpd_can_network_connect: allow Apache to make outbound network connections
    • httpd_can_network_connect_db: allow Apache to connect to databases
    • httpd_use_nfs: allow Apache to serve files from NFS mounts
    • httpd_use_cifs: allow Apache to serve files from Samba/CIFS mounts
    • httpd_enable_homedirs: allow Apache to access user home directories
  • FTP (ftpd):
    • ftpd_anon_write: allow anonymous FTP write access
    • ftpd_use_passive_mode: allow passive mode FTP connections
  • Samba:
    • samba_enable_home_dirs: allow Samba to share home directories
    • samba_export_all_rw: allow Samba read/write access to all directories

Fix: Enable the boolean persistently

# The -P flag makes the change persist across reboots
setsebool -P httpd_can_network_connect on

# Verify the boolean is now on
getsebool httpd_can_network_connect

Without -P, the boolean resets after reboot. Always use -P for permanent fixes.

Cause 3: Non-Standard Port

If a service is configured to listen on a port that SELinux does not associate with that service type, it will be blocked from binding to that port.

Check which ports SELinux currently allows for a service:

# List all ports allowed for HTTP
semanage port -l | grep http

# List all allowed ports for SSH
semanage port -l | grep ssh

If your Apache is configured to run on port 8090 and that port is not listed under http_port_t, SELinux will block it from starting.

Fix: Add the port to the SELinux allowed list

# Add port 8090 as an allowed HTTP port
semanage port -a -t http_port_t -p tcp 8090

# Verify the port now appears in the list
semanage port -l | grep http

Things to check before applying this fix:

  • Confirm the port is actually intentional and not a misconfiguration
  • Make sure the port is not already assigned to another service type
    • Run semanage port -l | grep 8090 to check
    • If it belongs to another type, use -m (modify) instead of -a (add)
  • Also open the port in firewalld if external access is needed

Step 4: Test and Verify

After applying a fix, always test in enforcing mode. Never test in permissive mode and assume the fix worked.

# Confirm enforcing mode is active
getenforce

# Restart the affected service
systemctl restart httpd

# Check service status
systemctl status httpd

# Check for any new denials
ausearch -m avc -ts recent

How to interpret the results:

  • No new AVC denials and service runs correctly: the fix worked
  • New AVC denials appear: there is a second issue. Read the new denial and repeat the process.
  • Service fails but no AVC denials: SELinux is no longer the cause. Check service logs with journalctl -xe

Quick Reference: SELinux Troubleshooting Commands

  • Check SELinux mode:
    • getenforce: shows Enforcing, Permissive, or Disabled
    • sestatus: shows full SELinux status including policy and mode
  • Find denials:
    • ausearch -m avc -ts recent: last 10 minutes
    • ausearch -m avc -ts today: everything today
    • sealert -a /var/log/audit/audit.log: human-readable with fix suggestions
  • File context:
    • ls -lZ /path/to/file: check current context
    • restorecon -Rv /path/: restore default context
    • semanage fcontext -a -t type_t "/path(/.*)?": add persistent custom context rule
    • semanage fcontext -l | grep /path: check existing context rules for a path
  • Booleans:
    • getsebool -a | grep service: list all booleans for a service
    • setsebool -P boolean_name on: enable boolean persistently
    • setsebool -P boolean_name off: disable boolean persistently
  • Ports:
    • semanage port -l | grep service: list allowed ports for a service
    • semanage port -a -t port_type -p tcp PORT: add a new allowed port
    • semanage port -m -t port_type -p tcp PORT: modify an existing port assignment

Full command reference for copy-paste use:

# Check mode
getenforce
sestatus

# Find denials
ausearch -m avc -ts recent
sealert -a /var/log/audit/audit.log

# File context
ls -lZ /path/to/file
restorecon -Rv /path/to/directory
semanage fcontext -a -t type_t "/path(/.*)?"
restorecon -Rv /path

# Booleans
getsebool -a | grep service_name
setsebool -P boolean_name on

# Ports
semanage port -l | grep service
semanage port -a -t port_type -p tcp PORT

What Not to Do

  • Do not disable SELinux
    • Setting SELINUX=disabled in /etc/selinux/config removes a critical security layer
    • It is never the right fix for a production system
    • On the RHCSA exam, any task that requires SELinux enforcing mode will be marked wrong if you disabled it
  • Do not use permissive mode as a permanent solution
    • Permissive mode is a diagnostic tool only
    • Find the denial, apply the proper fix, return to enforcing
    • A system running in permissive mode is not protected by SELinux
  • Do not use chcon for permanent fixes
    • chcon changes the context of a file directly but the change is wiped during a filesystem relabel
    • Use semanage fcontext followed by restorecon for permanent fixes
    • chcon is only useful for temporary testing to confirm a context change will fix the issue

SELinux and the RHCSA Exam

SELinux is one of the heaviest topics on the RHCSA and the one that fails the most candidates. What the exam tests:

  • Diagnosing a denial using ausearch and identifying the cause
  • Applying persistent context changes with semanage fcontext and restorecon
  • Enabling booleans persistently with setsebool -P
  • Adding non-standard ports to the SELinux allowed list
  • Completing all of the above with SELinux in enforcing mode throughout

Candidates who only know how to turn SELinux off will fail those tasks. The exam expects you to fix the actual issue, not work around it.

If you want to practice SELinux troubleshooting on a real RHEL 9 environment, LinuxCert.Guru has dedicated SELinux labs covering basics, policy management, and troubleshooting with auto-graded tasks and instant feedback.

Conclusion

SELinux permission denied errors always have a specific cause. The fix is never to disable SELinux. Here is the complete process:

  1. Confirm SELinux is the cause by testing in permissive mode, then returning to enforcing
  2. Find the denial using ausearch -m avc -ts recent
  3. Get a plain-language explanation with sealert -a /var/log/audit/audit.log
  4. Identify the cause:
    • Wrong file context: fix with semanage fcontext and restorecon
    • Disabled boolean: fix with setsebool -P
    • Blocked port: fix with semanage port -a
  5. Verify in enforcing mode by restarting the service and checking for new denials

Follow this process every time and SELinux stops being a source of frustration and becomes something you can troubleshoot in under 5 minutes.

Practice SELinux troubleshooting on real hands-on lab at LinuxCert.Guru → https://linuxcert.guru