Linux & Hosting

Linux file permissions explained: chmod, chown and the numbers

Updated August 2026

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 fileOn a directory
r readview contentslist the names inside
w writemodify contentscreate, delete, rename entries inside
x executerun it as a programenter 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:

ModeSymbolicMeaningTypical use
644rw-r--r--owner edits, everyone readsordinary files, web content
755rwxr-xr-xowner full, everyone read+enter/rundirectories, scripts
600rw-------owner onlysecrets, .env, SSH keys
700rwx------owner only (dir)~/.ssh
777rwxrwxrwxeveryone can do everythingalmost 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; 077 for 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 /tmp is why you can't delete other people's temp files). Rarely needed day-to-day; nice to recognise when ls shows an s or t.