## 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 EACCES or EPERM and 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 chown on 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+rwX for 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 x bit on the file, a valid shebang, and that the interpreter is executable.

- If the filesystem is mounted noexec , binaries will not run even with x set.

- Attributes and mounts:

- lsattr can reveal immutable ( i ) files that refuse writes.

- findmnt shows options like noexec that 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: x allows traversal, r lists names, w creates or deletes entries.

- Files: r reads, w writes, x executes.

- 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 rw and group r on 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-- ) or 0660 ( rw-rw---- )

- Directories commonly 0755 ( rwxr-xr-x ) or 0770 ( 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 at 0777 .

- Effective mode = base & ~umask

- Examples:

- umask 022 → files 0644 , dirs 0755

- umask 027 → files 0640 , dirs 0750

- umask 002 → files 0664 , dirs 0775 (good for group collaboration)

Set umask:

- For interactive shells: add umask 027 to ~/.profile or /etc/profile .

- For systemd services: add UMask=0027 under [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 chmod and chown changes 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.