Cheatsheet

chmod Permissions Cheatsheet

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.

Quick reference

How an octal digit is built

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

Modes you will see most often

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`

What r, w and x mean on a file versus a directory

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

Symbolic syntax

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.

Special bits (fourth leading digit)

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

Options worth knowing

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 `/`

Common patterns

Set a web root to 755 directories and 644 files

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.

Use capital X to add execute only where it makes sense

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.

Read the result of umask

(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.

Copy a mode from another file

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.

Remove write for group and others without retyping the whole mode

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.

Set and clear setgid on a shared directory

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.

Pitfalls

  • Using 777 to make an error go away: It lets every local user and every process run as the web server write to the file. Find the real owner with ls -l and fix it with chown and 755 or 644 instead.
  • Applying 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.
  • Typing an invalid digit: Octal digits stop at 7. chmod 999 a prints chmod: invalid mode: '999'.
  • Expecting 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.
  • Forgetting the umask in symbolic mode: 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.
  • Letting SSH keys be too open: OpenSSH refuses a private key that others can read. With a 644 key, 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.
  • Setting setuid on files you do not fully trust: The program runs with the owner's privileges, often root, so any bug in it becomes a privilege escalation. Keep setuid for audited binaries, and note that chmod can silently ignore the bit when the system policy says so, as the GNU manual warns.
  • Running recursive chmod from the wrong directory: chmod -R follows your typo. Check pwd first and prefer an explicit absolute path. GNU chmod has --preserve-root as a guard for /.

Related ZipKit tools

Related cheatsheets

  • Docker Commands Cheatsheet — file ownership and --user matter when bind-mounting directories into containers
  • Nginx Directives Cheatsheet — the worker user needs read access to your web root and execute on every parent directory
  • .htaccess Cheatsheet — the Apache counterpart for per-directory configuration, which also depends on correct file permissions