Linux Error Guide: 'open: Permission denied' — Fix File and Directory Access
Fix the Linux open Permission denied EACCES error by checking mode bits, ownership, parent directory execute bits, ACLs, SELinux, and AppArmor denials.
- #linux
- #troubleshooting
- #errors
- #permissions
Stuck on this Linux Admins error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
Few messages are as common, or as deceptively simple, as a permission failure when a process tries to touch a file. The kernel returns the EACCES errno, and userland tools print it like this:
open: Permission denied
You will see the same underlying error surface as open('/path'): Permission denied, cannot open '/path' for reading: Permission denied, or in Python as PermissionError: [Errno 13] Permission denied. In every case the syscall (open, openat, read, or write) was rejected with errno 13.
The trap is assuming this is always about the file’s own mode bits. In practice EACCES has at least six distinct root causes: the file’s mode, the file’s ownership, a missing execute bit on a parent directory that blocks path traversal, POSIX ACLs, an SELinux AVC denial, or an AppArmor profile denial. This guide walks through each one and gives you a repeatable workflow to identify which is biting you.
Symptoms
- A service fails to start and its log shows
open(...): Permission deniedfor a config file, socket, or log path. - A user can
lsa directory but cannotcata file inside it, or vice versa. - The file mode looks correct (
-rw-r--r--) yet reads still fail. - The command works as
rootbut not as the service account, or works when SELinux is set to permissive. straceshows the failing syscall returning-1 EACCES (Permission denied).- On SELinux systems,
/var/log/audit/audit.loggains newtype=AVClines the instant you reproduce the failure.
Common Root Causes
- Mode bits. The requesting user lacks the read (
r) or write (w) permission in the applicable owner/group/other class. - Ownership. The file is owned by another user or group, so the process falls into the
otherclass with fewer rights than expected. - Missing execute bit on a parent directory. To reach
/a/b/c/file, the process needs the execute (search,x) bit on/a,/a/b, and/a/b/c. A single missingxanywhere in the chain yieldsEACCESeven if the file itself is world-readable. - POSIX ACLs. An extended ACL (shown by a
+inls -l) can grant or deny access independently of the classic mode bits. - SELinux. The DAC check passes but the mandatory access control layer denies the operation because the file’s security context does not match the process domain’s policy, logged as an AVC.
- AppArmor. On Debian/Ubuntu/SUSE, a confined process may be blocked by its profile even though DAC and ownership are fine.
Diagnostic Workflow
Start with the classic discretionary checks, then escalate to mandatory access control.
Inspect the file and confirm who you are:
ls -l /path/to/file
stat /path/to/file
id
Check every parent directory in one shot. namei -l resolves the full path and prints the mode of each component, which is the fastest way to spot a missing x bit mid-chain:
namei -l /path/to/file
Look for ACLs. A trailing + on the mode string (for example -rw-r-----+) means an ACL is present:
getfacl /path/to/file
If DAC looks correct, suspect SELinux. Check the label and recent denials:
getenforce
ls -Z /path/to/file
sudo ausearch -m avc -ts recent
sudo sealert -a /var/log/audit/audit.log
As a quick, reversible test, flip SELinux to permissive and retry. If the operation now succeeds, the root cause is a labeling or policy issue, not DAC:
sudo setenforce 0
# retry the failing operation
sudo setenforce 1
On AppArmor systems, check status and the kernel ring buffer for DENIED lines that name the profile and path:
sudo aa-status
sudo dmesg | grep -i apparmor
Example Root Cause Analysis
A team reported that nginx returned 403s while serving files from a freshly created /srv/webapp/public directory. The files were -rw-r--r-- and owned by the deploy user, so mode and ownership looked fine.
namei -l /srv/webapp/public/index.html told a different story:
namei -l /srv/webapp/public/index.html
The output showed /srv/webapp as drwxr-x---, owned by deploy:deploy. The nginx worker runs as user nginx, which fell into the other class and therefore had no execute (search) bit on /srv/webapp. It could not traverse into public at all, so every read failed with EACCES before the file’s own permissive mode ever mattered.
The fix was to grant traversal on the parent, without exposing a listing:
sudo chmod o+x /srv/webapp
Granting only the x bit (not r) lets processes pass through the directory to reach known paths while still preventing them from listing its contents. After this change the 403s stopped. Had ls -Z shown a mislabeled context instead, the correct fix would have been restorecon rather than a mode change.
Prevention Best Practices
- Fix ownership and mode, not just mode. When a service cannot read its data, set both:
chown -R app:app /var/lib/appandchmod 750 /var/lib/app. - Grant directory traversal deliberately. Use
chmod o+x(or a group withx) on parent directories rather than making files world-readable to work around a missing search bit. - Prefer ACLs over loosening base permissions. When one extra account needs access,
setfacl -m u:backup:r-- /pathis safer than widening theotherclass. - Relabel instead of disabling SELinux. If a file lives in the wrong context,
restorecon -Rv /pathrestores the default policy label; usechcononly for one-off tests. Leaving SELinux enforcing is the secure default. - Extend, do not disable, AppArmor profiles. Put a profile into
aa-complainwhile you gather the real denials, then translate them into explicit allow rules and return to enforce mode. - Reproduce as the real user. Run failing commands with
sudo -u serviceuserso you test the exact effective UID and groups the service uses.
Quick Command Reference
# Discretionary access control
ls -l /path/to/file # mode, owner, group
namei -l /path/to/file # mode of every parent component
stat /path/to/file # detailed metadata
id # effective uid/gid/groups
getfacl /path/to/file # POSIX ACL entries
# Fixes for DAC
chmod u+rw,g+r /path/to/file
chown app:app /path/to/file
chmod o+x /parent/dir # allow traversal only
setfacl -m u:name:r-- /path # grant one user read
# SELinux
getenforce
ls -Z /path/to/file
sudo ausearch -m avc -ts recent
sudo sealert -a /var/log/audit/audit.log
sudo restorecon -Rv /path/to/file
sudo setenforce 0 # temporary permissive test
# AppArmor
sudo aa-status
sudo dmesg | grep -i apparmor
sudo aa-complain /etc/apparmor.d/usr.sbin.app
Conclusion
open: Permission denied is not one problem but a family of them, and the fastest path to a fix is to rule out causes in order. Confirm the file’s mode and ownership, then use namei -l to catch the classic missing execute bit on a parent directory. If discretionary checks all pass, move to mandatory access control: inspect ls -Z and ausearch for SELinux AVCs, or aa-status and dmesg for AppArmor denials. Relabel with restorecon and extend profiles rather than disabling security layers wholesale. Working through these layers turns a vague errno 13 into a precise, one-line fix every time.
Fixed it? Get 500 Linux Admins & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.