Tools

File Permissions by Example: A Working Guide to chmod, Octal, and the Triplet Model

June 10, 2026·10 min read

Why chmod Feels Confusing Until It Doesn't

Almost everyone who works with Unix-style systems eventually hits a moment where a deploy fails, a web server returns Forbidden, an SSH key gets rejected, or a cron job silently does nothing — and the answer is a permission bit set wrong. The fix is usually a four-character command. Getting to the right four characters is the part nobody really teaches you, because the documentation jumps straight into octal arithmetic and assumes you already have the mental model.

The good news is that the mental model is small. Once you internalize it, every chmod 644, chmod 755, and chmod 600 stops being a memorized incantation and becomes a sentence you can read. This piece is the version of that explanation I wish someone had handed me when I was twenty-two and SSH was refusing to load my key because the file was group-readable.

The piece is example-driven on purpose. Every concept gets paired with a permission value you have already seen in the wild, because there are maybe a dozen permission values that account for ninety-five percent of real-world Unix files, and once you know what they encode the rest follows from the same rules.

The Triplet Model in One Sentence

Every file and directory on a Unix system has nine permission bits, organized as three groups of three. The three groups are owner, group, and other. The three bits inside each group are read, write, and execute. That is the entire model. Every permission string you have ever seen — rwxr-xr-x, rw-r--r--, rwx------ — is just those nine bits in order, with a dash where the bit is off.

The leftmost character in something like -rwxr-xr-x is not part of the permissions. It is the file type: a dash for a regular file, d for a directory, l for a symbolic link, c for a character device, and a few rarer values. The permissions start at position two and run for nine characters.

Read this string slowly: rw-r--r--. The owner has read and write, but not execute. The group has read only. Everyone else has read only. That is the default permission for a newly created file on most Linux systems, and it is what you want for a text file, a configuration file, an image, or anything else where being able to run it as a program would be either meaningless or dangerous.

Where the Octal Digits Come From

People say chmod 644 out loud all the time, but where do those digits come from? Each of the three triplets gets compressed into a single octal digit. Read is worth 4, write is worth 2, execute is worth 1. You add them up.

For rw-: read (4) plus write (2) plus no execute (0) equals 6. For r--: read (4) plus nothing plus nothing equals 4. So rw-r--r-- compresses to 644. That is all 644 means. It is not a code; it is two read-and-write for the owner, read-only for everyone else, written down in the laziest possible notation.

Run through the common values once and you will not have to think about them again. 755 is rwxr-xr-x: full access for the owner, read-and-execute for everyone else. That is the standard for executables and directories. 700 is rwx------: full access for the owner, nothing for anyone else. That is the standard for an SSH directory, a credentials folder, anything private and runnable. 600 is rw-------: read and write for the owner only. That is the standard for private keys, password files, and anything you do not want even other accounts on the same machine to read. 444 is r--r--r--: read-only for everyone. That is a sane default for a config file you want to be sure nothing accidentally overwrites.

If the arithmetic ever feels slippery, a chmod calculator that toggles the nine bits as switches and shows you the numeric, symbolic, and full command form at the same time is genuinely faster than working it out in your head — and more importantly, it forces you to confront which bits you are actually granting rather than typing a memorized number.

What the Execute Bit Means for Directories

This is the part that catches almost everyone exactly once. On a regular file, the execute bit means "this file is runnable as a program," which is intuitive. On a directory, the execute bit means something completely different: it means "you can traverse into this directory." Without the execute bit on a directory, you cannot cd into it, you cannot resolve a path through it, and you cannot read files inside it even if those files are themselves world-readable.

This is why 755 rather than 644 is the right default for directories. The owner needs full permissions to add and remove files. Everyone else needs read (to list the directory) and execute (to traverse into it and reach the files inside). Strip the execute bit off a directory and every file under it becomes unreachable, even when the file's own permissions look perfectly fine.

The classic version of this failure is an Apache or Nginx server returning 403 Forbidden when you have just spent an hour confirming that the actual HTML file is readable. The HTML file is readable. The problem is a parent directory somewhere along the path that the web server's user cannot traverse into. chmod o+x on the offending directory fixes it, and you remember the lesson for the rest of your career.

The Symbolic Form You Already Half-Know

Numeric mode is fast. Symbolic mode is more readable, and useful when you only want to change one thing without disturbing the others. The syntax is chmod <who><op><perm>.

The who is one or more of u (user/owner), g (group), o (other), or a (all). The op is + to add a permission, - to remove it, or = to set it exactly. The perm is r, w, x, or some combination.

