File permissions & ownership cheat sheet

Unix permissions answer one question per file: who may do what. The who is three classes — owner (u), group (g), others (o); the what is read, write, execute. Numeric modes pack the three classes into octal digits, symbolic modes adjust them in place, and everything on this sheet operates on those two representations.

The sheet moves from everyday chmod through ownership to the special bits (where the honest advice is mostly caution) and finishes with ACLs — the escape hatch for granting one extra user access without reshuffling groups. SSH-related rows matter more than they look: a loose ~/.ssh is the most common reason a key is refused.

Numeric chmod: the everyday modes

Digit arithmetic — each class gets one octal digit, summed from r=4, w=2, x=1

7 = rwx
read + write + execute (4+2+1)
6 = rw-
read + write, no execute (4+2)
5 = r-x
read + execute, no write (4+1)
755
rwxr-xr-x: owner full, group and others read+execute — programs and directories
644
rw-r--r--: owner writes, everyone reads — ordinary files
600
rw-------: owner only — secrets and private keys
Recursive rows batch through find so directories and files get different modes in one pass each
TaskCommandNotes
Programs and directorieschmod 755 deploy.shrwxr-xr-x — owner manages, everyone else can run or enter
Ordinary readable fileschmod 644 index.htmlrw-r--r-- — the default-looking mode for content
Owner-only directorychmod 700 ~/.sshrwx------ — sshd refuses looser modes on this path
Private keys and secretschmod 600 ~/.ssh/id_ed25519rw------- — OpenSSH rejects group/world-readable keys outright
AVOID: world-writablechmod 777 fileany user may replace or plant content; fix owner/group instead — 777 is almost never the right answer
Directories only, recursivelysudo find /var/www -type d -exec chmod 755 {} +{} + batches many files per chmod — faster than the \; variant
Files only, recursivelysudo find /var/www -type f -exec chmod 644 {} +pairs with the directory row; shortcut: chmod -R u=rwX,go=rX /var/www
Exact read-only for everyonechmod a=r report.mdsets the mode precisely — strips write and execute in one stroke

Symbolic chmod: adjust in place

Syntax: who(+/-/=what) — u owner, g group, o others, a all; + adds, - removes, = sets exactly

chmod +x deploy.sh
add execute for everyone — the everyday script enabler
chmod u+x ~/bin/check.sh
execute for the owner only
chmod go-w /srv/www
take write away from group and others, leave the rest untouched
chmod go-rwx secret.env
strip group and others to nothing — equals 700
chmod u=rwx,g=rx,o=
set each class exactly; o= removes all bits for others — equals 755
capital X
chmod -R u=rwX,go=rX dir — execute lands on directories and already-executable files only, never on plain data files

Ownership: chown and friends

Changing to an arbitrary user or group requires root, except chgrp into a group you belong to
TaskCommandNotes
Change the ownersudo chown alice report.mdtakes user or UID
Owner and group togethersudo chown alice:developers report.mdthe common web-app fix in one call
Group only, owner keptsudo chown :developers report.mdempty user side skips the owner
chgrp shorthandchgrp developers report.mdnon-root works only into groups you are a member of
Recurse a whole treesudo chown -R www-data:www-data /var/www/appdouble-check the path — a typo with -R is expensive
Inspect like ls -l, preciselystat -c '%A %a %U:%G %n' report.mdsymbolic, octal, owner:group, name — GNU stat specifiers

Special bits and umask

