chmod Command

Change File Modes (Permissions)

chmod [OPTION]... MODE[,MODE]... FILE...
chmod [OPTION]... OCTAL-MODE FILE...
chmod [OPTION]... --reference=RFILE FILE...

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

Owner (User)
rwx
File creator/owner
Group
rwx
Group members
Others
rwx
Everyone else

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
Reading Permission Strings: When you see -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

$ ls -l script.sh
-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
Explanation: Octal notation is the most common way to set complete permission sets. 755 (rwxr-xr-x) is standard for executables and directories - the owner can do everything, while group and others can read and execute. 644 (rw-r--r--) is standard for regular files - owner can edit, everyone can read. 600 (rw-------) is for private files like SSH keys where only the owner should have any access. The three digits represent owner, group, and other permissions respectively.
Quick Calculation: For 755: Owner=7 (4+2+1=rwx), Group=5 (4+1=r-x), Other=5 (4+1=r-x). Add the numbers where you want permissions: read=4, write=2, execute=1.

Example 2: Using Symbolic Notation to Add/Remove Permissions

$ ls -l report.txt
-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
Explanation: Symbolic notation is more flexible for incremental changes. 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.
Symbolic vs Octal: Use symbolic when you want to modify one or two permissions without changing the rest. Use octal when you want to set a complete, known permission pattern all at once.

Example 3: Recursive Permission Changes

$ ls -la project/
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
Explanation: The -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.
Common Mistake: Never do 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

# Setuid (4000) - runs with owner's permissions
$ 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
Explanation: Beyond basic permissions, Linux has three special permission bits. Setuid (4xxx) causes executables to run with the file owner's privileges - passwd uses this to let normal users change passwords (needs root). Setgid (2xxx) on files runs with group privileges; on directories, new files inherit the directory's group (crucial for shared team directories). Sticky bit (1xxx) on directories means only the file owner can delete their files, even if others have write permission to the directory - /tmp uses this so users can't delete each other's temp files.
Security Warning: Setuid/setgid are powerful and dangerous. Setuid scripts in particular can create security holes. Only use on carefully audited executables. Most systems ignore setuid on shell scripts for security reasons. Never set setuid on user scripts without thorough security review.

Example 5: Using the X (Conditional Execute) Permission

$ mkdir test_dir
$ 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
Explanation: The capital 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.
Best Practice: Use +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

# Typical web directory structure
$ 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
Explanation: Web directories require careful permission management for security. Static content (HTML, CSS, images) should be 644 for files and 755 for directories - the web server can read and serve them, but can't modify them. Upload directories need 775 with setgid so the web server can write uploaded files and they inherit the correct group. Never use 777 on web directories as this allows anyone to modify your website. The find commands ensure files and directories get appropriate, different permissions.
Web Security: Common mistakes that create vulnerabilities:
  • 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

# SSH is very strict about key file 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.
Explanation: SSH is extremely security-conscious and will refuse to use keys with incorrect permissions. Private keys (id_rsa, id_ed25519) must be 600 (only owner can read/write) - SSH will refuse to use a private key that others can read. The .ssh directory itself should be 700 (only owner access). Public keys (.pub files), known_hosts, and config can be 644 (owner write, all read). The authorized_keys file should be 600. If permissions are wrong, SSH will fail with error messages about "unprotected private key file" or "permissions are too open."
Quick SSH Permission Fix: If you're having SSH key problems, run:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_*
chmod 644 ~/.ssh/*.pub
chmod 600 ~/.ssh/authorized_keys

Example 8: Using --reference to Match Permissions

$ ls -l template.sh newscript.sh
-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
Explanation: The --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

$ chmod -v 755 script1.sh script2.sh script3.sh
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
Explanation: The -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.
Best Practice: Always use -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

# Check original permissions
$ 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
Explanation: Many file operations don't preserve permissions by default. Regular 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.
Common Gotcha: Simple file copies lose special permissions, execute bits, and setuid/setgid/sticky bits. Always use -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

1. Principle of Least Privilege

Grant only the minimum permissions needed. Start restrictive (600/700) and open up only as needed.

2. Never Use 777

World-writable permissions (777) are almost always wrong and create security vulnerabilities. Use 755 or 775 instead.

3. Separate Files and Directories

Use find to set different permissions for files vs directories:

find /path -type f -exec chmod 644 {} \;
find /path -type d -exec chmod 755 {} \;
4. Use Symbolic for Modifications, Octal for Setting

Use symbolic (u+x, g-w) when modifying specific permissions. Use octal (755, 644) when setting complete permission patterns.

Security Checklist:
  • ✓ 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