Skip to main content

Linux File Permissions Explained — Who Can Do What?

· 7 min read
Goel Academy
DevOps & Cloud Learning Hub

Your deployment script fails at 2 AM with Permission denied. The container can't write to the log directory. A junior developer accidentally made a config file world-writable. Sound familiar? Understanding Linux file permissions is the difference between a secure, working system and a nightmare.

The Permission Model — rwx Decoded

Every file and directory in Linux has three sets of permissions for three types of users:

SymbolPermissionOn FilesOn Directories
r (4)ReadView contentsList contents
w (2)WriteModify contentsCreate/delete files inside
x (1)ExecuteRun as programEnter (cd into) directory
# Let's see permissions in action
ls -la /opt/myapp/
# drwxr-xr-x 2 deploy apps 4096 Feb 25 10:00 .
# -rwxr-x--- 1 deploy apps 2048 Feb 25 10:00 deploy.sh
# -rw-r--r-- 1 deploy apps 512 Feb 25 10:00 config.yaml
# -rw------- 1 deploy apps 256 Feb 25 10:00 secrets.env

Reading this output: -rwxr-x--- breaks down as:

  • - = regular file (d = directory, l = symlink)
  • rwx = owner can read, write, execute
  • r-x = group can read and execute
  • --- = others have zero access

chmod — Changing Permissions

Two ways to use chmod: numeric (octal) and symbolic. Both do the same thing.

# Numeric mode — most common in scripts
chmod 755 deploy.sh # rwxr-xr-x — executable script
chmod 644 config.yaml # rw-r--r-- — readable config
chmod 600 secrets.env # rw------- — owner-only secrets
chmod 700 /opt/myapp/bin # rwx------ — private directory

# Symbolic mode — easier to read
chmod u+x script.sh # Add execute for owner
chmod g-w config.yaml # Remove write from group
chmod o-rwx secrets.env # Remove all permissions from others
chmod a+r README.md # Add read for all (a = all)

# Recursive — apply to directory and everything inside
chmod -R 755 /var/www/html/

Here's the quick reference for common permission sets:

OctalSymbolicUse Case
755rwxr-xr-xExecutable scripts, public directories
644rw-r--r--Config files, HTML, public read
600rw-------Private keys, secrets, credentials
700rwx------Private directories, .ssh/
775rwxrwxr-xShared group directories
666rw-rw-rw-Never use this in production

chown and chgrp — Changing Ownership

Permissions don't matter if the wrong user owns the file. This is the most common cause of "Permission denied" in deployments.

# Change owner
sudo chown deploy deploy.sh

# Change owner and group
sudo chown deploy:apps /opt/myapp/config.yaml

# Recursive ownership change — critical for deployments
sudo chown -R www-data:www-data /var/www/html/

# Change group only
sudo chgrp docker /var/run/docker.sock

# Real scenario: Fix Nginx permission issues
sudo chown -R www-data:www-data /usr/share/nginx/html/
sudo find /usr/share/nginx/html/ -type d -exec chmod 755 {} \;
sudo find /usr/share/nginx/html/ -type f -exec chmod 644 {} \;

That last example is a pattern you'll use constantly: directories get 755 (need execute to enter), files get 644 (no execute needed).

Special Permissions — SUID, SGID, and Sticky Bit

These are the permissions most engineers don't understand but absolutely should.

SUID (Set User ID) — Octal 4000

When set on an executable, it runs as the file owner, not the user who launched it.

# Check which system binaries have SUID
find /usr/bin -perm -4000 -ls
# You'll see passwd, sudo, ping — they need root privileges

# The passwd command is owned by root with SUID
ls -la /usr/bin/passwd
# -rwsr-xr-x 1 root root 68208 Feb 25 10:00 /usr/bin/passwd
# Notice the 's' instead of 'x' in owner permissions

# Set SUID (be very careful with this)
sudo chmod u+s /opt/myapp/special-binary
sudo chmod 4755 /opt/myapp/special-binary

SGID (Set Group ID) — Octal 2000

On directories, new files inherit the directory's group instead of the creator's primary group. This is essential for shared project directories.

# Create a shared project directory
sudo mkdir /opt/shared-project
sudo chown root:developers /opt/shared-project
sudo chmod 2775 /opt/shared-project

