chmod 755 vs u+x: numeric and symbolic modes
Change Linux permissions with chmod: the octal digits, the u/g/o symbolic form, why a recursive numeric mode breaks a tree, and what capital X fixes.
What chmod changes
chmod changes the permission bits on a file or a directory, and it accepts those bits in one of two notations. The numeric form is an octal number that replaces every permission bit at once. The symbolic form names a class of user and an operator, and it edits only the bits you name. Both end in the same system call. The difference is what happens to the bits you did not mention.
Reading a mode and changing a mode are different jobs. If the letters in a long listing are not yet obvious, read what drwxr-xr-x means field by field first. This guide only changes them.
Run everything here as your ordinary login user. The root account bypasses these checks, so a permission demonstration run as root proves nothing. If you are still working as root on a new server, create a normal user with least privilege and come back.
Set up a sandbox you can throw away
cd "$(mktemp -d)"
touch notes.txtmktemp -d creates an empty directory under /tmp and prints the path it made, and command substitution passes that path straight to cd. Nothing below touches a file you care about.
One command reports the result of every change:
stat -c '%a %A %n' notes.txt%a prints the mode as an octal number, %A prints the same mode in the letter form a long listing uses, and %n prints the name. In the blocks that follow, a line starting with $ is what you type, and the line under it is what the command printed. Run them yourself. The output on your machine should match line for line, because a numeric mode does not depend on anything in your environment.
The numeric form: one octal digit per class
The first digit belongs to the owner of the file. The second belongs to the file's group. The third belongs to everyone else. Each digit is a sum, one value per bit. Set one bit at a time and read what stat reports.
$ chmod 400 notes.txt
$ stat -c '%a %A %n' notes.txt
400 -r-------- notes.txt
$ chmod 200 notes.txt
$ stat -c '%a %A %n' notes.txt
200 --w------- notes.txt
$ chmod 100 notes.txt
$ stat -c '%a %A %n' notes.txt
100 ---x------ notes.txtThe letters line up with the digits. After the leading file-type character, the first group of three letters is the owner's, the next three are the group's, and the last three belong to everyone else. Add the values to combine bits inside one class.
$ chmod 600 notes.txt
$ stat -c '%a %A %n' notes.txt
600 -rw------- notes.txt
$ chmod 700 notes.txt
$ stat -c '%a %A %n' notes.txt
700 -rwx------ notes.txt
$ chmod 750 notes.txt
$ stat -c '%a %A %n' notes.txt
750 -rwxr-x--- notes.txtA numeric mode is absolute. It writes all nine bits on every run, so a class you never thought about is written too.
$ chmod 755 notes.txt
$ stat -c '%a %A %n' notes.txt
755 -rwxr-xr-x notes.txt
$ chmod 600 notes.txt
$ stat -c '%a %A %n' notes.txt
600 -rw------- notes.txtThe second command said nothing about the group or about everyone else, and their bits changed anyway. That is the character of the numeric form, and it is why the numeric form is the wrong tool for a small edit. (chmod also handles the setuid, setgid and sticky bits, which live in a fourth digit ahead of these. That is a separate topic, and this guide stays with the three.)
The symbolic form: a class, an operator, then the bits
Write the class first. u is the owner, g is the group, o is everyone else, and a is all of them at once. Then the operator: + adds bits, - removes bits, and = sets that class to exactly the bits you list and clears the rest of its bits. Then the bits themselves, as r, w or x.
$ chmod 640 notes.txt
$ stat -c '%a %A %n' notes.txt
640 -rw-r----- notes.txt
$ chmod u+x notes.txt
$ stat -c '%a %A %n' notes.txt
740 -rwxr----- notes.txt
$ chmod g-r notes.txt
$ stat -c '%a %A %n' notes.txt
700 -rwx------ notes.txt
$ chmod o=r notes.txt
$ stat -c '%a %A %n' notes.txt
704 -rwx---r-- notes.txtEach command touched one class and left the others where they were. Combine clauses with commas and no spaces, and chmod applies them from left to right.
$ chmod u=rw,go=r notes.txt
$ stat -c '%a %A %n' notes.txt
644 -rw-r--r-- notes.txt
$ chmod go= notes.txt
$ stat -c '%a %A %n' notes.txt
600 -rw------- notes.txtgo= with nothing after the equals sign takes every bit away from the group and from everyone else. That is the shape you want on a file only its owner may read, such as the private half of an SSH key pair.
chmod can also report its own work, which matters inside a script where nobody is watching the screen.
chmod -v u+x notes.txt
chmod -v u+x notes.txt-v prints a line for every file it was given. Run both commands and compare the two lines on your screen: the first names the file and reports the mode it changed from and the mode it changed to, and the second reports the mode as retained, because the bit it was asked to add was already set. -c prints a line only when a file actually changed, so it is the better one to keep in a log.
chmod 755 vs u+x: which notation to use
Start from the same file twice and compare what each notation leaves behind.
$ chmod 640 notes.txt
$ chmod 755 notes.txt
$ stat -c '%a %A %n' notes.txt
755 -rwxr-xr-x notes.txt
$ chmod 640 notes.txt
$ chmod u+x notes.txt
$ stat -c '%a %A %n' notes.txt
740 -rwxr----- notes.txtBoth commands made the file executable for its owner. Only one of them left the other two classes as they were. Use a numeric mode when you know the exact end state you want and you want it enforced, which is the honest case in a deployment script that must produce the same result on a machine you have never seen. Use a symbolic mode when the sentence in your head contains add or remove, because the numeric form has no way to say leave that part alone.
The mode a new file starts with does not come from chmod at all. It comes from the umask of the process that created the file. Run umask and umask -S in your own shell to see the value in force there, and expect it to differ between distributions, between your login shell and a systemd service, and between one account and another. The umask sets the mode of every new file, so it decides what you are starting from before chmod runs.
What the execute bit does on a directory
This is the misunderstanding that costs the most time. On a regular file, the execute bit says the kernel may run the file. A directory holds no instructions, so there is nothing in it to run. On a directory the execute bit means traversal: permission to resolve a name inside it. To reach dir/file you need it on dir. The read bit is a separate permission, and it allows listing the names the directory holds. You can hold either one without the other, which is easier to believe once you have watched it happen.
$ mkdir vault
$ printf 'hello\n' > vault/secret.txt
$ chmod 600 vault/secret.txt
$ chmod 100 vault
$ echo vault/*
vault/*
$ cat vault/secret.txt
helloThe shell printed the pattern back unchanged because expanding vault/* means reading the directory, and a glob that matches nothing is passed through as literal text instead of raising an error. cat still worked because you supplied the name yourself and traversal was allowed. Now swap the two bits over.
$ chmod 400 vault
$ echo vault/*
vault/secret.txt
$ cat vault/secret.txt
cat: vault/secret.txt: Permission deniedThe names are visible and the contents are not. Read gives you the list. Execute gives you the way in. Almost every directory you use wants both.
$ chmod 500 vault
$ cat vault/secret.txt
helloWhy chmod -R with a numeric mode damages a tree
-R walks the whole tree and applies the mode you gave to everything it meets. A directory and a data file want different bits, so one absolute number cannot be correct for both. Build a small tree and watch it go wrong.
$ mkdir -p site/css
$ touch site/index.html site/css/main.css
$ printf '#!/bin/bash\necho hi\n' > site/deploy.sh
$ chmod 755 site site/css site/deploy.sh
$ chmod 644 site/index.html site/css/main.css
$ stat -c '%a %A %n' site site/css site/index.html site/css/main.css site/deploy.sh
755 drwxr-xr-x site
755 drwxr-xr-x site/css
644 -rw-r--r-- site/index.html
644 -rw-r--r-- site/css/main.css
755 -rwxr-xr-x site/deploy.shThat is the layout a directory of static files wants. Here is the first recursive mistake.
$ chmod -R 755 site
$ stat -c '%a %A %n' site/index.html site/css/main.css
755 -rwxr-xr-x site/index.html
755 -rwxr-xr-x site/css/main.cssNothing broke, and that is the problem. This mistake is silent, so it survives in copied instructions for years. Two files of data now claim to be programs. Git stores the execute bit, so your next commit carries the change to everyone who clones the repository, and rsync and tar carry it to every machine you copy the tree to.
The second recursive mistake is loud. Aim a mode with no execute bit at a directory and the directory closes.
$ chmod 644 site
$ stat -c '%a %A %n' site
644 drw-r--r-- site
$ echo site/*
site/css site/deploy.sh site/index.html
$ cd site
bash: cd: site: Permission denied
$ cat site/index.html
cat: site/index.html: Permission deniedThe names are still listed because the read bit survived, and every path through the directory is closed because the execute bit did not. chmod -R 644 site does that to every directory in the tree in one command. What you find afterwards is not always uniform either, because chmod has to walk through the same directories whose traversal it is taking away, so check what actually landed with find site -type d -exec stat -c '%a %n' {} + instead of assuming. Put this one back before continuing.
$ chmod 755 site
$ chmod 644 site/index.html site/css/main.css
$ stat -c '%a %A %n' site site/index.html
755 drwxr-xr-x site
644 -rw-r--r-- site/index.htmlA recursive chmod has no undo. chmod does not record what it replaced, so the repair is you, rebuilding modes from what you believe they used to be. That is the same slow and uncertain work as reconstructing files after an accidental rm -rf, and the lesson is the same one: check the target of a recursive command before you press enter.
The capital X: execute on directories, not on every file
Symbolic modes accept X as well as x. X sets the execute bit only where it makes sense: on a directory, or on a regular file that already carries an execute bit for some class. That rule is exactly the rule you wanted -R to follow.
$ chmod -R u=rwX,go=rX site
$ stat -c '%a %A %n' site site/css site/index.html site/css/main.css site/deploy.sh
755 drwxr-xr-x site
755 drwxr-xr-x site/css
644 -rw-r--r-- site/index.html
644 -rw-r--r-- site/css/main.css
755 -rwxr-xr-x site/deploy.shOne command, and the directories stayed traversable while the data files were left alone. The script kept its execute bit because it already had one, which is the second half of the X rule. X reads the mode a file has right now, so a file that has already lost every execute bit cannot be brought back by it.
$ chmod 644 site/deploy.sh
$ chmod -R u=rwX,go=rX site
$ stat -c '%a %A %n' site/deploy.sh
644 -rw-r--r-- site/deploy.sh
$ site/deploy.sh
bash: site/deploy.sh: Permission denied
$ chmod u+x site/deploy.sh
$ stat -c '%a %A %n' site/deploy.sh
744 -rwxr--r-- site/deploy.sh
$ site/deploy.sh
hiThe same rule works the other way. X will extend an execute bit a file already has to another class:
$ chmod go+X site/deploy.sh
$ stat -c '%a %A %n' site/deploy.sh
755 -rwxr-xr-x site/deploy.shSo lowercase x is for one named file you mean to make runnable, and X is for the sweep across a tree. When you want an exact mode per kind of thing rather than a rule based on the current mode, split the walk with find instead.
find site -type d -exec chmod 755 {} +
find site -type f -exec chmod 644 {} +
chmod u+x site/deploy.sh-type d selects directories and -type f selects regular files, so each kind gets the mode it should have. -exec ... + batches many paths into one chmod call rather than starting a process per file, which matters on a tree with thousands of entries. The script gets its bit back on a separate line, deliberately.
Check the result before you walk away
Two commands find the mistakes that matter after any recursive change. The first lists directories you can no longer walk into.
find . -type d ! -perm -u+xThe second lists files that any account on the machine may edit.
find . -type f -perm -o+wIn -perm -o+w, the leading - means at least these bits, so it matches a file whose o class holds the write bit whatever else is set. Neither command printing anything is the result you want. Run both from the top of any tree you have just changed with -R, and run stat -c '%a %A %n' on the handful of paths you meant to change, so the mode you believe you set is the mode the kernel actually holds.
FAQ
What is the difference between chmod 755 and chmod u+x?
A numeric mode is absolute. It writes every permission bit on the file, so the classes you did not think about get rewritten as well. A symbolic mode is an edit. u+x adds one bit for the owner and leaves every other bit as it was. Use the numeric form when you know the exact end state you want on a file. Use the symbolic form when you want to add or remove one thing. Run stat -c '%a %A %n' <file> before and after either command and the difference shows up in the output.
Why is chmod -R 755 on a directory of web files a bad idea?
Because -R sends the same absolute mode to directories and to regular files, and those two need different bits. A directory needs the execute bit to be traversable. Data files such as HTML and CSS do not need it, and marking them executable is a change git records in your next commit and rsync copies to the next machine. Use chmod -R u=rwX,go=rX <dir> so the execute bit lands on directories only, or drive it from find with -type d and -type f so each kind gets its own mode.
What does the execute bit do on a directory?
It grants traversal, not execution. There is nothing inside a directory to run. The execute bit is permission to resolve a name inside it, so you need it on every directory along a path before you can open the file at the end of that path. The read bit is separate and grants listing the names. You can hold one without the other: with execute alone you can open a file whose name you already know while the shell cannot expand a glob in that directory, and with read alone you can see the names while every attempt to open one fails.
When should I use capital X instead of lowercase x?
Use X whenever the command is recursive. X sets the execute bit on directories, and on regular files only when one execute bit is already set, so a single pass makes a tree traversable without turning data files into programs. Use lowercase x when you are pointing at one file and you mean that file. One limit is worth remembering: X cannot restore an execute bit that has already been cleared everywhere on a file, because it has nothing left to match on. Put that one back with chmod u+x <file>.
Does chmod change who owns a file?
No. chmod only changes permission bits. Ownership belongs to chown for the user and chgrp for the group, and handing a file to a different user needs root. This matters because the bits are read against the owner and group recorded on the file, not against whoever wrote it last. Run stat -c '%U %G %a %n' <file> to see the owner, the group and the mode together before you decide which of the three commands you actually need.