Linux permissions: chmod, chown and umask made clear
Reading ls -l
ls -l script.sh
-rwxr-xr-- 1 alice devs 4096 Jul 1 10:00 script.shBreak -rwxr-xr-- into 1 + 3 + 3 + 3 characters:
-: the type —-file,ddirectory,lsymlink.rwx: the owner (alice) can read, write, execute.r-x: the group (devs) can read and execute, not write.r--: others can only read.
Three blocks, always in the same order: user, group, others. r = read, w = write, x = execute (for a directory, x means the right to enter it).
Octal notation
Each right has a value: r = 4, w = 2, x = 1. Add them up per block: rwx = 7, r-x = 5, r-- = 4. Hence the classics:
- 755 (
rwxr-xr-x): scripts and directories — the owner does everything, others read and traverse. - 644 (
rw-r--r--): regular files — the owner edits, others read. - 600 (
rw-------): secrets (SSH keys,.env) — nobody but you.
chmod: changing rights
chmod 644 notes.txt # octal : fixe tout d'un coup / octal: set everything at once
chmod u+x deploy.sh # symbolique : ajoute x au propriétaire / symbolic: add x for the owner
chmod g-w,o-r config.yml # retire w au groupe, r aux autres / remove w from group, r from others
chmod -R 755 /var/www/site # récursif sur un dossier / recursive on a directoryTwo syntaxes, same result. Octal (chmod 644) replaces all permissions at once — ideal when you know the final state you want. Symbolic (u+x, g-w) only changes what you name: u/g/o/a for user/group/others/all, +/-/= to add/remove/set.
chown and chgrp: changing ownership
sudo chown alice fichier.txt # change le propriétaire / change the owner
sudo chown alice:devs fichier.txt # propriétaire ET groupe / owner AND group
sudo chgrp devs fichier.txt # groupe seulement / group only
sudo chown -R alice:devs /srv/app # récursif / recursivechmod sets which rights; chown sets who they apply to. Changing the owner requires sudo (you can't "give away" a file). The user:group form does both in one command.
The classic Docker "Permission denied"
Your container crashes with Permission denied on its mounted volume? It's almost always a UID mismatch: Linux doesn't compare usernames, it compares numbers. If the container process runs as UID 1000 but the host directory belongs to root (UID 0), the container can't write — names don't matter.
The fix: hand the host directory to the UID the container uses (stated in the image docs, or visible with docker exec <ctn> id).
docker exec mon-conteneur id # → uid=1000 gid=1000
sudo chown -R 1000:1000 ./app-data # aligne le dossier hôte sur l'UID du conteneur
# align the host dir with the container UIDumask in two sentences
umask defines the permissions removed by default from every file you create: with the common umask 022, a file is born as 644 (666 − 022) and a directory as 755 (777 − 022). That's why your new files are never executable or group-writable unless you ask.
⚠️ Don't chmod -R 777
chmod -R 777 "fixes" the error by giving every right to everyone — including any compromised process on the machine. It's a band-aid that hides the real problem (wrong ownership) and leaves a lasting hole.
Instead: identify who needs access (ls -l, id, docker exec ... id), then fix ownership with chown and keep rights minimal (755/644, 600 for secrets). If several users must write, create a shared group rather than opening up to everyone.