Permission Denied Errors
A runbook for diagnosing 'Permission denied' errors on Linux — file permissions, ownership, directory traversal permissions, ACLs, and SELinux/AppArmor.
Overview
“Permission denied” is usually straightforward once you check the right thing — but there are several distinct causes (file permissions, directory traversal permissions, ACLs, and security modules like SELinux) that all produce the exact same error message.
The Problem
A process or user gets Permission denied reading, writing, or executing a file that “should” be accessible.
Investigation
ls -l /path/to/file # basic owner/group/permission check
stat /path/to/file # full metadata
# Check EVERY directory in the path — traversal (x) permission matters at every level
namei -l /path/to/file
# Who is actually running the process?
ps -ef | grep <process_name> # confirm the actual UID/user the process runs as
id <username> # confirm that user's UID/GID and group memberships
# ACLs — permissions beyond the basic owner/group/other model
getfacl /path/to/file
# SELinux (RHEL/CentOS/Fedora) — a COMPLETELY separate permission layer from standard Unix permissions
getenforce # Enforcing / Permissive / Disabled
ls -Z /path/to/file # SELinux context of the file
ausearch -m avc -ts recent # recent SELinux denials from the audit log
# AppArmor (Ubuntu/Debian) equivalent
aa-status
dmesg | grep -i apparmor
Root Cause Possibilities
- Wrong owner/group — the file is owned by a different user/group than the one the process runs as.
- Missing execute (traversal) permission on a PARENT directory — you can have full permissions on the file itself but still get denied if any directory in the path lacks
xfor that user. - The process runs as an unexpected user — e.g., a service configured to run as
www-datatrying to access a file only readable byroot. - SELinux/AppArmor denial — standard Unix permissions look correct, but a security module is blocking the access at a different layer entirely.
- A restrictive ACL — an ACL entry (
getfacl) overriding the expected owner/group/other behavior. - Filesystem mounted read-only — writes fail with permission-like errors when the underlying mount is
ro.
Resolution
# Fix ownership
sudo chown www-data:www-data /path/to/file
# Fix a missing traversal permission on a parent directory
chmod +x /path/to/parent_dir
# Fix an ACL issue
setfacl -m u:www-data:rx /path/to/file
# SELinux: restore the expected default context (often the actual fix, not chmod!)
sudo restorecon -v /path/to/file
# Or, if a new/nonstandard path genuinely needs a policy change:
sudo semanage fcontext -a -t httpd_sys_content_t "/path/to/dir(/.*)?"
sudo restorecon -Rv /path/to/dir
# Read-only mount
mount | grep /path/to/mount # confirm it's actually ro
sudo mount -o remount,rw /path/to/mount # if intentional to fix immediately (understand WHY it was ro first)
Prevention
- On SELinux-enabled systems, always check
getenforce/ausearch -m avcBEFORE spending time on standardchmod/chown— a huge number of “permission denied” issues on RHEL-family systems are SELinux denials with completely correct standard Unix permissions. - Use
namei -lhabitually when a permission error doesn’t make sense fromls -lon the file alone — traversal permission on a parent directory is easy to overlook. - Avoid
chmod 777/broad ACL grants as a “fix” — they mask the real cause and create a security gap; find and fix the specific mismatch instead. - Document which user each service runs as, so ownership issues can be diagnosed by checking against that expectation, not guessed at.
Summary
Standard Unix permissions (ls -l, namei -l for the full path, id for the process’s actual user) resolve most permission-denied cases. On SELinux/AppArmor-enabled systems, check the security module’s own logs/status FIRST, since standard permissions can look entirely correct while the actual denial happens at that separate layer — restorecon is often the real fix there, not chmod.
Pro Tip
If chmod 777 “fixes” a permission error on a RHEL/CentOS/Fedora system and it’s still denied, that’s a strong signal the real blocker is SELinux, not standard Unix permissions — check getenforce and ausearch -m avc -ts recent before touching permissions any further.
Add More Questions to This Guide
Know a question that should be here? Share it and help the community!
Open Google Form