Linux file permissions explained: chmod, chown and the numbers
This domain once mirrored the Linux Documentation Project's HOWTOs, where a generation learned this stuff. Consider this the 2026 edition of the permissions HOWTO — same model, still the answer to half of all "permission denied" errors.
Every file and directory on Linux has an owner, a
group, and three sets of permissions: what the owner may do, what
members of the group may do, and what everyone else may do. Once that sentence
clicks, ls -l output stops being hieroglyphics:
-rw-r--r-- 1 rob www-data 4096 Aug 15 10:12 index.html drwxr-xr-x 2 rob rob 4096 Aug 15 10:12 uploads │└┬┘└┬┘└┬┘ └owner └group │ │ │ └── others: r-- read only │ │ └───── group: r-- read only │ └──────── owner: rw- read + write └────────── type: - file, d directory, l symlink
r, w, x — and what they mean for directories
| On a file | On a directory | |
|---|---|---|
r read | view contents | list the names inside |
w write | modify contents | create, delete, rename entries inside |
x execute | run it as a program | enter it / access things inside by name |
The directory column is where intuition fails. A directory without
x is a locked room — you can't reach anything in it even if the files
themselves are wide open. That's why directories are almost always
r-x or rwx, and why "I gave the file 644 but the web
server still can't read it" is usually a parent directory missing x.
The numbers
Each trio is a 3-bit number: r=4, w=2,
x=1, added together. Three trios → three digits:
| Mode | Symbolic | Meaning | Typical use |
|---|---|---|---|
644 | rw-r--r-- | owner edits, everyone reads | ordinary files, web content |
755 | rwxr-xr-x | owner full, everyone read+enter/run | directories, scripts |
600 | rw------- | owner only | secrets, .env, SSH keys |
700 | rwx------ | owner only (dir) | ~/.ssh |
777 | rwxrwxrwx | everyone can do everything | almost never |
chmod: change permissions
chmod 644 index.html # numeric chmod 755 deploy.sh chmod u+x deploy.sh # symbolic: add execute for the owner chmod go-w config.php # remove write from group and others chmod -R u=rwX,go=rX /var/www/site # recursive: capital X = execute on dirs only
That last form is the professional way to fix a whole tree: files become
644, directories 755, in one command, without accidentally making every file
executable (which plain chmod -R 755 does).
chown: change owner and group
sudo chown rob:www-data index.html # owner rob, group www-data sudo chown -R www-data:www-data /var/www/site sudo chgrp docker /opt/stacks # group only
The three permission problems everyone actually hits
1. "Permission denied" from a web server
Nginx/Apache run as www-data. They need r on files,
rx on every directory from / down to them, and
w only where the app writes (uploads, cache). The right fix is
ownership and 644/755, not 777:
sudo chown -R rob:www-data /var/www/site sudo chmod -R u=rwX,g=rX,o= /var/www/site # you edit, server reads, others nothing sudo chmod -R g+w /var/www/site/uploads # server may write here only
2. Docker bind mounts owned by the wrong user
A container's process runs as some UID (often 1000, or 33, or root); the
files it creates in a bind mount are owned by that number, which maps to
whatever user has that UID on the host — or nobody. Symptoms: files owned by
1000 or systemd-coredump you can't edit, or a container
that can't write its own data directory. Fixes: many images accept
PUID/PGID environment variables (set them to your
user's — id -u, id -g); otherwise
chown -R <uid>:<gid> the mounted directory to match what
the container runs as. See Compose
basics.
3. SSH silently ignoring your keys
OpenSSH refuses keys if permissions are too loose — and it doesn't say so unless you read the server's auth log. The required shape:
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_ed25519 chmod 644 ~/.ssh/id_ed25519.pub
Why 777 is the wrong answer
It "fixes" the error by letting every user and process on the machine read,
change and delete the files. On a server that also runs a web application, that
turns any small bug in that app into a way to rewrite anything you own. The real
fix is always one of: wrong owner (chown), missing x on
a directory, or the wrong user running the process. Diagnose with
ls -la up the tree and ps -o user,cmd -C nginx (or
whatever the process is) — thirty seconds, and you learn something.
Two extras for completeness
umask— the default permissions new files get, expressed as bits to remove.022(→ files 644, dirs 755) is the usual default;077for a paranoid account.- setuid / setgid / sticky — a fourth digit
(
chmod 2775 shared/makes new files inherit the directory's group, handy for shared project folders;chmod 1777 /tmpis why you can't delete other people's temp files). Rarely needed day-to-day; nice to recognise whenlsshows ansort.