chmod
Change file and directory permissions
chmod changes who can read, write, or execute a file or directory. It never changes who the
owner and the group are, which is chown's job. Every file has three
permission classes (owner, group, and everyone else), and chmod sets them either as a
three-digit numeric mode (chmod 644 file) or as a targeted symbolic edit (chmod u+x file). For the full owner/group/other model, what each bit means on a directory, and how
umask fits in, see File permissions explained.
Numeric mode replaces all nine bits
Numeric mode is one octal digit per class (owner, group, other), where r=4, w=2, and x=1
sum together: chmod 755 script.sh gives the owner rwx (7) and group/other r-x (5) each.
It's fast once the arithmetic is automatic, but it always sets all nine bits in one go,
discarding whatever combination was there before.
Symbolic mode changes one and leaves the rest
Symbolic mode names a class (u, g, o, or a), an operator (+, -, =), and a
permission letter: chmod u+x script.sh adds execute for the owner only, without touching
anything else. Use symbolic mode when you want to flip one bit rather than recompute the
whole three-digit number from scratch.
Recursive changes need care
chmod -R applies a mode to a directory and everything inside it, files and subdirectories
alike, in one pass. That includes anything whose permissions were deliberately tighter than the
rest of the tree (a private key sitting inside a project directory, say), and there's no
built-in undo once it's done. The examples below show a type-aware alternative, using find,
before the blunt -R version and the failure mode it produces.
Permissions aren't the only gate
A permission-denied error doesn't always mean the file's own mode is wrong. Reading, writing, or deleting a file also depends on the permissions of every directory between it and the filesystem root, and deleting a file is governed by the directory's write permission, not the file's. The troubleshooting section below walks through both.
Sample files used on this page
Every example below was run against these files. Recreate them to follow along.
./ the modes every example starts from - ls -l, before any of them run
total 28
-rw-r--r-- 1 user user 0 Aug 12 21:45 a.txt
-rw-r--r-- 1 user user 0 Aug 12 21:45 b.txt
-rw-r--r-- 1 user user 18 Aug 12 21:45 backup.sh
---------- 1 user user 0 Aug 12 21:45 blank.txt
-rw-r--r-- 1 user user 0 Aug 12 21:45 c.txt
drwxr-xr-x 2 user user 4096 Aug 12 21:45 data
-rwxr--r-- 1 user user 20 Aug 12 21:45 deploy.sh
-rw------- 1 user user 0 Aug 12 21:45 id_rsa
drw------- 2 user user 4096 Aug 12 21:45 locked
lrwxrwxrwx 1 user user 9 Aug 12 21:45 notes-link -> notes.txt
-rw-r--r-- 1 user user 0 Aug 12 21:45 notes.txt
drwxr-xr-x 5 user user 4096 Aug 12 21:45 project
-rw-r--r-- 1 user user 0 Aug 12 21:45 report.csv
drwxr-xr-x 2 user user 4096 Aug 12 21:45 shared
-rwxr-xr-x 1 user user 20 Aug 12 21:45 tool
project/ the tree the recursive and auditing examples walk - oops.txt is the world-writable one
project:
total 12
drwxr-xr-x 2 user user 4096 Aug 12 21:45 data
-rw-r--r-- 1 user user 0 Aug 12 21:45 notes.txt
-rw-rw-rw- 1 user user 0 Aug 12 21:45 oops.txt
drwxr-xr-x 2 user user 4096 Aug 12 21:45 scripts
drwxr-xr-x 2 user user 4096 Aug 12 21:45 secrets
project/data:
total 0
-rw-r--r-- 1 user user 0 Aug 12 21:45 report.csv
project/scripts:
total 4
-rw-r--r-- 1 user user 20 Aug 12 21:45 deploy.sh
project/secrets:
total 0
-rw------- 1 user user 0 Aug 12 21:45 id_rsa
Reading and setting basic permissions
Numeric mode for the common case, symbolic mode for a single-bit tweak.
Set a file to the standard rw-r--r-- mode
chmod 644 notes.txt && ls -l notes.txt
The most common numeric mode: the owner can read and write, everyone else can only read.
Show output
-rw-r--r-- 1 user user 0 Aug 12 21:45 notes.txt
Add execute permission for the owner only
chmod u+x deploy.sh && ls -l deploy.sh
u targets the owner, +x adds execute without touching read, write, or the group/other bits.
Show output
-rwxr--r-- 1 user user 20 Aug 12 21:45 deploy.sh
Remove write permission from group and other
chmod go-w notes.txt && ls -l notes.txt
Combine two classes (g and o) in one operator. Owner permissions are untouched.
Show output
-rw-r--r-- 1 user user 0 Aug 12 21:45 notes.txt
Add read permission for everyone
chmod a+r notes.txt && ls -l notes.txt
a means all three classes at once: owner, group, and other.
Show output
-rw-r--r-- 1 user user 0 Aug 12 21:45 notes.txt
Set an exact permission set with symbolic mode
chmod u=rwx,g=rx,o=r deploy.sh && ls -l deploy.sh
= assigns exactly the listed permissions to a class, clearing any others it had. Comma-separate multiple class assignments in a single command.
Show output
-rwxr-xr-- 1 user user 20 Aug 12 21:45 deploy.sh
Check a file's current mode as a number
stat --format '%a %n' deploy.sh
stat prints the numeric mode directly, handy for scripts that need to compare or restore permissions rather than parse ls -l.
Show output
744 deploy.sh
Common permission recipes
The handful of numeric modes that cover most real files.
Make a script executable
chmod 755 deploy.sh && ls -l deploy.sh
rwxr-xr-x: the owner can edit and run it, everyone else can only run it. The standard mode for scripts and compiled binaries.
Show output
-rwxr-xr-x 1 user user 20 Aug 12 21:45 deploy.sh
Set a plain data file to the standard mode
chmod 644 report.csv && ls -l report.csv
rw-r--r--: the owner can edit it, everyone else can read it. What most new files should end up at.
Show output
-rw-r--r-- 1 user user 0 Aug 12 21:45 report.csv
Lock a private key down to owner-only
chmod 600 id_rsa && ls -l id_rsa
rw-------: only the owner can read or write it, nobody else can even list its contents. SSH refuses to use a private key with looser permissions than this.
Show output
-rw------- 1 user user 0 Aug 12 21:45 id_rsa
Lock a directory down to owner-only
chmod 700 data/ && ls -ld data
rwx------: only the owner can list, enter, or modify this directory at all.
Show output
drwx------ 2 user user 4096 Aug 12 21:45 data
Make a file group-writable for shared editing
chmod 664 notes.txt && ls -l notes.txt
rw-rw-r--: owner and group can both edit it, other can only read. Useful in a shared project directory with a common group.
Show output
-rw-rw-r-- 1 user user 0 Aug 12 21:45 notes.txt
Restrict a config file to owner and group, no others
chmod 640 notes.txt && ls -l notes.txt
rw-r-----: owner can edit, group can read, other has no access at all.
Show output
-rw-r----- 1 user user 0 Aug 12 21:45 notes.txt
Restrict a directory to owner-write, group-browse
chmod 750 data/ && ls -ld data
rwxr-x---: owner has full control, group can look inside and read files, other has nothing. A common mode for a team's working directory.
Show output
drwxr-x--- 2 user user 4096 Aug 12 21:45 data
A leading zero is the same as no leading zero
chmod 0644 notes.txt && ls -l notes.txt
The fourth (special-bits) digit defaults to 0, so 0644 and 644 do the same thing. You'll see the four-digit form in scripts that also set setuid/setgid/sticky.
Show output
-rw-r--r-- 1 user user 0 Aug 12 21:45 notes.txt
Symbolic operators: +, -, and =
Add, remove, or set exactly, per class, without recomputing the whole number.
Add execute for all classes with the +x shorthand
chmod +x deploy.sh && ls -l deploy.sh
With no class letter given, chmod acts on all three classes, the same as a+x, but honours the current umask on new bits.
Show output
-rwxr-xr-x 1 user user 20 Aug 12 21:45 deploy.sh
Add a permission to more than one class at once
chmod u+x,g+x deploy.sh && ls -l deploy.sh
Comma-separate multiple class-operator pairs to change several classes differently in one command.
Show output
-rwxr-xr-- 1 user user 20 Aug 12 21:45 deploy.sh
Strip every permission from other
chmod o-rwx notes.txt && ls -l notes.txt
o-rwx removes read, write, and execute for other in one go, without needing to know what other currently had.
Show output
-rw-r----- 1 user user 0 Aug 12 21:45 notes.txt
Copy the owner's permissions onto the group
chmod g=u notes.txt && ls -l notes.txt
g=u sets the group's bits to match whatever the owner currently has, instead of listing them out by hand.
Show output
-rw-rw-r-- 1 user user 0 Aug 12 21:45 notes.txt
Remove execute from every class
chmod a-x deploy.sh && ls -l deploy.sh
Turns an executable back into a plain file without affecting its read or write bits.
Show output
-rw-r--r-- 1 user user 20 Aug 12 21:45 deploy.sh
Add read, write, and conditional execute in one operator
chmod u+rwX blank.txt && ls -l blank.txt
Capital X (unlike lowercase x) only adds execute if the file already has execute set for someone, or if it's a directory. On a plain non-executable file like this one, the X has no effect; only rw gets added.
Show output
-rw------- 1 user user 0 Aug 12 21:45 blank.txt
= without a class still respects the umask
chmod =rwx notes.txt && ls -l notes.txt
=rwx with no class letter defaults to all classes, like a=rwx, but unlike an explicit a=rwx it's filtered through the current umask (0022 here), so group and other lose the write bit that a bare 777 would have kept.
Show output
-rwxr-xr-x 1 user user 0 Aug 12 21:45 notes.txt
Changing several files at once
A single command can carry as many targets as you give it.
Set the same mode on a list of files
chmod 600 a.txt b.txt c.txt && ls -l a.txt b.txt c.txt
chmod accepts any number of targets after the mode; each gets the same change.
Show output
-rw------- 1 user user 0 Aug 12 21:45 a.txt
-rw------- 1 user user 0 Aug 12 21:45 b.txt
-rw------- 1 user user 0 Aug 12 21:45 c.txt
Set the same mode on every file matching a glob
chmod 644 *.txt && ls -l a.txt b.txt c.txt notes.txt
The shell expands *.txt before chmod ever runs, so this touches every matching file in the current directory, not just the ones you're thinking of. Check with ls *.txt first if you're unsure what will match.
Show output
-rw-r--r-- 1 user user 0 Aug 12 21:45 a.txt
-rw-r--r-- 1 user user 0 Aug 12 21:45 b.txt
-rw-r--r-- 1 user user 0 Aug 12 21:45 c.txt
-rw-r--r-- 1 user user 0 Aug 12 21:45 notes.txt
Changing permissions recursively
-R walks a whole directory tree in one pass. Prefer a type-aware approach, since files and directories usually need different modes and a blanket -R can't tell the difference.
Set directories to 755 without touching files
find project -type d -exec chmod 755 {} \;
Restricting the -exec to -type d means files inside are left alone entirely, whatever their current mode.
Set files to 644 without touching directories
find project -type f -exec chmod 644 {} \;
The companion to the directory pass above. Run both to normalise a tree to the standard 755/644 split.
Make only scripts executable, by name pattern
find project -type f -name "*.sh" -exec chmod +x {} \; && ls -l project/scripts/deploy.sh
Restrict the -exec further with -name so only matching files get the execute bit, leaving other file types at their existing mode.
Show output
-rwxr-xr-x 1 user user 20 Aug 12 21:45 project/scripts/deploy.sh
Pipe find into xargs for the same effect
find project -type f -name "*.txt" -print0 | xargs -0 chmod 644
-print0 and xargs -0 handle filenames with spaces or newlines safely, and batch many files into fewer chmod invocations than repeated -exec ... {} \; calls.
Add a permission recursively without resetting anything else
chmod -R u+w project/
Because +w only adds a bit, it can't remove permissions that were already tighter elsewhere in the tree, unlike a numeric -R reset. Safer when the goal is "make sure I can write to all of this" rather than "set an exact mode everywhere."
Add execute recursively, but only where it already makes sense
chmod -R a+X project/
Capital X adds execute to directories (so they stay traversable) and to files that are already executable for someone, but leaves plain data files alone. Safer than a+x for a mixed tree, since it won't make every text file "executable."
Refuse to recurse over / by accident
chmod -R --preserve-root 755 /
--preserve-root is the default in current GNU coreutils, but making it explicit documents intent in a script. It fails fast, before touching anything, if the target resolves to /.
Show output
chmod: it is dangerous to operate recursively on '/'
chmod: use --no-preserve-root to override this failsafe
Reset an entire tree to one mode (the blunt, risky version)
chmod -R 755 project/ && ls -ld project project/data project/data/report.csv project/secrets/id_rsa
Danger: a numeric -R sets every matching file and directory to exactly this mode, overwriting anything tighter that was there on purpose. Run this on a tree containing a 600 private key or a 700 secrets directory and it silently makes them world-readable and world-executable, with no prompt and no way back except restoring from a backup or re-applying the original permissions by hand. Prefer the type-aware find commands above.
Show output
drwxr-xr-x 5 user user 4096 Aug 12 21:45 project
drwxr-xr-x 2 user user 4096 Aug 12 21:45 project/data
-rwxr-xr-x 1 user user 0 Aug 12 21:45 project/data/report.csv
-rwxr-xr-x 1 user user 0 Aug 12 21:45 project/secrets/id_rsa
Special permission bits: setuid, setgid, sticky
Above the normal nine bits sit three more, with a numeric digit of their own (4000/2000/1000).
Make new files in a directory inherit its group
chmod g+s shared/ && ls -ld shared
Setgid on a directory makes files created inside it take the directory's group, instead of the creating user's primary group. Shows as an s in the group execute slot.
Show output
drwxr-sr-x 2 user user 4096 Aug 12 21:45 shared
Set setgid and full group access in one numeric mode
chmod 2775 shared/ && ls -ld shared
The leading 2 sets setgid; 775 gives owner and group full access and other read/execute. A common mode for a shared team directory.
Show output
drwxrwsr-x 2 user user 4096 Aug 12 21:45 shared
Set setuid on an executable
chmod 4755 tool && ls -l tool
The leading 4 sets setuid, shown as s in the owner execute slot: the program runs with the file owner's privileges rather than the caller's. Note this only takes effect on compiled binaries: the Linux kernel deliberately ignores the setuid bit on #!-scripts for security reasons, even though chmod still applies it to the file.
Show output
-rwsr-xr-x 1 user user 20 Aug 12 21:45 tool
Remove setuid and setgid
chmod u-s,g-s tool
Clears both special bits with symbolic mode, without needing to know or recompute the rest of the numeric mode.
Set the sticky bit on a shared directory
chmod +t shared/ && ls -ld shared
Sticky, shown as t in the other execute slot, restricts deleting or renaming a file inside the directory to that file's own owner (or root), even when others have write access to the directory itself. This is how /tmp stays shared but not a free-for-all.
Show output
drwxr-xr-t 2 user user 4096 Aug 12 21:45 shared
Set sticky plus full permissions in one numeric mode, like /tmp
chmod 1777 shared/ && ls -ld shared
The leading 1 sets sticky; 777 gives everyone full access. Matches the mode /tmp uses on a standard Debian install.
Show output
drwxrwxrwt 2 user user 4096 Aug 12 21:45 shared
chmod and symlinks
Symlinks themselves have no meaningful permissions on Linux; chmod follows them by default.
chmod on a symlink changes its target, not the link
chmod 600 notes-link && ls -l notes-link notes.txt
By default chmod dereferences a symlink and changes whatever it points to. The link itself always shows rwxrwxrwx and that never changes.
Show output
lrwxrwxrwx 1 user user 9 Aug 12 21:45 notes-link -> notes.txt
-rw------- 1 user user 0 Aug 12 21:45 notes.txt
Try to change the symlink itself instead of its target
chmod -hv 644 notes-link
-h asks chmod to affect the symlink rather than its target. On Linux this is a no-op: the kernel has no lchmod syscall, so symlink permissions cannot be set. -v shows the command noticing and skipping it rather than pretending to succeed.
Show output
neither symbolic link 'notes-link' nor referent has been changed
Copying and reporting permission changes
Reuse a known-good mode, or see exactly what changed.
Copy one file's permissions onto another
chmod --reference=deploy.sh notes.txt && ls -l notes.txt
Sets notes.txt to whatever mode deploy.sh currently has, instead of spelling the mode out. Useful in scripts that need to match an existing file's permissions exactly.
Show output
-rwxr--r-- 1 user user 0 Aug 12 21:45 notes.txt
Only print a line when the mode actually changes
chmod -c 755 notes.txt
-c (--changes) stays silent if the file was already at the target mode, and reports it like -v only when something changed.
Show output
mode of 'notes.txt' changed from 0644 (rw-r--r--) to 0755 (rwxr-xr-x)
Always report the mode, changed or not
chmod -v 644 notes.txt
-v (--verbose) prints a line for every target, even ones already at the requested mode, useful when auditing a batch change ran over everything you expected.
Show output
mode of 'notes.txt' retained as 0644 (rw-r--r--)
Auditing permissions across a tree
find's -perm test locates files by mode, a natural pairing with chmod.
Find world-writable files, a common security smell
find project -type f -perm -002
-perm -002 matches files where the other-write bit is set, regardless of the rest of the mode. Worth running before trusting a tree pulled from somewhere else.
Show output
project/oops.txt
Find setuid binaries on the system
find /usr/bin -maxdepth 1 -perm -4000 | sort
-perm -4000 matches the setuid bit specifically. A short, expected list on a stock Debian install (su, passwd, and a handful of others); an unfamiliar entry here is worth investigating.
Show output
Your output will differ: which binaries are setuid depends on what you have installed; this is a minimal Debian plus sudo
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/gpasswd
/usr/bin/mount
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/su
/usr/bin/sudo
/usr/bin/umount
Troubleshooting permission errors
What chmod itself reports, and what looks like a chmod problem but isn't.
chmod on a file that doesn't exist
chmod 644 does-not-exist.txt
A plain, specific error rather than a silent no-op. Check the path and spelling before assuming permissions are the problem.
Show output
chmod: cannot access 'does-not-exist.txt': No such file or directory
chmod on a file you don't own
chmod 600 /etc/shadow
Only the file's owner (or root) can change its mode. Everyone else gets Operation not permitted, no matter what permissions the file currently has.
Show output
chmod: changing permissions of '/etc/shadow': Operation not permitted
Running a script without the execute bit
./backup.sh
Read permission is enough to view a script's contents, but execute permission is required to run it directly. chmod +x is the fix, not a syntax problem in the script.
Show output
bash: ./backup.sh: Permission denied
A directory missing its execute bit blocks access to files inside, even with read
ls -l locked/
Read on a directory lets you list filenames; execute is what lets you reach the files by name to open or stat them. Without it, even the owner sees every entry as inaccessible.
Show output
ls: cannot access 'locked/secret.txt': Permission denied
total 0
-????????? ? ? ? ? ? secret.txt
Deleting a file depends on the directory's permissions, not the file's
chmod 000 data/locked.txt
rm -f data/locked.txt
A file with no permissions at all can still be deleted, because removing a directory entry is controlled by the directory's write bit, not the target file's mode. Locking a file down to 000 does not protect it from deletion by anyone who can write to its parent directory. rm has the same rule from the other side, including what the sticky bit on /tmp is there to fix.
chmod changes existing files; umask governs new ones
umask
touch fresh.txt
stat -c '%A %n' fresh.txt
chmod never runs automatically. Every new file lands at a mode set by umask subtracted from a default, which is why plain files consistently appear at 644 without anyone typing chmod. See File permissions explained for how the subtraction works.
Show output
0022
-rw-r--r-- fresh.txt