How do Linux file permissions work? (And what are attributes?)
What this is
The permission model behind every Permission denied, every 403, and every "why can't the web server read my upload". It's a small, consistent system, ten minutes here and ls -l output becomes legible forever.
The model: three actions, three audiences
Every file and directory carries permissions for three actions, read (r), write (w), execute (x), granted separately to three audiences: the owner (one user), the group (one group of users), and others (everyone else). It also carries exactly one owner and one group, that's the whole state.
Reading ls -l:
-rw-r--r-- 1 www-data www-data 4096 Jul 2 10:00 index.php
drwxr-xr-x 2 deploy deploy 4096 Jul 2 10:00 uploads
First character: type (- file, d directory, l symlink). Then three triplets, owner/group/others: rw- r-- r-- means the owner reads and writes, everyone else reads. Then the owner and group names.
What r, w, x mean, files vs directories
- On a file: read the contents / change the contents / run it as a program.
- On a directory, the part that trips everyone:
r= list the names inside,w= create/delete/rename entries inside, andx= traverse, enter it, or reach anything through it. A file you can read inside a directory you can't traverse is unreachable, which is why a700home directory above a web root produces 403s with perfect file permissions.
Octal notation, decoded once
Each triplet is a number: r=4, w=2, x=1, summed. So:
644=rw-r--r--, the standard file: owner edits, world reads.755=rwxr-xr-x, the standard directory (and executable): owner full, world can enter/read.600/700, private file/directory, what.envfiles and~/.sshwant.777= everyone can do everything, the "fix" that's always wrong: on a server it means any compromised process can rewrite your code.
Changing things: chmod, chown, chgrp
chmod 644 file.php # set exact permissions (octal)
chmod u+x deploy.sh # symbolic: add execute for the owner
chmod -R g+w /var/www/shared # recursive, careful with -R and x!
chown www-data:www-data -R /var/www/site # owner and group together
One recursion trap: chmod -R 644 strips the x from directories and breaks traversal. The idiom that sets files and directories correctly in one pass is in the 403 guide (find -type d → 755, find -type f → 644).
The special bits: setuid, setgid, sticky
A fourth, leading octal digit covers three special cases you'll mostly read rather than set:
- setuid (4xxx,
sin the owner triplet): the program runs as its owner regardless of who launches it, howpasswdedits a root-owned file. On your own files, almost never; a stray setuid-root binary is a compromise artifact worth noticing. - setgid (2xxx): on a directory, new files inherit the directory's group instead of the creator's, genuinely useful for shared web roots (
chmod g+s /var/www/shared). - sticky (1xxx,
ton the end): in a world-writable directory, only a file's owner may delete it, why/tmpis1777and everyone's temp files survive each other.
umask: where default permissions come from
New files don't appear as 666/777, the umask subtracts bits at creation. The usual 022 yields 644 files and 755 directories, which is why fresh files are usually right without thought. If a service creates files your web server can't read, its umask (settable per systemd unit with UMask=) is the suspect.
Attributes: the layer beneath permissions
Separate from permissions, ext4 supports file attributes, flags on the file itself, managed with chattr and listed with lsattr. The two that matter in practice:
i(immutable):chattr +i file, the file can't be modified, deleted, or renamed by anyone, root included, untilchattr -i. Legitimate for lock-down-this-config; also a known malware persistence trick, when a file refuses deletion as root,lsattrit.a(append-only): writes can only add, never rewrite, occasionally used to protect logs.
If a permission mystery survives everything above, check lsattr before questioning reality.
A note on ACLs
When one-owner-one-group isn't expressive enough ("these two users, plus the web server, but nobody else"), POSIX ACLs add per-user/per-group entries on top: setfacl -m u:deploy:rw file, inspect with getfacl, and a + at the end of ls -l's permission block is the tell that ACLs are in play. Most single-admin servers never need them; knowing the + exists prevents one specific afternoon of confusion.
Still need help?
You can open a support ticket. So we can help on the first reply, it's worth mentioning:
- the VPS hostname or IP,
- the
ls -lline for the file or folder, and who needs which access.
Related questions
- "What do 644, 755, and 777 actually mean?"
- "Why does execute on a directory matter?"
- "What are setuid, setgid, and the sticky bit?"
- "Why can't root delete this file (chattr immutable)?"
- "What does the + at the end of ls -l permissions mean?"