Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Linux Admins By James Joyner IV · · 11 min read

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 denied even though you own the file.
  • open('/var/log/app.log'): Permission denied from a service.
  • cd: /data/private: Permission denied — a directory you cannot traverse.
  • A binary that runs fine from /usr/bin fails when copied to /tmp or an NFS mount.

Most Likely Causes of the ‘Permission denied’ Error

Production causes, most common first:

  1. Missing execute bit. A script or binary lacks +x (chmod +x was never run, or a checkout/unzip dropped the bit).
  2. 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.
  3. 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.
  4. noexec mount option. The filesystem holding the binary (often /tmp, /dev/shm, or an NFS share) is mounted noexec, so nothing there can execute regardless of mode.
  5. POSIX ACLs override the visible mode. ls -l shows a +, and a mask/ACL entry is denying access the base mode appears to allow.
  6. SELinux (RHEL/Rocky) or AppArmor (Ubuntu/Debian) denial. MAC policy blocks the access even when DAC permissions are correct.
  7. 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

  1. Add the execute bit (safe, specific):

    chmod +x ./app
  2. Correct ownership so the running user can access it:

    sudo chown appuser:appgroup /var/lib/app/data
  3. Grant search on path directories rather than opening the file wide:

    chmod o+x /data /data/private     # x = traverse, not read
  4. Remount without noexec, or place executables on a normally-mounted filesystem:

    sudo mount -o remount,exec /tmp

    Prefer moving the binary to /usr/local/bin over weakening /tmp hardening.

  5. Fix an ACL instead of the base mode when a + is present:

    setfacl -m u:appuser:rx ./app
  6. Clear the immutable bit if a file refuses writes:

    Warning: chattr -i removes deliberate write protection — confirm the file is meant to be mutable before clearing it.

    sudo chattr -i /etc/resolv.conf
  7. 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 777 and setenforce 0 as “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; git tracks +x; watch unzip, 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 /tmp and /dev/shm as noexec and 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.

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.

Free download · 368-page PDF

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.