LinuxBeginner

Understanding File Permissions in Linux (chmod, chown Explained)

How Linux's read/write/execute permission model actually works, what chmod's numeric codes mean, and when to reach for chown instead.

DevFieldGuideJuly 20, 2026 (updated July 29, 2026)6 min read
Share:

Every "permission denied" error on a Linux system traces back to this model — worth understanding properly instead of reflexively running chmod 777.

The three permission types

  • r (read) — view a file's contents, or list a directory's contents.
  • w (write) — modify a file, or create/delete files within a directory.
  • x (execute) — run a file as a program, or cd into a directory (directories need x to be traversable at all, which surprises people).

The three "who" categories

Every file has permissions for three separate groups: the owner (a single user), the group (a set of users), and others (everyone else). Running ls -la shows all nine permission bits at once:

bash
$ ls -la script.sh
-rwxr-xr--  1 daniel  staff  1240 Jul 20 10:15 script.sh

Reading rwxr-xr-- in three chunks of three: rwx (owner: read, write, execute), r-x (group: read, execute, no write), r-- (others: read only, no write, no execute).

chmod with numeric codes

Each permission has a value: r=4, w=2, x=1. Add them up per category to get a three-digit code.

CombinationValue
rwx7 (4+2+1)
rw-6 (4+2)
r-x5 (4+1)
r--4
bash
chmod 755 script.sh   # owner: rwx, group: r-x, others: r-x — typical for an executable script
chmod 644 file.txt     # owner: rw-, group: r--, others: r-- — typical for a regular file
chmod 600 secrets.env  # owner: rw-, group: none, others: none — typical for sensitive files

chmod with symbolic notation

For targeted changes without recalculating the whole number:

bash
chmod +x script.sh       # add execute for everyone
chmod u+w file.txt        # add write for the owner (u = user/owner)
chmod g-w file.txt        # remove write for the group
chmod o-rwx secrets.env   # remove all permissions for others

u, g, o (and a for all) combined with +, -, = and the permission letters — useful when you want to change one thing without touching the rest.

chown — changing who owns the file

chmod controls what each category can do; chown controls who is in the owner/group categories in the first place.

bash
chown daniel file.txt           # change the owner
chown daniel:staff file.txt      # change owner and group together
chown -R daniel:staff /var/www   # recursive — apply to a directory and everything inside it

A very common real-world case: a file gets created by a root-owned process (e.g., inside Docker, or via sudo) and a regular user can no longer edit it. chown back to the right user is usually the actual fix — not chmod 777, which papers over an ownership problem with an overly permissive one.

The rule of thumb

If you find yourself reaching for chmod 777 (or chmod -R 777 on a whole directory), stop and ask who specifically needs access, and use the narrowest chmod/chown combination that grants it. It's rarely more than a couple of extra seconds of thought, and it avoids leaving a door open that outlives the reason you opened it.

Permission errors inside a running container are a close cousin of this topic — see Docker Compose for local development for the volume-mounting patterns where host/container permission mismatches most often show up.

Special permission bits: setuid, setgid, and the sticky bit

Beyond the standard rwx model, three less-common bits handle specific edge cases worth recognizing when you see them:

bash
chmod u+s /usr/bin/some-binary   # setuid — runs as the file's owner, not the invoking user
chmod g+s /shared/project        # setgid on a directory — new files inherit the directory's group
chmod +t /tmp                     # sticky bit — only the file's owner can delete/rename it

setuid is why passwd (owned by root, setuid-enabled) lets a regular user change their own password despite the password database itself being root-owned — the binary temporarily runs with the owner's (root's) privileges for that specific operation. setgid on a shared directory is genuinely useful for team environments — every new file created inside automatically inherits the directory's group, instead of each contributor's own default group, keeping shared files consistently group-accessible without everyone remembering to chgrp manually. The sticky bit is why /tmp is world-writable but users still can't delete each other's files in it — write access to the directory alone would normally allow deleting any file inside it, and the sticky bit specifically overrides that for exactly this shared, world-writable case.

umask: setting default permissions before files even exist

umask controls what permissions new files and directories get by default, before any explicit chmod is ever run — it's a subtractive mask applied against a base of 666 for files and 777 for directories:

bash
umask       # show the current mask, e.g. 022
umask 027   # set a stricter default — group gets read-only, others get nothing

A umask of 022 (a common default) subtracts write access for group and others, so new files land at 644 and new directories at 755 automatically. Setting a stricter umask (like 027, common on shared/multi-user servers) means every new file starts more locked-down by default, without needing to remember to chmod each one individually after creation — the earlier note about 777 directories not cascading to new files inside them is really umask deciding those new files' starting permissions instead.

Common mistakes

Common mistakes
  • Running chmod -R 777 on an entire directory to fix one file's permission problem — grants far more access than needed, to far more files than needed, and the "temporary" fix is rarely revisited once the immediate error goes away.
  • Confusing a permission problem with an ownership problem. "Permission denied" after a file was created by root (common inside Docker containers, or after sudo) is usually an ownership issue chown fixes — chmod alone won't help if the real issue is that your user isn't in the right owner/group category at all.
  • Forgetting that changing a file's permissions doesn't cascade to files created inside a directory afterward. A directory set to 777 doesn't mean every new file created inside it also gets 777 — new files get permissions based on the creating process's umask, not the parent directory's mode.
  • Setting 600 on a file that a service running as a different user actually needs to read — a mistake that trades "too permissive" for "too restrictive" and just moves the debugging time from a security review to a broken deployment.
Advertisement

Frequently Asked Questions

Advertisement
DevFieldGuide
DevFieldGuide

Editorial Team

Practical tutorials and developer tools, written and maintained by the DevFieldGuide team.

Enjoyed this article?

Get the next one straight to your inbox, along with the best of what we publish each week.

Related Articles

More in Linux

View all