About 80% of “Permission denied” errors on servers come from this.
This guide teaches you how to read permissions, understand owner/group,
and fix access issues without breaking security (avoiding the classic chmod 777).
Linux access control is basically two things: ownership (owner/group) + permissions (r/w/x). If either is wrong, your app, your service, or your CI/CD pipeline will fail.
The “truth” command:
ls -lah
# -l long format (permissions, owner, group, size, date)
# -a include hidden files (starting with .)
# -h human-readable sizes (K/M/G)
Typical examples:
-rw-r----- 1 ubuntu devops 120K Jan 18 app.log
drwxr-x--- 2 root devops 4.0K Jan 18 /opt/app
-rwxr-xr-x 1 ubuntu ubuntu 2.3K Jan 18 deploy.sh
- file | d directoryrw- (owner) r-- (group) --- (others)ubuntu devopsls -lah
whoami
id
That tells you: what permissions exist, who you are, and what groups you have.
On directories, r/w/x mean:
ls)cd) and “pass through” the directoryTwo styles: symbolic (readable) and numeric (fast). In DevOps you’ll see both.
You specify who is affected: u (owner), g (group), o (others), a (all).
chmod u+rwx file # u = owner
chmod g+rx dir # g = group
chmod o-r file # o = others
chmod a+r file # a = all
Values:
chmod 644 file # owner rw- (6), group r-- (4), others r-- (4)
chmod 600 secret # owner rw-, nobody else (great for keys/credentials)
chmod 755 script.sh # owner rwx, group rx, others rx (common executable)
chmod 750 /opt/app # owner rwx, group rx, others none (safer on servers)
x, it won’t run → you’ll get Permission denied.chmod +x script.sh or chmod 755 script.sh.chmod does not fix ownership. If the file/folder belongs to the wrong user/group, permissions alone may not solve your issue.
chown ubuntu file # change owner to ubuntu
chown ubuntu:devops file # owner ubuntu and group devops
chown -R ubuntu:devops /opt/app # recursive (common in deployments)
chownchmod
In DevOps, a service/pipeline usually runs as a specific user
(e.g., www-data, jenkins, deploy).
Instead of opening access to everyone, you define a group and grant access to the group.
groups # see your groups
id # see uid/gid and group memberships
# (as root) add a user to a group
usermod -aG devops ubuntu
# apply group access to app directory
chown -R root:devops /opt/app
chmod -R 770 /opt/app
Makes new files inherit the directory’s group. This prevents new files from landing in the wrong group and breaking access.
chmod g+s /opt/app
# you'll see an "s" in the group permission block when listing
Allows many users to write there, but prevents them from deleting each other’s files.
That’s why /tmp usually has the sticky bit set.
chmod +t /tmp
www-data).chown or groups./var/log/app missing w for the right user/group.x → chmod +x or chmod 755.ls -lah first: it shows owner/group/permissions.777. Use groups + 770/750 + setgid for shared directories.chown fixes ownership; chmod fixes permissions.Real scenario: your pipeline (or deploy user) tries to write logs into a directory created by provisioning (root), and it fails with Permission denied.
/tmp so you don’t break anything.# 1) Create typical app folders
mkdir -p /tmp/perm-lab/app/{bin,logs,config}
cd /tmp/perm-lab
# 2) Create a simple "start script"
cat > /tmp/perm-lab/app/bin/start.sh << 'EOF'
#!/bin/bash
echo "App started at $(date)" >> /tmp/perm-lab/app/logs/app.log
echo "OK"
EOF
# 3) Inspect the structure
ls -lah /tmp/perm-lab/app/bin
ls -lah /tmp/perm-lab/app/logs
# Simulate provisioning that created everything as root
sudo chown -R root:root /tmp/perm-lab/app
sudo chmod -R 700 /tmp/perm-lab/app
# Try running the script (it should fail)
bash /tmp/perm-lab/app/bin/start.sh
You should see something like:
Permission denied
whoami
id
ls -lah /tmp/perm-lab/app
ls -lah /tmp/perm-lab/app/bin
ls -lah /tmp/perm-lab/app/logs
Ask yourself:
start.sh?logs/ allow write access?
✅ Professional fix: use a group (e.g., devops) and grant access to the group.
That way your deploy/pipeline user can operate without opening the directory to everyone.
# 1) Create devops group if it doesn't exist (safe if it already exists)
sudo groupadd devops 2>/dev/null || true
# 2) Add your user to the devops group
sudo usermod -aG devops "$USER"
# 3) Change group ownership (keep root as owner)
sudo chown -R root:devops /tmp/perm-lab/app
# 4) Full access for owner+group, none for others
sudo chmod -R 770 /tmp/perm-lab/app
# 5) Ensure group inheritance for new files (setgid)
sudo chmod g+s /tmp/perm-lab/app /tmp/perm-lab/app/logs
# 6) Ensure script is executable (in case it lost +x)
sudo chmod 770 /tmp/perm-lab/app/bin/start.sh
# 7) Retry
bash /tmp/perm-lab/app/bin/start.sh
# 8) Check the log
cat /tmp/perm-lab/app/logs/app.log
Why this works:
x (execute).logs directory is writable for the group.777).Note: if group changes don’t apply immediately, open a new terminal or run:
newgrp devops