Intro
Linux file permissions control who can read, write, or execute files and directories. When you hit a "Permission denied" error, the fastest path to resolution is a clear, repeatable workflow. This guide shows how to:
- Identify the exact failing path and operation
- Read ownership and modes correctly
- Use chmod and chown safely with least privilege
- Understand umask so defaults are correct at file creation time
- Validate access for services in production-like conditions
Workflow Overview
Use this sequence to cut noise and reduce rework:
- Reproduce and capture the failure
- Note the command and path: which file or directory, and which operation (read, write, execute, list, create, delete).
- If the message is generic, trace filesystem calls locally:
strace -e trace=file -f -o /tmp/trace.log your_command
- Look for
EACCESorEPERMand the failing path.
- Inspect the path and its parents
- Show the target and each parent directory:
namei -l /path/to/thing
- Inspect details:
ls -ld /path /path/to /path/to/thing
stat -c "%A %a %U:%G %n" /path/to/thing
- Check mount options that can block execution:
findmnt -no TARGET,OPTIONS --target /path/to/thing
noexec blocks running binaries; nodev and nosuid affect device and suid bits.
- Check immutable attribute (rare but painful):
lsattr /path/to/thing
If set, clear it before writing:
chattr -i /path/to/thing
- Confirm the acting identity
- For interactive shells:
whoami
id
id -Gn
- For a service:
systemctl show -p User,Group,SupplementaryGroups your.service
ps -o user,group,egroup,egid= -C your-binary
(or by PID)
- Decide the minimal fix
- Ownership mismatch: prefer
chownon the smallest subtree, not the whole filesystem. - Missing group access: use a shared group and setgid on the directory.
- Mode too strict: add only needed bits (often
g+rwXfor collaborators). - Mode too loose: remove world access (
o-rwx) and rely on user/group.
- Set correct defaults
- If new files keep coming in with bad permissions, adjust umask or enforce group inheritance with setgid on shared directories.
- Validate as the intended user
sudo -u appuser -g appgroup -- test -r /path && echo readable
sudo -u appuser -g appgroup -- test -w /path && echo writable
sudo -u appuser -g appgroup -- test -x /path/to/bin && echo executable
Diagnose Permission denied
Most denials come from one of three causes: missing execute on a directory you need to traverse, missing read or write on the target file, or a mount/attribute preventing the operation.
Checklist:
- Traverse permission on every parent directory:
namei -l /srv/app/data/file.txt
Ensure x on each directory for the acting user or group.
- Read/Write on the leaf:
stat -c "%A %a %U:%G %n" /srv/app/data/file.txt
- Execution failures:
- If a script: ensure
xbit on the file, a valid shebang, and that the interpreter is executable. - If the filesystem is mounted
noexec, binaries will not run even withxset.
- Attributes and mounts:
lsattrcan reveal immutable (i) files that refuse writes.findmntshows options likenoexecthat block execution.
Example: A Python script fails with Permission denied. namei -l shows the parent directory /srv/app has mode 750 and group appdata, but the user is not in that group. Adding the user to appdata and re‑logging in fixes the traversal.
Ownership, groups, and modes
Permissions are evaluated in order:
- Owner (user) bits
u=rwx - Group bits
g=rwx(if the process is in the file's group) - Others bits
o=rwx
Key points:
- Directories:
xallows traversal,rlists names,wcreates or deletes entries. - Files:
rreads,wwrites,xexecutes. - Effective group matters: ensure the process is in the group you expect (
id -Gn). - Use the minimal scope when fixing: operate on the path that is actually failing.
Helpful inspections:
ls -ld /srv/app /srv/app/data
stat -c "%A %a %U:%G %n" /srv/app/data
namei -l /srv/app/data/report.csv
Use chmod safely
Avoid blanket changes. Prefer symbolic modes that express intent:
- Grant owner
rwand groupron files and directories, remove world:
chmod -R u+rwX,g+rX,o-rwx /srv/app/data
- Make a script executable by owner and group:
chmod ug+x /srv/app/bin/run.sh
- Set directory for collaboration: read, write, and traverse for group:
chmod 2770 /srv/shared
The leading 2 sets setgid so new files inherit the directory's group.
Numeric reference examples:
- Files commonly
0644(rw-r--r--) or0660(rw-rw----) - Directories commonly
0755(rwxr-xr-x) or0770(rwxrwx---)
Avoid:
chmod -R 777 ...(overexposes data and can mask real ownership issues)- Removing execute from directories you still need to traverse
Use chown and groups
Ownership fixes should match responsibility.
- Set owner and group precisely:
chown app:appdata /srv/app/data
chown -R app:appdata /srv/app/data/logs # scope only what is needed
- Create and use a collaboration group:
groupadd appdata
usermod -aG appdata alice
usermod -aG appdata app
Users must re‑login to gain new group membership (or use newgrp appdata).
- Enforce group inheritance in shared dirs:
chmod g+s /srv/app/shared
New files will get the directory's group automatically.
- Create directories with the right owner, group, and mode in one step:
install -d -o app -g appdata -m 2770 /srv/app/shared
umask fundamentals
umask defines which permission bits are removed from the default when new files or directories are created.
- Files start at
0666; directories start at0777. - Effective mode = base
&~umask - Examples:
umask 022→ files0644, dirs0755umask 027→ files0640, dirs0750umask 002→ files0664, dirs0775(good for group collaboration)
Set umask:
- For interactive shells: add
umask 027to~/.profileor/etc/profile. - For systemd services: add
UMask=0027under[Service]in the unit file, then:
systemctl daemon-reload
systemctl restart your.service
Tip: Combine UMask= with a setgid project directory to get predictable, shared defaults.
Validate service access
Services often run as non‑login users, so always test as that identity.
- Discover service user and groups:
systemctl show -p User,Group,SupplementaryGroups your.service
- Verify working directory, data, and binaries:
sudo -u app -g appdata -- test -r /srv/app/config.yml && echo readable
sudo -u app -g appdata -- test -w /srv/app/data && echo writable
sudo -u app -g appdata -- test -x /srv/app/bin/run && echo executable
- Check path parents for traversal:
sudo -u app -- namei -l /srv/app/data/report.csv
- Confirm mounts do not block execution:
findmnt -no TARGET,OPTIONS --target /srv/app/bin
If permissions look correct yet denials persist, a mandatory access control system (like SELinux or AppArmor) may be involved. That is outside this file permission guide, but the symptoms often appear similar.
Local Pilot Plan
Start small so you can inspect results and roll back easily.
- Create a test user, group, and directories
groupadd appdata
useradd -m -G appdata app
install -d -o app -g appdata -m 2770 /srv/app-test/shared
install -d -o app -g appdata -m 0750 /srv/app-test/logs
- Set defaults with umask for collaboration
sudo -u app -- bash -c 'umask 002; touch /srv/app-test/shared/pilot.txt; mkdir /srv/app-test/shared/subdir'
Verify new files are 0664 and dirs are 0775, and group is appdata:
stat -c "%a %G %n" /srv/app-test/shared/pilot.txt /srv/app-test/shared/subdir
- Validate access paths
sudo -u app -g appdata -- touch /srv/app-test/shared/pilot.txt
namei -l /srv/app-test/shared/pilot.txt
sudo -u app -g appdata -- test -w /srv/app-test/logs && echo logs writable
- Exercise failure and fix
chmod g-w /srv/app-test/logs
sudo -u app -g appdata -- test -w /srv/app-test/logs || echo denied as expected
chmod g+w /srv/app-test/logs
Keep a simple command log so you can reproduce or roll back.
- Document commands and results
When the pilot behaves as intended, apply the same minimal changes to the real paths.
Conclusion
Use a systematic workflow to solve "Permission denied" errors:
- Identify the failing path and operation
- Inspect ownership, groups, and modes on every directory level
- Apply minimal
chmodandchownchanges with least privilege - Set predictable defaults with
umask(and setgid where useful) - Validate as the real service user before wider rollout
Start with a narrow local pilot, confirm results, then scale to production-like paths with confidence.