Symbolic vs Numeric (Octal) Permissions
Every file and directory on a Linux system carries nine permission bits — read, write, and execute for the owner, for the group, and for everyone else — that decide who can do what with it. The chmod command changes those bits, but Linux gives you two completely different ways to write the same change: symbolic mode, which uses letters and operators like u+x or go-w, and numeric (octal) mode, which uses a three-digit number like 755 or 640. Both notations end up flipping the exact same bits in the file’s inode; the difference is in how precisely you can target the change and how easy it is to reason about. This lesson covers how each notation works under the hood, when to reach for one over the other, and the mistakes people make when switching between them.
Overview: How Permissions and chmod Work
Every file and directory has an owner (a user) and a group, stored as numeric IDs in its inode — the on-disk structure that holds a file’s metadata (size, timestamps, permissions, and pointers to its data blocks, but notably not its name, which lives in the directory entry). Alongside the owner and group IDs, the inode stores a mode: a set of bits describing what the owner, the group, and everyone else (“other”) are allowed to do. For a plain file, three permissions matter per category: r (read the file’s contents), w (write/modify it), and x (execute it as a program or script). For a directory, the meanings shift slightly: r lets you list the directory’s entries, w lets you create or delete entries inside it, and x lets you cd into it or traverse it to reach files below.
Because there are three categories (owner, group, other) and three permission types (r, w, x) per category, the full permission set is nine bits. Each category’s three bits can be summarized as one octal digit from 0 to 7, because r is worth 4, w is worth 2, and x is worth 1 — any combination is just the sum. A digit of 7 (4+2+1) means full rwx; a digit of 6 (4+2) means read and write but not execute; a digit of 5 (4+1) means read and execute but not write. Three digits in a row — owner, group, other — describe the entire permission set for a file, which is exactly what numeric mode gives you directly.
Symbolic mode expresses the same nine bits differently: instead of a number, you name who you’re changing (u for the owner/user, g for group, o for other, a for all three), an operator (+ to add a permission, - to remove one, = to set exactly these permissions and clear anything not listed), and the permission letters themselves (r, w, x). When you run chmod, it doesn’t matter which notation you typed — the command parses your argument, computes the resulting 9-bit (or more, with special bits) mode value, and passes that value to the chmod() system call, which the kernel uses to overwrite the mode field stored in the file’s inode. The file itself doesn’t “remember” whether you used symbolic or numeric syntax; only the resulting bits persist.
The key practical difference is that numeric mode is always absolute — you specify all three digits, so the whole permission set is replaced in one step — while symbolic mode with + or - is relative to whatever the file’s current permissions already are. Symbolic = is absolute like numeric mode, but only for the categories you name; anything you don’t mention (like the owner’s bits, if you only specify go=) is left untouched. This distinction is the source of most of the confusion between the two styles, and it’s covered in Common Mistakes below.
Syntax
The general shape of the command is the same regardless of which mode notation you use:
# General form
# chmod MODE TARGET
# Symbolic form
# chmod WHO OPERATOR PERMS TARGET
# WHO: u (owner) g (group) o (other) a (all three)
# OPERATOR: + (add) - (remove) = (set exactly, clearing unlisted bits)
# PERMS: r w x (and X, s, t for special cases)
# Numeric (octal) form
# chmod OWNER GROUP OTHER TARGET
# each digit is 0-7, computed as r(4) + w(2) + x(1)
| Octal digit | Binary | Meaning | Symbolic equivalent |
|---|---|---|---|
| 7 | 111 | read, write, execute | rwx |
| 6 | 110 | read, write | rw- |
| 5 | 101 | read, execute | r-x |
| 4 | 100 | read only | r-- |
| 3 | 011 | write, execute | -wx |
| 2 | 010 | write only | -w- |
| 1 | 001 | execute only | --x |
| 0 | 000 | no permission | --- |
A few flags worth knowing beyond the mode itself:
-R— apply the change recursively to a directory and everything inside it.-v— print a line for every file changed (useful when combined with-R).--reference=FILE— copy another file’s mode instead of specifying one yourself.
Examples
Example 1: Numeric mode to lock down a log file. Application logs often contain sensitive data and shouldn’t be world-readable. Here we check the current permissions, then restrict them.
ls -l /var/log/app.log
-rw-r--r-- 1 root adm 152034 Aug 4 09:12 /var/log/app.log
sudo chmod 640 /var/log/app.log
ls -l /var/log/app.log
-rw-r----- 1 root adm 152034 Aug 4 09:12 /var/log/app.log
The file started at 644 (owner read/write, group and other read-only). chmod 640 replaced all nine bits at once: owner keeps read/write (6), group keeps read-only (4), and other loses all access (0). Notice we needed sudo because the file is owned by root — only the owner or root can change a file’s permissions.
Example 2: Symbolic mode to make a script executable. A freshly written or downloaded script is not executable by default; you add just the execute bit without touching read/write.
ls -l backup.sh
-rw-r--r-- 1 alice alice 892 Aug 4 09:20 backup.sh
chmod u+x backup.sh
ls -l backup.sh
-rwxr--r-- 1 alice alice 892 Aug 4 09:20 backup.sh
u+x is relative: it left every other bit exactly as it was and flipped on only the owner’s execute bit. This is the everyday “make it runnable” pattern — see the Common Mistakes section for what happens if you try to run a script before doing this.
Example 3: Proving symbolic and numeric produce identical results. A deploy script should be runnable by its owner and the deployment group, but completely inaccessible to everyone else.
chmod 750 /srv/deploy/release.sh
This is exactly equivalent to:
chmod u=rwx,g=rx,o= /srv/deploy/release.sh
ls -l /srv/deploy/release.sh
-rwxr-x--- 1 devuser devteam 2048 Aug 4 09:30 /srv/deploy/release.sh
Either command run on its own produces this exact mode: owner gets 7 (rwx), group gets 5 (r-x), other gets 0 (nothing, written as an empty list after o=). This is a good way to sanity-check that you understand a numeric mode: translate each digit into rwx letters and see if it matches what the symbolic form would produce.
How It Works Step by Step
When you run chmod 750 release.sh, here’s what actually happens:
- The shell looks up
chmodin$PATH(it’s normally/usr/bin/chmod) and executes it with the arguments750andrelease.sh. chmodparses750as three octal digits and converts them into a 9-bit mode value:111 101 000.- It resolves
release.shto an inode via the filesystem (following the directory entries down the path). - It checks that the calling process’s effective user ID matches the file’s owner (or is root) — otherwise it fails with “Operation not permitted”.
- It issues the
chmod()system call with the target inode and the new mode value, and the kernel overwrites the permission bits stored in that inode directly. No file data is touched — only metadata.
For symbolic mode with + or -, there’s one extra step before the last one: chmod first reads the file’s current mode (via stat()), computes the new value by adding or clearing the requested bits on top of it, and then calls chmod() with that computed result. This is why u+x is safe to run repeatedly and never disturbs bits you didn’t mention, while a numeric mode always writes all nine bits regardless of what they were before.
Common Mistakes
Mistake 1: Using a numeric mode and accidentally wiping bits you meant to keep. Numeric mode replaces the entire permission set, so if you only meant to tighten one category, you can silently remove permissions you needed.
chmod 644 deploy.sh
If deploy.sh was previously 755 (executable by owner, group, and other), this command doesn’t just remove group/other write — it strips execute from every category, because 644 has no x anywhere. The script will fail with “Permission denied” the next time anyone tries to run it. If the actual goal was “stop group and other from writing to this file, but leave everything else alone,” the fix is a targeted symbolic change:
chmod go-w deploy.sh
This only clears the write bit for group and other, leaving the execute bit — and every other bit — exactly as it was.
Mistake 2: Using = in symbolic mode and forgetting it clears unlisted permissions for that category. Unlike +, the = operator is absolute for the categories you name.
chmod o=r shared-script.sh
If shared-script.sh previously had o=rx (other could read and execute it), this command doesn’t just confirm read access — it silently drops the execute bit for other too, since =r means “other gets exactly read, nothing else.” Anyone outside the owner and group who relied on running this script will suddenly get “Permission denied.” If the real intent was simply to make sure others can read it without touching anything else they already had, use + instead of =:
chmod o+r shared-script.sh
This adds read for other while leaving any existing execute or write bits untouched.
Best Practices
- Use numeric mode when you’re setting a file’s permissions from scratch or you want to state the complete, unambiguous result in one number (common in scripts and deployment tooling, since it’s deterministic regardless of the file’s prior state).
- Use symbolic mode when you want to change one thing relative to whatever the current permissions are, such as adding execute after writing a script, without needing to know or restate the rest of the mode.
- Always run
ls -l(orstat) after achmodon anything sensitive to confirm the resulting bits match your intent, especially after a numeric change. - Never use
777ora+rwxas a quick fix for a permission error — it’s almost always masking a different problem (wrong owner, wrong group) and opens the file to being modified or deleted by anyone. - For scripts, prefer the minimal execute grant (
u+x, or750/u=rwx,g=rx,o=if a group needs to run it too) rather than making every script world-executable by habit. - When locking down secrets or private keys, remember the target is usually
600(owner read/write, nobody else anything) — many tools (likessh) will actively refuse to use a key file with looser permissions.
Practice Exercises
- Your private SSH key at
~/.ssh/id_rsacurrently shows as-rw-r--r--inls -l. Usingchmod, restrict it so only your own user account can read or write it, with no access for group or other at all. Work out both a numeric command and a symbolic command that achieve this, and verify withls -lthat they produce the identical result. - You have a script
report.shthat currently has mode644. You want your own user and your team’s group to be able to run it, but people outside the group should have no access whatsoever. Write the numericchmodcommand for the end state you want, then write an equivalent symbolic command using=for all three categories. - A shared directory
/srv/uploadsneeds everyone in theuploadersgroup to be able to create and delete files inside it, while people outside the group can only look at the file names (list the directory) but not add or remove anything. Figure out the appropriate permission bits for the owner, group, and other, and express your answer as both an octal number and a symbolicchmodcommand.
Summary
- Every file has nine permission bits: read, write, execute, each split across owner, group, and other.
- Numeric (octal) mode uses one digit per category, from 0-7, computed as r(4) + w(2) + x(1), and always replaces the entire permission set.
- Symbolic mode names who (
u/g/o/a), an operator (+/-/=), and permission letters (r/w/x);+and-are relative to the current mode, while=is absolute for the categories named. - Both notations end up calling the same
chmod()system call and writing the same bits into the file’s inode — there is no functional difference in the result, only in how precisely and safely you can express the change. - Numeric mode is best for setting a complete, known permission state; symbolic mode is best for adjusting one category without disturbing the rest.
- Always verify the result with
ls -l, especially after a numeric change on a file whose prior permissions you weren’t certain of.
