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
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.
Download the Free 500-Prompt DevOps AI Toolkit
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.