Why this matters
You will constantly meet permissions written as numbers: chmod 600 ~/.ssh/id_ed25519, "the uploads directory must be 755", install -m 0644. You need to read and write that form as fast as the letters. You also need to know why new files get the permissions they get (the umask), and how permissions on directories work, because they behave differently from files and cause most of the genuinely confusing "Permission denied" errors.
Octal notation
Each permission triplet is three bits, and three bits make exactly one octal digit (0 to 7). Give each letter a value and add:
r = 4 w = 2 x = 1
rwx r-x r--
4+2+1 4+0+1 4+0+0
7 5 4 → 754
| Digit | Letters | Typical use |
|---|---|---|
| 7 | rwx | owner of a directory or script |
| 6 | rw- | owner of a data file |
| 5 | r-x | others on a directory or program |
| 4 | r-- | others on a data file |
| 0 | --- | no access |
The modes you will see most:
| Mode | Letters | Use |
|---|---|---|
| 644 | rw-r--r-- | ordinary files |
| 755 | rwxr-xr-x | directories, programs, scripts |
| 600 | rw------- | private files (keys, passwords) |
| 700 | rwx------ | private directories (~/.ssh) |
| 640 / 750 | shared with a group, hidden from others |
Octal chmod sets all nine bits at once, which is exactly what you want when you know the target state; symbolic chmod is better for "add or remove one thing, leave the rest":
$ chmod 640 report.txt
$ stat -c '%a %A %n' report.txt
640 -rw-r----- report.txt
A leading zero (0644) is allowed and means the same; a fourth digit in front sets the special bits described at the end of this lesson.
umask: the default permissions
Why do new files come out as 644 and new directories as 755? Programs ask the kernel for a mode when they create something: touch, editors and > redirections ask for 666 (rw for everyone), mkdir asks for 777. The kernel then removes the bits listed in the process's umask:
requested 666 rw-rw-rw- 777 rwxrwxrwx
umask 022 ----w--w- 022 ----w--w-
result 644 rw-r--r-- 755 rwxr-xr-x
The umask is a mask of bits to clear (formally mode & ~umask). It is not subtraction: a mask of 7 on a 6 gives 0, not -1. Open the shell for this lesson:
$ umask
0022
$ umask -S
u=rwx,g=rx,o=rx
$ umask 077
$ touch private.txt; mkdir privdir
$ ls -ld private.txt privdir
-rw-------. 1 learner learner 0 Nov 1 16:00 private.txt
drwx------. 1 learner learner 0 Nov 1 16:00 privdir
$ umask 022
Like the current directory, the umask is a per-process property, inherited by children, and umask is therefore a builtin. Setting it in a script affects only that script; to change your default, set it in ~/.bashrc or ~/.profile. Common values: 022 (others can read), 027 (others nothing), 077 (fully private).
Note that no umask produces an executable data file: since programs ask for 666, the x bits are never there to begin with. Compilers and chmod +x add them deliberately.
Directory permissions
A directory is a list of names. Its bits control operations on that list:
| Bit | On a directory it allows |
|---|---|
r | reading the list of names (ls) |
w | changing the list: create, delete, rename entries (needs x too) |
x | searching (traversing): using a name to reach its inode; cd into it; accessing anything below |
The x bit is the important one. To open /work/site/public/index.html, the kernel resolves the path one component at a time (module 2), and it needs x on every directory along the way: /, /work, /work/site, /work/site/public. Then it checks the file's own r.
Try the combinations on a directory you own:
$ mkdir lab && echo hi > lab/f.txt
$ chmod 700 lab; ls lab; cat lab/f.txt
f.txt
hi
$ chmod 600 lab # r and w, no x
$ ls lab
ls: cannot access 'lab/f.txt': Permission denied
f.txt
$ cat lab/f.txt
cat: lab/f.txt: Permission denied
$ chmod 300 lab # w and x, no r
$ ls lab
ls: cannot open directory 'lab': Permission denied
$ cat lab/f.txt
hi
$ chmod 755 lab
- With
rbut nox, you can see names but not use them. - With
xbut nor, you can use names you already know but not discover them. Web servers' home directories sometimes use this (711): the server can reach~/public_html, but nobody can list your home.
Deleting is a directory operation
Remember from the last lesson: removing a file edits the directory, so it needs w and x on the directory, not any permission on the file. That leads to a security problem for shared directories like /tmp, where everybody needs w: anyone could delete anyone's files.
Special bits
A fourth octal digit, in front of the other three, holds three special bits. You will mostly read them rather than set them.
$ ls -ld /tmp /usr/bin/passwd
drwxrwxrwt. 1 root root 0 Nov 1 13:59 /tmp
-rwsr-xr-x. 1 root root 118168 Apr 19 2025 /usr/bin/passwd
- Sticky bit (1000, shown as
tin the other-execute position): on a directory, only a file's owner (or the directory's owner, or root) may delete or rename it. That makes/tmp(mode 1777) safe to share. - setuid (4000,
sin the owner-execute position): when an executable with this bit is run, the new process gets the file owner's UID instead of the caller's./usr/bin/passwdis owned by root and setuid, so when you run it, it runs as root and can update/etc/shadow, but only in the narrow way its code allows (your own password, after checking the old one).sudoandsuwork the same way. Setuid programs are prime targets for attackers, which is why there are few of them, and why the kernel ignores setuid on scripts. - setgid (2000,
sin the group-execute position): on an executable, run with the file's group; on a directory, new files inside inherit the directory's group instead of the creator's primary group, which is handy for team directories.
This sandbox runs with "no new privileges", so even passwd cannot actually gain root here.
Capital X: execute only where it makes sense
A tree mixes directories (which need x) and data files (which should not have it). chmod -R a+x would make everything executable. The capital X adds execute only to directories and to files that already have an execute bit for someone:
$ chmod -R u=rwX,go=rX project/
That one line gives directories 755, ordinary files 644, and keeps scripts runnable. It is the correct way to "fix permissions" on a copied tree.
Common mistakes
- Treating the umask as subtraction. It clears bits. 666 with umask 077 is 600, not "-11".
- Forgetting
xon a parent directory. A file with mode 644 is still unreadable if any directory above it lacksxfor you. Check each component, or usenamei -l /path/to/file, which lists the permissions of every directory along a path. - Giving a directory 644. Without
xit is unusable. Directories needxwherever they needr. - Fixing permissions with
chmod -R 755. Makes every file executable. Useu=rwX,go=rX. - Expecting
chmod 000to protect a file from deletion. Deletion depends on the directory's bits. - Forgetting the leading special digit is there.
chmod 755on a setgid directory may or may not clear setgid depending on the system; be explicit (chmod 2775) when it matters.
Recap
- Octal: r=4, w=2, x=1 per triplet; 644 files, 755 directories and programs, 600/700 private.
- New files get the requested mode (666 files, 777 directories) minus the bits in the umask (
umask,umask -S, a per-process builtin setting). - Directories:
rlists names,wchanges the list,xtraverses; every directory on a path needsx. - Deleting or renaming needs
wandxon the directory, not permissions on the file. - Special bits: sticky (
/tmp), setuid (passwd,sudo), setgid (team directories). chmod -R u=rwX,go=rXis the safe recursive fix.
Going further: run namei -l /work/site/public/index.html to see every permission along the path. Then find the setuid programs on this system: ls -l /usr/bin | grep '^-..s' (module 6 explains the pattern).