Linux Error: Permission denied — Cause, Fix, and Troubleshooting Guide
How to fix the Linux 'Permission denied' error (EACCES): file modes, ownership, ACLs, noexec mounts, missing execute bit, SELinux and AppArmor denials explained.
- #linux
- #troubleshooting
- #permissions
- #security
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.
Summary
Permission denied is the kernel’s EACCES (errno 13): the process’s credentials do not satisfy the access check for the file or directory it touched. Usually it is a plain mode/ownership problem — a missing execute bit, a file owned by another user, or a directory in the path you cannot traverse. But when the classic chmod/chown checks all pass and it still fails, the cause is deeper: a noexec mount, a POSIX ACL, or a Mandatory Access Control denial from SELinux or AppArmor.
Common Symptoms
bash: ./app: Permission denied-bash: /usr/local/bin/tool: Permission deniedeven though you own the file.open('/var/log/app.log'): Permission deniedfrom a service.cd: /data/private: Permission denied— a directory you cannot traverse.- A binary that runs fine from
/usr/binfails when copied to/tmpor an NFS mount.
Most Likely Causes of the ‘Permission denied’ Error
Production causes, most common first:
- Missing execute bit. A script or binary lacks
+x(chmod +xwas never run, or a checkout/unzip dropped the bit). - Wrong ownership or group. The file is owned by
root(or another user) and the running process is not, and the mode does not grant the “other” class access. - A non-traversable directory in the path. You need
x(execute/search) on every directory component to reach a file, even if the file itself is world-readable. noexecmount option. The filesystem holding the binary (often/tmp,/dev/shm, or an NFS share) is mountednoexec, so nothing there can execute regardless of mode.- POSIX ACLs override the visible mode.
ls -lshows a+, and a mask/ACL entry is denying access the base mode appears to allow. - SELinux (RHEL/Rocky) or AppArmor (Ubuntu/Debian) denial. MAC policy blocks the access even when DAC permissions are correct.
- Immutable bit (
chattr +i) preventing writes to a file that otherwise looks writable.
Quick Triage
# Who am I, and what are the file's mode/owner?
id
ls -l ./app
# Can I traverse the whole path? (x on every component)
namei -l ./app
# Is the filesystem mounted noexec?
mount | grep "$(df --output=target ./app | tail -1)"
Diagnostic Commands
ls -l ./app
Shows mode, owner, and group. A trailing + (e.g. -rw-r--r--+) signals ACLs are in play. No x in the owner triad means it is not executable.
stat ./app
Numeric mode (0644), owner/group UID/GID, and the containing device — useful to correlate with mount options.
id
Your UID, GID, and supplementary groups. Compare against the file’s owner/group to see which permission class (user/group/other) applies to you.
namei -l /path/to/app
Walks and prints the permissions of every path component. A directory without x (search) for you anywhere along the path blocks access — the most-missed cause.
getfacl ./app
Dumps POSIX ACLs. A restrictive mask:: or a user:someone:--- entry can deny access the base mode seems to permit.
mount | grep noexec
findmnt -no OPTIONS /tmp
Reveals noexec (and nosuid, nodev) mount options. A binary on a noexec filesystem gets Permission denied at exec regardless of +x.
lsattr ./app
Shows extended attributes; an i flag (immutable) blocks writes/deletes even for root until cleared with chattr -i.
# SELinux (RHEL/Rocky)
ls -Z ./app
sudo ausearch -m AVC -ts recent
# AppArmor (Ubuntu/Debian)
sudo aa-status
sudo dmesg | grep -i 'apparmor.*DENIED'
When DAC checks all pass, MAC is the remaining suspect. ls -Z shows the SELinux context; recent AVCs and AppArmor DENIED lines in dmesg confirm a policy block.
Fix / Remediation
-
Add the execute bit (safe, specific):
chmod +x ./app -
Correct ownership so the running user can access it:
sudo chown appuser:appgroup /var/lib/app/data -
Grant search on path directories rather than opening the file wide:
chmod o+x /data /data/private # x = traverse, not read -
Remount without
noexec, or place executables on a normally-mounted filesystem:sudo mount -o remount,exec /tmpPrefer moving the binary to
/usr/local/binover weakening/tmphardening. -
Fix an ACL instead of the base mode when a
+is present:setfacl -m u:appuser:rx ./app -
Clear the immutable bit if a file refuses writes:
Warning:
chattr -iremoves deliberate write protection — confirm the file is meant to be mutable before clearing it.sudo chattr -i /etc/resolv.conf -
Handle MAC denials via the dedicated guides — do not blanket-disable security. For SELinux, generate a targeted policy or fix the context; for AppArmor, adjust the profile. See the linked guides below.
Warning: Avoid
chmod -R 777andsetenforce 0as “fixes.” Recursive 777 is a security hole, and disabling SELinux masks the real policy gap. Scope permissions to the exact user and path.
Validation
ls -l ./app # execute bit / ownership correct
namei -l ./app # every path component traversable
sudo -u appuser ./app # runs as the intended service user
findmnt -no OPTIONS "$(df --output=target ./app | tail -1)" # no noexec
Prevention
- Preserve execute bits through packaging (tarballs keep modes;
gittracks+x; watchunzip, which does not). - Run services as a dedicated, least-privilege user and own their data directories to that user.
- Keep executables off
noexec-mounted paths; harden/tmpand/dev/shmasnoexecand put binaries in/usr/local/bin. - Use ACLs for shared-directory access instead of loosening base modes.
- Keep SELinux
Enforcing/ AppArmor enabled and write proper policy rather than disabling them.
Related Errors
- Linux Error: Operation not permitted
- Linux Error: Text file busy
- Linux Error: command not found
- SELinux AVC denial
- AppArmor “DENIED” operation
Final Notes
Permission denied (EACCES) is a DAC failure first: check the execute bit, the owner/group against your id, and search permission on every directory in the path with namei -l. When those all look right, move to the second tier — noexec mounts, ACLs (getfacl), the immutable bit, and finally SELinux/AppArmor. Fix the specific gap; never reach for chmod -R 777 or setenforce 0.
Want faster Linux incident response? Use DevOps AI Toolkit to turn production errors into clear diagnostics, remediation steps, and reusable runbooks.
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.