Overview
The chmod (change mode) command changes the permissions (or modes) of files and directories in Linux and Unix systems. Permissions control who can read, write, or execute files, forming the foundation of Linux file security. Understanding chmod is essential for system administration, secure file sharing, and protecting sensitive data.
Linux permissions operate on three levels: owner (user), group, and others. Each level can have three types of permissions: read (r), write (w), and execute (x). The chmod command allows you to modify these permissions using either symbolic notation (letters) or octal notation (numbers).
Permission Structure
Permission Types
| Permission | Symbol | Octal Value | On Files | On Directories |
|---|---|---|---|---|
| Read | r | 4 | View file contents | List directory contents |
| Write | w | 2 | Modify file contents | Create/delete files in directory |
| Execute | x | 1 | Run as program/script | Enter (cd into) directory |
-rwxr-xr--, break it down as:
- First character: File type (- = regular file, d = directory, l = symlink)
- Characters 2-4: Owner permissions (rwx = read, write, execute)
- Characters 5-7: Group permissions (r-x = read, no write, execute)
- Characters 8-10: Other permissions (r-- = read only)
Octal (Numeric) vs Symbolic Notation
Octal Notation
Uses three digits (0-7), each digit is the sum of: r=4, w=2, x=1
| Octal | Binary | Permissions | Meaning |
|---|---|---|---|
| 0 | 000 | --- | No permissions |
| 1 | 001 | --x | Execute only |
| 2 | 010 | -w- | Write only |
| 3 | 011 | -wx | Write and execute |
| 4 | 100 | r-- | Read only |
| 5 | 101 | r-x | Read and execute |
| 6 | 110 | rw- | Read and write |
| 7 | 111 | rwx | Full permissions |
Common Octal Permission Patterns
| Octal | Permissions | Typical Use |
|---|---|---|
| 644 | rw-r--r-- | Regular files (owner can edit, others read) |
| 755 | rwxr-xr-x | Executable files, directories |
| 600 | rw------- | Private files (SSH keys, passwords) |
| 700 | rwx------ | Private executables, directories |
| 664 | rw-rw-r-- | Group-writable files |
| 775 | rwxrwxr-x | Group-writable directories |
| 777 | rwxrwxrwx | World-writable (use with caution!) |
Symbolic Notation
Uses letters and operators to add, remove, or set specific permissions:
| Component | Options | Meaning |
|---|---|---|
| Who | u, g, o, a | User (owner), Group, Others, All |
| Operator | +, -, = | Add, Remove, Set exactly |
| Permission | r, w, x | Read, Write, Execute |
Detailed Examples
Example 1: Basic Octal Permission Setting
-rw-r--r-- 1 craig users 1024 Nov 24 08:30 script.sh
# Make it executable for everyone
$ chmod 755 script.sh
$ ls -l script.sh
-rwxr-xr-x 1 craig users 1024 Nov 24 08:30 script.sh
# Make it private (only owner can read/write)
$ chmod 600 config.txt
$ ls -l config.txt
-rw------- 1 craig users 512 Nov 24 08:30 config.txt
# Standard permissions for regular files
$ chmod 644 document.txt
$ ls -l document.txt
-rw-r--r-- 1 craig users 2048 Nov 24 08:30 document.txt
Example 2: Using Symbolic Notation to Add/Remove Permissions
-rw-r--r-- 1 craig users 1024 Nov 24 08:30 report.txt
# Add execute permission for owner
$ chmod u+x report.txt
$ ls -l report.txt
-rwxr--r-- 1 craig users 1024 Nov 24 08:30 report.txt
# Add write permission for group
$ chmod g+w report.txt
$ ls -l report.txt
-rwxrw-r-- 1 craig users 1024 Nov 24 08:30 report.txt
# Remove read permission from others
$ chmod o-r report.txt
$ ls -l report.txt
-rwxrw---- 1 craig users 1024 Nov 24 08:30 report.txt
# Add execute for group and others
$ chmod go+x script.sh
# Remove write from everyone
$ chmod a-w readonly.txt
u+x means "add execute for user (owner)", g+w adds write for group, o-r removes read from others. You can combine targets: go+x adds execute for both group and others. a means "all" (user, group, and others). This method is better when you want to modify specific permissions without affecting others, unlike octal which sets all permissions at once.
Example 3: Recursive Permission Changes
total 16
drwxr-xr-x 3 craig users 4096 Nov 24 08:30 .
drwxr-xr-x 5 craig users 4096 Nov 24 08:29 ..
-rw------- 1 craig users 1234 Nov 24 08:30 README.md
drwx------ 2 craig users 4096 Nov 24 08:30 src
-rw------- 1 craig users 567 Nov 24 08:30 config.yaml
# Make all files group-readable recursively
$ chmod -R g+r project/
$ ls -la project/
drwxr-xr-x 3 craig users 4096 Nov 24 08:30 .
-rw-r----- 1 craig users 1234 Nov 24 08:30 README.md
drwxr-x--- 2 craig users 4096 Nov 24 08:30 src
-rw-r----- 1 craig users 567 Nov 24 08:30 config.yaml
# Set standard permissions recursively
$ find project -type f -exec chmod 644 {} \;
$ find project -type d -exec chmod 755 {} \;
# Better approach using find
$ chmod -R 755 project/ # directories get 755
$ find project -type f -exec chmod 644 {} \; # files get 644
-R (recursive) flag applies permissions to a directory and all its contents. However, be careful: chmod -R 644 would make directories non-executable (can't cd into them). The better approach is to use find to separate files and directories: give directories 755 (need execute to enter them) and files 644 (no execute needed). The find method is more precise for complex permission structures where files and directories need different permissions.
chmod -R 777 on a directory tree. This gives everyone full permissions to everything, creating security risks. Similarly, chmod -R 644 makes directories inaccessible. Always consider files vs directories separately.
Example 4: Special Permissions - Setuid, Setgid, and Sticky Bit
$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 59640 Mar 22 2019 /usr/bin/passwd
# Notice the 's' in owner execute position
# Set setuid on a script (be very careful!)
$ chmod 4755 myscript.sh
$ ls -l myscript.sh
-rwsr-xr-x 1 craig users 256 Nov 24 08:30 myscript.sh
# Setgid (2000) - runs with group's permissions
# On directories: new files inherit directory's group
$ chmod 2775 /shared/project
$ ls -ld /shared/project
drwxrwsr-x 2 craig developers 4096 Nov 24 08:30 /shared/project
# Notice the 's' in group execute position
# Sticky bit (1000) - only owner can delete files
$ chmod 1777 /tmp/shared
$ ls -ld /tmp/shared
drwxrwxrwt 2 craig users 4096 Nov 24 08:30 /tmp/shared
# Notice the 't' in other execute position
# Example of /tmp directory
$ ls -ld /tmp
drwxrwxrwt 10 root root 4096 Nov 24 08:30 /tmp
Example 5: Using the X (Conditional Execute) Permission
$ touch test_dir/file1.txt test_dir/script.sh
$ chmod +x test_dir/script.sh
$ ls -la test_dir/
total 8
drwxr-xr-x 2 craig users 4096 Nov 24 08:30 .
drwxr-xr-x 3 craig users 4096 Nov 24 08:30 ..
-rw-r--r-- 1 craig users 0 Nov 24 08:30 file1.txt
-rwxr-xr-x 1 craig users 0 Nov 24 08:30 script.sh
# Using capital X - adds execute only to directories and already-executable files
$ chmod -R g+X test_dir/
$ ls -la test_dir/
drwxr-xr-x 2 craig users 4096 Nov 24 08:30 . # directory got +x
-rw-r--r-- 1 craig users 0 Nov 24 08:30 file1.txt # no change
-rwxr-xr-x 1 craig users 0 Nov 24 08:30 script.sh # already had x
# This is perfect for fixing directory permissions after recursive copy
$ cp -r /source /dest
$ chmod -R u+rwX,go+rX,go-w /dest
X is a "smart execute" permission. It adds execute permission only to directories and to files that already have execute permission for someone. This is extremely useful for recursive operations where you want directories to be traversable but don't want to make regular text files executable. The common pattern chmod -R u+rwX,go+rX,go-w sets: owner full access, group/others read access, with directories executable but not making every file executable.
+X instead of +x in recursive operations to avoid accidentally making data files executable. This is especially important for web directories where you want to traverse directories but not execute HTML/CSS/image files.
Example 6: Setting Web Server Permissions
$ sudo ls -la /var/www/html/
total 40
drwxr-xr-x 5 www-data www-data 4096 Nov 24 08:00 .
-rw-r--r-- 1 www-data www-data 8192 Nov 24 08:00 index.html
drwxr-xr-x 2 www-data www-data 4096 Nov 24 08:00 css
drwxr-xr-x 2 www-data www-data 4096 Nov 24 08:00 images
drwxr-xr-x 2 www-data www-data 4096 Nov 24 08:00 scripts
# Secure web permissions setup
# Files: 644 (owner rw, others r)
$ sudo find /var/www/html -type f -exec chmod 644 {} \;
# Directories: 755 (owner rwx, others rx)
$ sudo find /var/www/html -type d -exec chmod 755 {} \;
# Special case: uploads directory (web server can write)
$ sudo chmod 775 /var/www/html/uploads
$ sudo chmod g+s /var/www/html/uploads # setgid
# Verify no execute permissions on content files
$ ls -la /var/www/html/
drwxr-xr-x 5 www-data www-data 4096 Nov 24 08:00 .
-rw-r--r-- 1 www-data www-data 8192 Nov 24 08:00 index.html
drwxr-xr-x 2 www-data www-data 4096 Nov 24 08:00 css
drwxrwsr-x 2 www-data www-data 4096 Nov 24 08:00 uploads
- 777 permissions (world-writable) - attackers can modify your files
- Execute permissions on .html/.php/.js files - not needed and risky
- Write permissions for web server on code files - allows code injection
- No restrictions on upload directories - allows malicious file uploads
Example 7: SSH Key Permissions
$ ls -la ~/.ssh/
total 32
drwx------ 2 craig craig 4096 Nov 24 08:00 .
drwxr-xr-x 25 craig craig 4096 Nov 24 08:00 ..
-rw------- 1 craig craig 3326 Nov 24 08:00 id_rsa
-rw-r--r-- 1 craig craig 743 Nov 24 08:00 id_rsa.pub
-rw-r--r-- 1 craig craig 2156 Nov 24 08:00 known_hosts
-rw-r--r-- 1 craig craig 356 Nov 24 08:00 config
# Fix SSH directory permissions
$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/id_rsa
$ chmod 644 ~/.ssh/id_rsa.pub
$ chmod 644 ~/.ssh/known_hosts
$ chmod 644 ~/.ssh/config
# For authorized_keys file
$ chmod 600 ~/.ssh/authorized_keys
# SSH will refuse to work if permissions are wrong:
$ ssh server
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/craig/.ssh/id_rsa' are too open.
chmod 600 ~/.ssh/id_*
chmod 644 ~/.ssh/*.pub
chmod 600 ~/.ssh/authorized_keys
Example 8: Using --reference to Match Permissions
-rwxr-xr-x 1 craig users 1024 Nov 24 08:30 template.sh
-rw-r--r-- 1 craig users 2048 Nov 24 08:31 newscript.sh
# Make newscript.sh have same permissions as template.sh
$ chmod --reference=template.sh newscript.sh
$ ls -l template.sh newscript.sh
-rwxr-xr-x 1 craig users 1024 Nov 24 08:30 template.sh
-rwxr-xr-x 1 craig users 2048 Nov 24 08:31 newscript.sh
# Useful for multiple files
$ touch script1.sh script2.sh script3.sh
$ chmod --reference=template.sh script*.sh
$ ls -l script*.sh
-rwxr-xr-x 1 craig users 0 Nov 24 08:32 script1.sh
-rwxr-xr-x 1 craig users 0 Nov 24 08:32 script2.sh
-rwxr-xr-x 1 craig users 0 Nov 24 08:32 script3.sh
# Get permissions from reference file
$ stat -c %a template.sh
755
--reference option copies permissions from one file to another without needing to know or calculate the actual permission values. This is particularly useful when you want new files to match existing files' permissions, or when standardizing permissions across a set of similar files. You can use it with wildcards to apply one file's permissions to multiple files. The stat command can show you what the permissions are in octal format if you're curious.
Example 9: Verbose and Diagnostic Output
mode of 'script1.sh' changed from 0644 (rw-r--r--) to 0755 (rwxr-xr-x)
mode of 'script2.sh' changed from 0644 (rw-r--r--) to 0755 (rwxr-xr-x)
mode of 'script3.sh' changed from 0644 (rw-r--r--) to 0755 (rwxr-xr-x)
# Show only changes (not files already with correct permissions)
$ chmod -c 644 *.txt
mode of 'file1.txt' changed from 0600 (rw-------) to 0644 (rw-r--r--)
mode of 'file2.txt' retained as 0644 (rw-r--r--)
mode of 'file3.txt' changed from 0666 (rw-rw-rw-) to 0644 (rw-r--r--)
# Recursive with verbose output
$ chmod -Rv g+w project/ 2>&1 | tee chmod.log
mode of 'project/' changed from 0755 (rwxr-xr-x) to 0775 (rwxrwxr-x)
mode of 'project/README.md' changed from 0644 (rw-r--r--) to 0664 (rw-rw-r--)
mode of 'project/src' changed from 0755 (rwxr-xr-x) to 0775 (rwxrwxr-x)
...
# See what would happen without actually changing (simulation)
$ chmod -v 777 /etc/passwd
chmod: changing permissions of '/etc/passwd': Operation not permitted
-v (verbose) flag shows each file being processed and what changes were made, including the old and new permission values in both octal and symbolic notation. The -c (changes) flag is similar but only reports actual changes, not files that already had the target permissions. Verbose output is valuable for auditing, logging system changes, and troubleshooting permission issues. You can pipe this output to tee to save it to a log file while still seeing it on screen, creating an audit trail of permission changes.
-v when making system-wide or important permission changes, especially as root. Save the output for documentation and potential rollback reference.
Example 10: Preserving Permissions During File Operations
$ stat -c "%a %n" /source/script.sh
755 /source/script.sh
# Wrong way - loses permissions
$ cp /source/script.sh /dest/
$ stat -c "%a %n" /dest/script.sh
644 /dest/script.sh # Lost execute permission!
# Right way - preserve permissions with -p
$ cp -p /source/script.sh /dest/script2.sh
$ stat -c "%a %n" /dest/script2.sh
755 /dest/script2.sh # Permissions preserved
# For recursive copies
$ cp -rp /source/ /dest/ # Preserves permissions, ownership, timestamps
# Using rsync (even better for large trees)
$ rsync -av /source/ /dest/ # Preserves everything
# Archive mode with tar preserves permissions
$ tar czf backup.tar.gz /source/
$ tar xzf backup.tar.gz # Permissions restored
# Check permissions before and after operations
$ find /source -ls > source_perms.txt
$ find /dest -ls > dest_perms.txt
$ diff source_perms.txt dest_perms.txt
cp creates new files with default permissions (usually 644 for files, 755 for directories), losing any special permissions. Use cp -p to preserve mode, ownership, and timestamps. For recursive copies, cp -rp or rsync -a (archive mode) preserve all attributes. When creating backups with tar, permissions are stored and restored automatically. Always verify permissions after file operations, especially for security-critical files like scripts, configurations, or SSH keys. Use find or stat to compare permission structures.
-p flag with cp, or better yet, use rsync for anything important. This is especially critical when migrating web servers, backing up system configs, or copying user directories.
Common Options
| Option | Description | Example |
|---|---|---|
-R, --recursive |
Change files and directories recursively | chmod -R 755 /dir |
-v, --verbose |
Output a diagnostic for every file processed | chmod -v 644 file.txt |
-c, --changes |
Like verbose but report only when change is made | chmod -c 755 *.sh |
-f, --silent, --quiet |
Suppress most error messages | chmod -f 644 * |
--reference=FILE |
Use FILE's mode instead of MODE values | chmod --reference=f1 f2 |
--preserve-root |
Fail to operate recursively on / (default) | chmod --preserve-root -R 755 / |
--no-preserve-root |
Do not treat / specially | chmod --no-preserve-root -R 755 / |
Related Commands
Quick Reference Tables
Common Permission Patterns
| Octal | Symbolic | Use Case |
|---|---|---|
| 644 | rw-r--r-- | Regular readable files (text, config) |
| 600 | rw------- | Private files (SSH keys, passwords) |
| 755 | rwxr-xr-x | Executables, directories |
| 700 | rwx------ | Private executables/directories |
| 664 | rw-rw-r-- | Group-editable files |
| 775 | rwxrwxr-x | Group-shared directories |
| 2775 | rwxrwsr-x | Collaborative directories (with setgid) |
| 1777 | rwxrwxrwt | Public directories (like /tmp) |
Best Practices
Grant only the minimum permissions needed. Start restrictive (600/700) and open up only as needed.
World-writable permissions (777) are almost always wrong and create security vulnerabilities. Use 755 or 775 instead.
Use find to set different permissions for files vs directories:
find /path -type d -exec chmod 755 {} \;
Use symbolic (u+x, g-w) when modifying specific permissions. Use octal (755, 644) when setting complete permission patterns.
- ✓ SSH private keys must be 600
- ✓ Home directories should be 700 or 750
- ✓ Web content should not be world-writable
- ✓ Executables in PATH should not be group/world-writable
- ✓ Configuration files should be 644 or more restrictive
- ✓ Never use setuid on shell scripts
Exit Status
| Code | Meaning |
|---|---|
| 0 | Success - permissions changed |
| 1 | Failure - permission denied, file not found, or invalid mode |