This cheatsheet covers the Unix chmod command: how to read and write permission modes in octal (755) and symbolic (u+x) form, and how to fix a whole web root without breaking directories. It is for anyone who has hit "Permission denied" on a server. The common confusion is that the same digit means different things on a file and on a directory.
Each permission digit is the sum of read (4), write (2) and execute (1). The three digits are owner, group and others, in that order.
| Digit | Binary | Letters | Meaning |
|---|---|---|---|
| 0 | 000 | `---` | No access |
| 1 | 001 | `--x` | Execute only |
| 2 | 010 | `-w-` | Write only |
| 3 | 011 | `-wx` | Write and execute |
| 4 | 100 | `r--` | Read only |
| 5 | 101 | `r-x` | Read and execute |
| 6 | 110 | `rw-` | Read and write |
| 7 | 111 | `rwx` | Read, write, execute |
| Mode | `ls -l` string | Typical use |
|---|---|---|
| 644 | `-rw-r--r--` | Regular files such as HTML, CSS, config read by a web server |
| 755 | `-rwxr-xr-x` | Directories and executable scripts |
| 600 | `-rw-------` | Private keys, `.env` files, `~/.ssh/id_ed25519` |
| 700 | `drwx------` | Private directories such as `~/.ssh` |
| 640 | `-rw-r-----` | Files readable by a service group but hidden from everyone else |
| 664 | `-rw-rw-r--` | Files shared by a group that edits them together |
| 777 | `-rwxrwxrwx` | Everyone can do everything. Almost never the right answer |
| 1777 | `-rwxrwxrwt` | World-writable directory with the sticky bit, like `/tmp` |
| Bit | On a file | On a directory |
|---|---|---|
| r | Read the contents | List the names inside |
| w | Change the contents | Create, rename or delete entries inside |
| x | Run it as a program | Enter it with `cd` and reach files by path |
| Part | Values | Example |
|---|---|---|
| Who | `u` owner, `g` group, `o` others, `a` all | `g` |
| Operator | `+` add, `-` remove, `=` set exactly | `+` |
| What | `r`, `w`, `x`, `X`, `s`, `t` | `x` |
chmod u+x,go-w file adds execute for the owner and removes write for group and others. If you leave out the who letters, the effect is as if a were given, except that bits set in your umask are not affected. Under umask 022, chmod +w on a 444 file gives 644, while chmod g+w gives 464, because an explicit who letter ignores the umask.
| Octal | Symbolic | Effect |
|---|---|---|
| 4000 | `u+s` | setuid: executable runs as the file owner |
| 2000 | `g+s` | setgid: executable runs as the file group; new files in a directory inherit its group |
| 1000 | `+t` | Sticky: only the file owner or directory owner can delete or rename entries in that directory |
| Option | What it does |
|---|---|
| `-R` | Apply recursively to a directory tree |
| `-c` | Print a line only for files whose mode changed |
| `-v` | Print a line for every file processed |
| `--reference=RFILE` | Copy the mode of another file |
| `--preserve-root` | Refuse to operate recursively on `/` |
mkdir -p site/css site/priv
touch site/index.html site/css/a.css site/priv/k
chmod -R 700 site
find site -type d -exec chmod 755 {} +
find site -type f -exec chmod 644 {} +
find site -printf '%m %p\n' | sort -k2
755 site
755 site/css
644 site/css/a.css
644 site/index.html
755 site/priv
644 site/priv/k
Use find with -type when files and directories need different modes. A plain chmod -R 755 would mark every file executable.
chmod -R 700 site
chmod -R u=rwX,go=rX site/css
find site/css -printf '%m %p\n' | sort -k2
755 site/css
755 site/css/a.css
X sets execute on directories and on files that already have an execute bit for someone. Here a.css came out 755 only because the earlier chmod -R 700 had given it an execute bit. Apply X to a tree of plain 644 files and they stay 644.
(umask 022; touch a; mkdir d)
(umask 077; touch b; mkdir e)
stat -c '%a %n' a d b e
644 a
755 d
600 b
700 e
New files start from 666 and new directories from 777, then the umask bits are cleared. A umask of 022 gives 644 and 755. A umask of 077 gives 600 and 700. Run umask with no arguments to see your current value; it printed 0022 in this run.
chmod 640 a
chmod --reference=a b
stat -c '%a %n' b
640 b
This is handy after you rebuild a config file and want the old permissions back. The reference file is only read, never changed, so you can point it at any file with the mode you want.
chmod 777 a
chmod g-w,o= a
stat -c '%a %A' a
750 -rwxr-x---
o= clears every bit for others, while g-w only removes one bit from the group.
mkdir sg
chmod 2775 sg
ls -ld sg
chmod 755 sg
stat -c '%a' sg
chmod 00755 sg
stat -c '%a' sg
drwxrwsr-x 2 root root 4096 Oct 5 09:24 sg
2755
755
New files inside a setgid directory inherit its group, which suits team folders. Note that chmod 755 kept the setgid bit on a directory. Per the GNU manual, clearing it with a numeric mode needs a leading zero (00755), -6000 or =755.
ls -l and fix it with chown and 755 or 644 instead.chmod -R 644 to a directory tree: Directories lose their execute bit, so nobody can enter them and you get "Permission denied" on every path inside. Use find -type d and find -type f separately.chmod 999 a prints chmod: invalid mode: '999'.chmod 755 to clear setgid on a directory: It does not. A directory with 2755 stays 2755 after chmod 755. Use 00755 or g-s.chmod +w file without u, g or o skips the bits your umask masks out, so group write may not be added under umask 022. Name the class, as in g+w.ssh-keygen -y -f k1 prints WARNING: UNPROTECTED PRIVATE KEY FILE! and Permissions 0644 for 'k1' are too open. Set the key to 600 and ~/.ssh to 700.chmod can silently ignore the bit when the system policy says so, as the GNU manual warns.chmod -R follows your typo. Check pwd first and prefer an explicit absolute path. GNU chmod has --preserve-root as a guard for /.--user matter when bind-mounting directories into containers