Octal Numbers
Three digits: owner, group, others. Each digit adds read 4, write 2, execute 1. Decode or build any mode with the chmod calculator.
| Digit | Meaning |
|---|---|
| rwx: read, write, execute | |
| rw-: read and write | |
| r-x: read and execute | |
| r--: read only | |
| ---: nothing |
The modes that cover almost every real case:
| Command | When to use it |
|---|---|
| Directories, scripts, binaries: owner full, others read and enter | |
| Normal files: owner edits, others read | |
| Private files: owner only (SSH keys, credentials) | |
| Private directories: owner only | |
| Read-only key files (AWS .pem) | |
| Group-writable directory for a team | |
| Group-writable file | |
| Almost never right; see Web Server Permissions below |
Symbolic Syntax
Who ( owner, group, others, all), then add, remove, set exactly, then , , .
| Command | What it does |
|---|---|
| Owner may execute | |
| Everyone may execute (the usual "make it runnable") | |
| Remove write from group and others | |
| Everyone gets exactly read, nothing else | |
| Same as 755, spelled out | |
| Let the group write | |
| Others get nothing | |
| Capital X: execute on directories only (see Recursive) |
Make a File Executable
| Command | What it does |
|---|---|
| Make a script executable | |
| Run it (the is required outside PATH) | |
| Confirm: |
The first line of the script should name its interpreter ( or ), or you will get or the wrong shell.
Recursive Changes
hits every file and directory, and one number is usually wrong for one of them: makes directories unenterable, makes every file executable.
| Command | What it does |
|---|---|
| Everything 755 (fine for a tree of scripts, wrong for docs) | |
| Directories to 755 only | |
| Files to 644 only | |
| One pass: X sets execute on dirs (and already-executable files) only |
More find patterns in the find cheat sheet.
Multiple Files and Wildcards
The shell expands the wildcard before chmod runs, so reaches chmod as a plain list of filenames.
| Command | What it does |
|---|---|
| Several named files in one call | |
| Every in the current directory only | |
| The top level of dir, not its subdirectories | |
| Hidden files (plain also matches and ) | |
| Copy another file's mode instead of naming one (GNU) | |
| When the list is long enough to hit |
Directories vs Files
The same three letters mean different things on a directory.
| Bit | On a file | On a directory |
|---|---|---|
| Read the contents | List the file names | |
| Modify the contents | Create, rename, delete entries inside | |
| Run it as a program | Enter it () and reach files inside |
Deleting a file needs on the directory, not on the file. A directory without blocks access to everything under it no matter what the files say.
Symlinks
A symlink has no useful mode of its own on Linux: access is decided by the target.
| Command | What it does |
|---|---|
| Follows the link and changes the target | |
| Changes the link itself (macOS/BSD only; GNU chmod has no ) | |
| Symlinks met during the walk are skipped, never followed | |
| Links always read ; Linux ignores that mode |
means the link points at a path that no longer exists.
setuid, setgid, Sticky Bit
A fourth leading digit, or / in symbolic form.
| Command | What it does |
|---|---|
| setuid: runs as the file's owner (how edits /etc/shadow) | |
| setgid on a directory: new files inherit its group | |
| Sticky bit: anyone writes, only owners delete their own files | |
| setuid, symbolic | |
| setgid, symbolic | |
| Sticky bit, symbolic | |
| List every setuid binary (audit) |
In , setuid/setgid show as in the owner/group execute slot and the sticky bit as at the end ( on /tmp). A capital or means the bit is set but execute is missing, which is usually a mistake.
SSH Key Permissions
OpenSSH checks permissions before it will use a key. A private key that group or others can read is refused outright with , and on the server (on by default) makes sshd ignore when the home directory, , or the file itself is group- or world-writable. Full command list in the SSH cheat sheet.
| Command | What it does |
|---|---|
| The .ssh directory, both ends | |
| Private key (fixes "UNPROTECTED PRIVATE KEY") | |
| Public key | |
| On the server; ignored if too open | |
| Client config | |
| Host key store (600 is fine too) | |
| AWS-style read-only key | |
| Home directory, when a shared box left it group-writable |
The server side is silent about all of this: sshd logs the reason locally and sends the client nothing, so a permissions problem on the server reaches you only as Permission denied (publickey).
Reading ls -l Output
-rwxr-xr-- 1 alice developers 4096 Sep 5 10:30 deploy.shType, then three triads: owner , group , others . That file is 754. Paste any string into the chmod calculator to get the number back.
| First character | Meaning |
|---|---|
| Regular file | |
| Directory | |
| Symlink |
| Command | What it does |
|---|---|
| Long listing with permissions | |
| The directory itself, not its contents | |
| Show the octal mode (GNU/Linux) | |
| Show the octal mode (macOS) |
A trailing means ACLs are set (see below); on macOS means extended attributes.
ACLs
When owner, group, and others cannot express who needs access, an ACL adds per-user entries on top of the mode bits. marks a file that has one with a trailing .
| Command | What it does |
|---|---|
| Show the ACL behind that | |
| Grant one extra user, without touching the group | |
| Same for a group | |
| Remove that entry | |
| Strip the ACL, back to plain mode bits | |
| Default ACL: new files inside inherit it |
Running chmod on a file with an ACL rewrites the ACL mask, which can quietly cut the extra entries down. Check with after any chmod on an ACL'd path.
chown and chgrp
chmod changes what the owner, group, and others are allowed to do. chown changes who the owner and group are, and needs root. When a service cannot write a path, chown is almost always the right half of the pair.
| Command | What it does |
|---|---|
| Change the owner | |
| Owner and group at once | |
| Recursive, the web-uploads fix | |
| Group only (works without root for your own files) | |
| See current owner and group |
umask
umask decides the default mode of new files: files start from 666 and directories from 777, minus the mask.
| Command | What it does |
|---|---|
| Show the current mask (022 is the common default) | |
| New files 644, new directories 755 | |
| New files 600, new directories 700 (private by default) | |
| Group-writable: files 664, directories 775 |
Set it in or to make it stick.
Web Server Permissions
The convention on Debian/Ubuntu (nginx and Apache run as ; on RHEL it is or ):
| Command | What it does |
|---|---|
| Server owns its files | |
| Directories 755 | |
| Files 644 | |
| Writable dirs: group-writable, with the right group |
When an app "needs" 777 to work, the real problem is ownership: the process user cannot write because it does not own the path. to the process user (or share a group) and keep 755/644. 777 hands write access to every account on the box.
Options and Flags
| Option | What it does |
|---|---|
| Recursive | |
| Report every file touched | |
| Report only files that actually changed | |
| Silence error messages | |
| Copy another file's mode (GNU) | |
| Change a symlink itself instead of its target (macOS/BSD) |
Fixing chmod Errors
| Error | Fix |
|---|---|
| You gave a mode and no file. chmod needs both: | |
| Octal digits only run 0-7; symbolic modes must read | |
| (you own it, still fails) | Immutable flag: then (Linux); then (macOS) |
| on macOS system paths | SIP protects them; you cannot chmod /System and friends |
| You do not own the file: , or fix ownership with chown | |
| / | You are in CMD or PowerShell, not a Unix shell; use , or run it inside WSL |
| An SMB, NFS, or NAS mount with no POSIX ACL support; set permissions on the share instead | |
| chmod "works" but nothing changes | FAT32/exFAT/NTFS mounts have no Unix permissions; set them at mount time with / |
| Remount first: |
macOS and Windows
| Environment | Situation |
|---|---|
| macOS | Same chmod (BSD flavour); everything above works, flags differ as shown |
| Windows (native) | No chmod; is the closest equivalent |
| Windows (WSL, Git Bash) | chmod works on Linux paths; on it is a no-op until you enable metadata |
| Git anywhere | Git tracks only the execute bit; sets it in the index |
To make chmod stick on Windows drives in WSL, add this and restart WSL with :
# /etc/wsl.conf
[automount]
options = "metadata,umask=22,fmask=11"Gotchas
- Modes survive reboots but not every copy. without , most unzip tools, and without reset the mode at the destination, and git records only the execute bit.
- chmod updates a file's ctime, not its mtime, so shows the old timestamp and backup tools that key on mtime can skip the change entirely.
- Bash wildcards never match dotfiles, so quietly leaves and alone.
- Numbers are absolute, letters are relative. sets all nine bits and wipes any setuid, setgid, or sticky bit on the file; only adds one bit.
- root ignores the read and write bits, so hides nothing from a root process or from anyone with sudo.