The three high-order octal digits and the default-mode mask. Security posture noted per row
TaskCommandNotes
Show current umaskumaskprints four octal digits, e.g. 0022 — leading 0 marks octal
Set default permissionsumask 022masks bits from the 666/777 baselines: new files 644, new dirs 755; persists via shell profile
SUID: run as the file's ownersudo chmod u+s /usr/local/bin/toolmode 4755, shown as -rwsr-xr-x; passwd is the classic legitimate user — treat custom SUID binaries as attack surface
Audit SUID binariessudo find / -xdev -perm -4000 -type fcompare against a fresh distro install; investigate every stray — privilege escalation loves forgotten SUID
SGID on a shared directorysudo chmod g+s /srv/teammode 2775: files created inside inherit the directory's group, not the creator's — team-share glue
Sticky bit on a shared directorysudo chmod +t /srv/pubdropmode 1777, shown as drwxrwxrwt: users may delete only their own files — the /tmp model
Two honest caveats. The kernel ignores the SUID bit on interpreted scripts — only real binaries gain the owner's identity, so 'make my script SUID' is not a thing. And SUID/SGID binaries execute with elevated identity regardless of who runs them: every one you add widens the escalation surface, which is why the find audit row belongs in periodic hardening reviews.

ACLs: finer-grained grants

POSIX ACLs extend the three-class model with named users and groups. Tools come from the acl package; ls -l marks extended entries with a trailing +
TaskCommandNotes
View a file's ACLgetfacl /srv/projectlists owner, group, named entries and the effective mask
Grant a named usersetfacl -m u:bob:rx /srv/project-m modifies; bob gains read+execute without joining the group
Revoke one entrysetfacl -x u:bob /srv/project-x removes the named-user line, leaving others intact
Inheritable grant for new filessudo setfacl -d -m u:deploy:rx /srv/releases-d sets a default ACL on the directory; newly created children inherit automatically — repeat per pre-existing subdirectory
ACL entries are capped by the ACL mask: if group permissions were tightened after the grant, the effective rights of named entries shrink accordingly — getfacl shows this as an effective: comment. setfacl recalculates the mask on every -m unless told otherwise with -n, so day-to-day you rarely manage it by hand.

FAQ

What does chmod 755 mean?

Three octal digits, one per class: owner, group, others. Each digit sums read=4, write=2, execute=1. So 755 unpacks to 7=4+2+1 (rwx) for the owner, 5=4+1 (r-x) for the group, 5=4+1 (r-x) for others — owner fully manages the file, everyone else may read and run it but not modify it. That is why 755 is the standard mode for programs and directories, while 644 (rw-, r--, r--) suits ordinary files nobody needs to execute.

When do I need 600 instead of 644?

Whenever others must not even read the file. 644 lets every local user read the contents; 600 restricts everything but the owner. Private keys, credential files, .env files and database dumps belong at 600 (and their containing directories at 700, so listings are hidden too). OpenSSH enforces this: it refuses to use a private key that group or others can read, reporting UNPROTECTED PRIVATE KEY FILE.

What does umask do?

umask subtracts permission bits from everything you newly create. Files start from a 666 baseline and directories from 777, and the mask removes bits: umask 022 strips write for group and others, producing 644 files and 755 directories. The common values: 022 (standard, others can read), 077 (private — 600 files, 700 dirs, right for multi-user shells), 002 (group-writable, common on shared-dev setups). Set it per-session with umask 022 or persistently in ~/.bashrc or /etc/profile.

What does the sticky bit do on a directory?

It lets many users write into a shared directory while allowing each of them to delete or rename only their own files — write without delete rights over neighbors. /tmp is the canonical example: mode 1777, displayed as drwxrwxrwt with the t flag. Apply it with chmod +t /dir (or the leading digit: chmod 1777 /dir) on any drop-box-style share; without it, anyone who can write to the directory can remove everyone's files.

What is SUID and is it safe?

SUID (set-user-ID, mode 4000, shown as an s in the owner's execute slot) makes a program execute with the identity of its file owner — typically root — no matter who runs it. It is how /usr/bin/passwd lets ordinary users edit /etc/shadow. Legitimate uses are narrow and carefully audited system binaries; every additional SUID binary is a standing privilege-escalation opportunity, so review periodically with sudo find / -xdev -perm -4000 -type f and treat unexpected results as incidents. Note the bit does nothing on shell or interpreted scripts — Linux ignores SUID on them.