So chmod u+x script.sh says "give the owner execute permission on script.sh," without changing anything else. chmod go-w file strips write permission from group and other. chmod a=r file sets the file to read-only for everyone in one go. The symbolic form is also additive, which matters when you do not want to think about the existing bits — useful in scripts that should not assume what was there before.

umask: Why New Files Are Born With the Permissions They Have

If you create a brand new file with touch newfile, it shows up as rw-r--r-- (644). Nothing in your command said anything about permissions, so where did those come from?

The answer is a process-level setting called umask, introduced in Version 7 Unix in 1979 and unchanged in spirit ever since. The umask is a mask — a set of bits to remove from the default. For files, the kernel starts with 666 (read and write for everyone) and subtracts the umask. For directories, it starts with 777 and subtracts. On most user accounts the umask is 022, which strips write permission from group and other, giving you 644 for files and 755 for directories.

On a root account or a hardened server, the umask is usually 077, which strips all access from group and other, giving you 600 for files and 700 for directories. That is why files created by a daemon running as root frequently end up unreadable to your own user — the daemon's umask is more restrictive than yours.

If you have ever created a file as part of a deploy and then watched a web server fail to read it, umask is usually the explanation. The fix is either to set a less restrictive umask in the process that creates the file, or to chmod the file after the fact.

The Three Bits Almost Nobody Explains: setuid, setgid, and Sticky

The nine permission bits are not actually the whole story. There are three more bits at the front of the mode word: setuid (4000), setgid (2000), and the sticky bit (1000). You see them in permission strings as an s or a t in place of an execute bit. They explain a few mysterious files on every Linux system.

The setuid bit on an executable causes the program to run with the file owner's privileges instead of the calling user's. The classic example is /usr/bin/passwd, which is setuid root because changing your password requires writing to a file that only root can write to. ls -l /usr/bin/passwd shows -rwsr-xr-x, where the s in the owner-execute position is the setuid bit. This is also where most of the historical Unix security disasters come from, which is why modern systems try to minimize the count of setuid binaries.

The setgid bit on a directory causes new files created inside to inherit the directory's group rather than the creator's primary group. That is mostly useful for shared project directories where everyone in the team should be able to read each other's files without remembering to chgrp every time.

The sticky bit on a directory restricts file deletion to the file's owner, even if other users have write permission on the directory. The canonical example is /tmp, which is world-writable but sticky — anyone can create files in /tmp, but you can only delete your own. ls -ld /tmp shows drwxrwxrwt, where the t at the end is the sticky bit.

You probably will not set these by hand often. But when you see -rwsr-xr-x on a binary or drwxrwxrwt on a directory, now you know what they are saying.

Two Patterns That Cause Most Real-World Permission Bugs

Almost every chmod-related failure I have debugged in fifteen years collapses into one of two patterns.

The first is the SSH private key problem. OpenSSH refuses to use a private key file if it is readable by anyone other than the owner. The error message is usually some variant of "Permissions for <keyfile> are too open" or "WARNING: UNPROTECTED PRIVATE KEY FILE." The fix is chmod 600 ~/.ssh/id_ed25519 (or whatever your key file is called). The ~/.ssh directory itself should be 700. This is enforced by OpenSSH on purpose, because a world-readable private key is roughly equivalent to publishing your password on a sticky note.

The second is the web server traversal problem. The web server runs as a user (commonly www-data or nginx) that is not you. For the web server to serve a file, every directory in the path from root to the file must be at least o+x for the web server's user, and the file itself must be o+r. Get one parent directory wrong and you get 403 Forbidden for reasons that have nothing to do with the file you are trying to serve. When in doubt, namei -l /path/to/file walks the path and prints the permissions at every level, which is the fastest way to spot where the traversal is breaking.

The Working Mental Model

Strip everything in this article down and you are left with a small set of working rules. Permissions are three triplets of read-write-execute, applied to owner-group-other. Octal mode is the same thing in compressed form: 4 for read, 2 for write, 1 for execute, summed per triplet. Execute on a directory means traversal, not running. New files inherit the kernel default minus your umask. SSH private keys must be 600 or OpenSSH refuses them. Web servers need execute on every parent directory to serve a file inside.

Once those rules feel automatic, ls -l stops being noise and starts reading like a sentence about who can do what. The few times a year you reach for a calculator are when you are setting up something unusual — a setgid project directory, a tricky umask in a deploy script, a permission set you want to triple-check before you ship — and a quick toggle of the nine switches is more honest than typing a memorized three-digit number you cannot quite reconstruct from first principles. Either way, the model is small enough to carry around in your head, and the system itself, for all the historical weirdness, is doing exactly what it says.

Stay Informed

Get ecosystem updates

New tools, posts, and ecosystem news — no spam, unsubscribe anytime.