# Now any file created inside inherits the 'developers' group
touch /opt/shared-project/newfile.txt
ls -la /opt/shared-project/newfile.txt
# -rw-r--r-- 1 vivek developers 0 Feb 25 10:00 newfile.txt

Sticky Bit — Octal 1000

On directories, only the file owner can delete their files — even if others have write access. The classic example is /tmp.

# Check /tmp — notice the 't' at the end
ls -ld /tmp
# drwxrwxrwt 15 root root 4096 Feb 25 10:00 /tmp

# Set sticky bit on a shared directory
sudo chmod 1777 /opt/shared-uploads
sudo chmod +t /opt/shared-uploads

# Users can create files but can't delete each other's files

umask — Default Permission Control

When you create a file, it doesn't get 777 by default. The umask subtracts permissions from the maximum.

# Check current umask
umask
# 0022 — typical default

# How it works:
# Files: 666 - 022 = 644 (rw-r--r--)
# Dirs: 777 - 022 = 755 (rwxr-xr-x)

# Test it
umask 0022
touch testfile && mkdir testdir
ls -la testfile testdir
# -rw-r--r-- testfile
# drwxr-xr-x testdir

# Set a stricter umask for security
umask 0077
touch securefile
ls -la securefile
# -rw------- securefile

# Make it permanent — add to ~/.bashrc or /etc/profile
echo "umask 0027" >> ~/.bashrc
umaskFile ResultDir ResultUse Case
0022644755Default, public readable
0027640750Group readable, no others
0077600700Owner only, maximum security

ACLs — When Basic Permissions Aren't Enough

Standard permissions give you owner, group, and others. But what if you need to give a specific user access without changing the group? That's where Access Control Lists (ACLs) come in.

# Install ACL tools (usually pre-installed)
sudo apt install acl

# Grant a specific user read access to a file
setfacl -m u:jenkins:rx /opt/myapp/deploy.sh

# Grant a group write access
setfacl -m g:qa-team:rw /opt/myapp/config.yaml

# View ACLs — notice the '+' in ls output
ls -la /opt/myapp/deploy.sh
# -rwxr-x---+ 1 deploy apps 2048 Feb 25 10:00 deploy.sh
getfacl /opt/myapp/deploy.sh

# Set default ACLs on a directory (inherited by new files)
setfacl -d -m g:developers:rwx /opt/shared-project/

# Remove ACLs
setfacl -x u:jenkins /opt/myapp/deploy.sh
setfacl -b /opt/myapp/deploy.sh # Remove all ACLs

Real-World Scenario: Fixing Deployment Permissions

Here's a complete script to set up proper permissions for a web application deployment:

#!/bin/bash
# fix-permissions.sh — Run after every deployment

APP_DIR="/var/www/myapp"
APP_USER="www-data"
APP_GROUP="www-data"
DEPLOY_GROUP="deploy"

# Set ownership
sudo chown -R ${APP_USER}:${APP_GROUP} ${APP_DIR}

# Directories: owner+group can enter, others can read
sudo find ${APP_DIR} -type d -exec chmod 750 {} \;

# Files: owner can read/write, group read only
sudo find ${APP_DIR} -type f -exec chmod 640 {} \;

# Scripts need execute permission
sudo find ${APP_DIR}/bin -type f -exec chmod 750 {} \;

# Secrets — owner only
sudo chmod 600 ${APP_DIR}/.env
sudo chmod 600 ${APP_DIR}/config/secrets.yaml

# Logs directory — writable by app
sudo chmod 770 ${APP_DIR}/logs/

# Allow deploy group to update files
sudo setfacl -R -m g:${DEPLOY_GROUP}:rwx ${APP_DIR}
sudo setfacl -R -d -m g:${DEPLOY_GROUP}:rwx ${APP_DIR}

echo "Permissions fixed successfully."

Quick Troubleshooting Checklist

When you hit "Permission denied," check these in order:

# 1. Who am I running as?
whoami
id

# 2. Who owns the file?
ls -la /path/to/file

# 3. What groups am I in?
groups

# 4. Are there ACLs?
getfacl /path/to/file

# 5. Is the parent directory traversable?
namei -l /path/to/file

The namei command is a hidden gem — it shows permissions for every directory in the path. If any directory along the way lacks execute permission for your user, you can't reach the file, even if the file itself has the right permissions.


Next up: Linux Process Management — learn how to find and fix that runaway process eating 100% CPU at 3 AM.