Linux file permissions control who can read, write, and execute files and directories. Every file has an owner, a group, and a set of permission bits. Understanding chmod, chown, umask, and special bits (SUID, SGID, sticky) is fundamental to Linux system administration and application security — see the Linux cheat sheet for the broader command reference.
How Do Linux Permissions Work?
Each file has three permission groups — owner (u), group (g), and others (o) — and three permission types — read (r), write (w), and execute (x). The ls -l command displays them as a 10-character string.
-rwxr-xr-- 1 alice devs 4096 Apr 12 10:00 deploy.sh
│└┬┘└┬┘└┬┘
│ │ │ └── others: r-- (read only)
│ │ └───── group: r-x (read + execute)
│ └──────── owner: rwx (read + write + execute)
└────────── file type: - (regular file), d (directory), l (symlink)
Permission values:
r (read) = 4
w (write) = 2
x (execute) = 1
Octal sum per group:
rwx = 4+2+1 = 7
r-x = 4+0+1 = 5
r-- = 4+0+0 = 4
→ 754 in octal notationHow Do You Use chmod?
chmod changes file permissions using either octal notation (numeric) or symbolic notation (letters). Octal is more concise; symbolic is more readable for targeted changes.
# Common octal permission values
chmod 755 script.sh # rwxr-xr-x — executable by everyone, writable by owner
chmod 644 config.yaml # rw-r--r-- — readable by everyone, writable by owner
chmod 600 id_ed25519 # rw------- — owner only (SSH private keys)
chmod 700 .ssh/ # rwx------ — owner only (SSH directory)
chmod 666 shared.txt # rw-rw-rw- — readable/writable by everyone (rarely correct)
chmod 400 secrets.env # r-------- — read-only by owner
# Recursive — apply to all files and directories
chmod -R 755 /var/www/html/Strict chmod 600 on private keys is mandatory — OpenSSH refuses to load a key that is group- or world-readable. The SSH keys guide covers the full ~/.ssh permission map.
# Symbolic format: [ugoa][+-=][rwxXsst]
# u=owner, g=group, o=others, a=all
# Add execute permission for the owner
chmod u+x deploy.sh
# Remove write permission from group and others
chmod go-w config.yaml
# Set exact permissions — owner rw, group r, others nothing
chmod u=rw,g=r,o= secret.conf
# Add execute only to directories (capital X)
chmod -R a+X /var/www/ # Adds execute to dirs but not files
# Make a file immutable to group write
chmod g-w important.log
# Multiple operations in one command
chmod u+rwx,g+rx,o-rwx deploy.shHow Do You Change Ownership with chown?
chown changes the owner and/or group of a file. Only root can change the owner; regular users can change the group to one they belong to.
# Change owner
sudo chown alice deploy.sh
# Change owner and group
sudo chown alice:devs deploy.sh
# Change group only (note the colon)
sudo chown :devs deploy.sh
# Alternative: change group with chgrp
sudo chgrp devs deploy.sh
# Recursive — change ownership of all contents
sudo chown -R www-data:www-data /var/www/html/
# Common pattern: set web server ownership
sudo chown -R nginx:nginx /usr/share/nginx/html/What Is umask and How Does It Work?
umask sets the default permission mask for newly created files and directories. It subtracts permissions from the maximum defaults (666 for files, 777 for directories).
# View current umask
umask # e.g., 0022
umask -S # symbolic: u=rwx,g=rx,o=rx
# How umask works:
# Files: 666 - 022 = 644 (rw-r--r--)
# Directories: 777 - 022 = 755 (rwxr-xr-x)
# Common umask values
umask 022 # Default — files 644, dirs 755 (readable by all)
umask 027 # Restrictive — files 640, dirs 750 (no access for others)
umask 077 # Private — files 600, dirs 700 (owner only)
# Set in shell profile (~/.bashrc or ~/.zshrc) for persistence
echo 'umask 027' >> ~/.bashrc
# Temporary umask for a single command
(umask 077 && touch secret.key) # Created with 600 permissionsWhat Are SUID, SGID, and Sticky Bit?
Special permission bits modify how files and directories behave beyond the standard read/write/execute model.
| Bit | Octal | On Files | On Directories |
|---|---|---|---|
| SUID | 4000 | Runs as file owner (e.g., passwd runs as root) | No effect |
| SGID | 2000 | Runs as file group | New files inherit directory's group |
| Sticky | 1000 | No effect (modern Linux) | Only file owner can delete their files |
# SUID — the 's' in owner execute position
chmod u+s /usr/bin/passwd
ls -l /usr/bin/passwd
# -rwsr-xr-x ← 's' means SUID is set
# SGID on a directory — new files inherit group
chmod g+s /shared/projects/
ls -ld /shared/projects/
# drwxrwsr-x ← 's' in group execute position
# Sticky bit — commonly set on /tmp
chmod +t /tmp/
ls -ld /tmp/
# drwxrwxrwt ← 't' in others execute position
# Using octal — prepend the special bit
chmod 4755 program # SUID + rwxr-xr-x
chmod 2775 shared-dir/ # SGID + rwxrwxr-x
chmod 1777 /tmp/ # Sticky + rwxrwxrwx
# Find all SUID files on the system (security audit)
find / -perm -4000 -type f 2>/dev/nullWhat are Linux capabilities (and how do they replace SUID)?
Capabilities split the all-or-nothing power of root into ~40 independent privileges. Instead of running a service as root or marking the binary SUID, grant only the one capability it needs. Binding a non-root web server to port 80 used to require SUID or a privileged user — with cap_net_bind_service the binary keeps every other restriction.
# Inspect capabilities on a binary
getcap /usr/bin/ping
# /usr/bin/ping cap_net_raw=ep
# Let a non-root binary bind to ports < 1024
sudo setcap cap_net_bind_service=+ep /usr/bin/myapp
# Drop all capabilities from a binary
sudo setcap -r /usr/bin/myapp
# Show the current shell's capability set
capsh --print
# Find every file with capabilities set (audit)
sudo getcap -r / 2>/dev/null| Capability | What it grants |
|---|---|
| CAP_NET_BIND_SERVICE | Bind sockets to TCP/UDP ports below 1024 |
| CAP_SYS_ADMIN | The "kitchen sink" — mount, swapon, namespaces. Avoid; usually too broad. |
| CAP_DAC_OVERRIDE | Bypass file read/write/execute permission checks |
| CAP_CHOWN | Make arbitrary changes to file UIDs and GIDs |
How do you make a file immutable with chattr?
Beyond the standard mode bits, ext4/XFS/Btrfs expose extended attributes via chattr. The two most useful are +i (immutable — even root cannot delete or modify the file without clearing the bit first) and +a (append-only — useful for log files an attacker should not be able to truncate).
# Make a file immutable — no rename, delete, or write (even by root)
sudo chattr +i /etc/resolv.conf
# Inspect attributes
lsattr /etc/resolv.conf
# ----i--------e------- /etc/resolv.conf
# Append-only — cannot truncate or overwrite, only append
sudo chattr +a /var/log/audit.log
# Remove the immutable / append-only bit
sudo chattr -i /etc/resolv.conf
sudo chattr -a /var/log/audit.logHow Do ACLs Extend Standard Permissions?
POSIX Access Control Lists (ACLs) let you grant permissions to specific users or groups beyond the single owner/group model. They are essential when multiple teams need different access levels to the same directory.
# View ACLs on a file
getfacl project/
# Grant read+write to a specific user
setfacl -m u:bob:rw project/report.txt
# Grant read to a specific group
setfacl -m g:auditors:r project/report.txt
# Set default ACL on a directory (inherited by new files)
setfacl -d -m g:devs:rwx /shared/code/
# Remove a specific ACL entry
setfacl -x u:bob project/report.txt
# Remove all ACLs (revert to standard permissions)
setfacl -b project/report.txt
# Recursive — apply ACL to all existing contents
setfacl -R -m g:devs:rwx /shared/code/
# The '+' in ls -l indicates ACLs are present
ls -l report.txt
# -rw-rw-r--+ 1 alice devs 1024 Apr 12 10:00 report.txtNote: ACLs require a filesystem that supports them (ext4, XFS, Btrfs). Mount with acl option if not enabled by default. The effective mask (mask::rwx) limits the maximum ACL permissions — check it when permissions seem wrong despite correct ACL entries.
How do permissions interact with Docker bind mounts?
Containers and the host share the kernel UID space. Bind-mount a host directory and files inside the container appear owned by whatever UID the container process uses — usually 0 (root) by default — which leaves files on the host owned by root after the container exits. Run the container as your own user with --user $(id -u):$(id -g) (or set USER in the Dockerfile) so created files match your host account. For a fuller treatment of dev-loop file ownership and bind mounts, see the dev containers guide.
References
- chmod(1) — Linux manual page — full octal and symbolic mode reference.
- chown(1) — Linux manual page — change file owner and group.
- umask(2) — Linux manual page — the file mode creation mask system call.
- acl(5) — Linux manual page — POSIX Access Control Lists, mask, and default ACLs.
- capabilities(7) — Linux manual page — every capability and the SUID-replacement model.
- chattr(1) — Linux manual page — extended attributes including immutable and append-only.
Key Takeaways
- • Permissions are expressed as three groups (owner, group, others) with read (4), write (2), execute (1)
- • Use
chmod 755for executables,644for config files,600for secrets - •
umask 022is the standard default — use027or077for tighter security - • SGID on directories ensures new files inherit the directory's group ownership
- • Sticky bit on shared directories prevents users from deleting each other's files
- • ACLs extend the owner/group model to support fine-grained per-user and per-group permissions
- • Audit SUID files regularly — they run with elevated privileges and are a common attack vector
Translate between octal, symbolic, and human-readable permissions with the chmod calculator tool.