Linux and Bash for Developers · Files, Disks and Permissions
Permission Denied After a Deploy
Diagnose permission errors from file modes, directory execute bits and umask, and fix them without chmod 777.
Scenario
A deploy script copies a new release into /srv/app/releases/<version> and restarts the service. The service fails with Permission denied reading its config, and the health check script won't run. Someone fixes it with chmod -R 777 /srv/app, and the security team opens a ticket the next week.
What you see in production
Permission deniedfor a file thatlsshows clearly exists.- Scripts fail with
Permission deniedeven though they're readable. - Files created by the deploy user have different permissions than files created by hand.
The naive code
umask 077 # set earlier in the deploy user's profile
cp -r build/ /srv/app/releases/1.4.0
chmod -R 777 /srv/app # "fixes" it
Reproduce it
Three common causes: a restrictive umask on new files, a directory without the execute (search) bit, and a script without the execute bit:
umask 077
mkdir -p release/config
echo "db_host=db" > release/config/app.ini
echo 'echo healthy' > release/health.sh
stat -c '%A %n' release/config/app.ini release/health.sh
umask 022
# 1. A directory you can read but not "search" (x) hides its files
chmod 644 release/config
cat release/config/app.ini 2>&1 | sed 's/^cat: //'
# 2. A script without the execute bit
./release/health.sh 2>&1 | sed 's/^.*: line [0-9]*: //'
# 3. A file other users can't read (simulated with a check)
[[ $(stat -c %a release/health.sh) == 600 ]] && echo "health.sh is 600: the service user (another user) can't read it"
Output
-rw------- release/config/app.ini -rw------- release/health.sh release/config/app.ini: Permission denied ./release/health.sh: Permission denied health.sh is 600: the service user (another user) can't read it
Run this on your computer — see “Get Started” for setup.
Root cause
- umask decides the permissions of new files and directories. With
077, everything the deploy creates is private to the deploy user, so the service user can't read it. - A directory's execute bit controls whether you can access files inside it; read alone only lists names.
- Running
./scriptneeds the execute bit;bash scriptdoesn't.
The fix
Set explicit, minimal permissions in the deploy, and give the service user access through ownership or a group, not through "everyone":
umask 022
mkdir -p release/config
echo "db_host=db" > release/config/app.ini
echo 'echo healthy' > release/health.sh
chmod 755 release release/config # dirs: owner rwx, others r-x (search)
chmod 640 release/config/app.ini # config: owner rw, group r (the service's group), others nothing
chmod 755 release/health.sh # script: executable
stat -c '%A %a %n' release release/config release/config/app.ini release/health.sh
./release/health.sh
Output
drwxr-xr-x 755 release drwxr-xr-x 755 release/config -rw-r----- 640 release/config/app.ini -rwxr-xr-x 755 release/health.sh healthy
Run this on your computer — see “Get Started” for setup.
- Set
umask 022(or027) explicitly in deploy scripts instead of relying on profiles. - Use
install -m 640 -o app -g app src dstto copy with the right owner and mode in one step. - Grant the service user access through a shared group (
chgrp app, mode640/750) or ownership. - Secrets:
600or640, owned by the service user.
Trade-offs
chmod 777fixes the error by letting any user or compromised process change your code and config.- Group-based access needs the service user in the group and a restart for new group membership to apply.
- ACLs (
setfacl) give finer control but are easy to forget during audits.
Detect it in production
- Check permissions after deploys:
find /srv/app -perm -o+wshould print nothing. namei -l /srv/app/current/config/app.inishows the permissions of every directory along a path.- Run the service's health check as the service user in CI or staging (
sudo -u app ...).
Key points
- Directories need the execute bit to reach the files inside.
- umask controls new file permissions; set it explicitly in automation.
- Grant access by owner or group with minimal modes, never 777.
Further reading: chmod(1) · umask(2) · Bash tutorial: permissions
Practice questions
Why can't you cat a file inside a directory with mode 644?
Answer
Directories need the execute (search) bit to access the entries inside; read only allows listing names.
What does umask 077 do to newly created files?
Answer
It removes all group and other permissions, so new files are readable and writable only by their owner.
Why is chmod -R 777 a bad fix?
Answer
It lets every user and any compromised process modify the files, including code and configuration; grant access with ownership or a group instead.
Open in the app to mark lessons complete, take notes and track your progress.