Every file and directory on a Linux system has a set of permissions that controls who can read, write, and execute it. Understanding this permission model is essential for system administration, web server configuration, deployment scripts, and everyday development. Whether you're debugging a "Permission denied" error or hardening a production server, this guide covers everything you need to know.
The Unix Permission Model
Linux inherited its permission system from Unix. Every file has three attributes that determine access:
- Owner — The user who owns the file. Usually the user who created it.
- Group — A group of users who share access. Every file belongs to exactly one group.
- Others — Everyone else on the system who isn't the owner and isn't in the group.
For each of these three categories, three permission bits control access:
- Read (r) — View the contents of a file, or list the contents of a directory.
- Write (w) — Modify the file, or create/delete files within a directory.
- Execute (x) — Run the file as a program, or enter (cd into) a directory.
Reading Permission Strings
When you run ls -l, you see permission strings like this:
-rwxr-xr-- 1 alice developers 4096 Feb 25 10:30 deploy.sh
drwxrwxr-x 3 alice developers 4096 Feb 25 10:30 scripts/The first character indicates the file type: - for regular file, d for directory, l for symbolic link. The next nine characters are three groups of three:
Owner Group Others
r w x r - x r - -
│ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ └─ no execute for others
│ │ │ │ │ │ │ └─── no write for others
│ │ │ │ │ │ └───── read for others
│ │ │ │ │ └───────── execute for group
│ │ │ │ └─────────── no write for group
│ │ │ └───────────── read for group
│ │ └───────────────── execute for owner
│ └─────────────────── write for owner
└───────────────────── read for ownerOctal (Numeric) Notation
Each permission bit has a numeric value. Read = 4, Write = 2, Execute = 1. You sum these values for each category to get a single digit:
Permission Binary Octal
--------- ------ -----
rwx 111 7
rw- 110 6
r-x 101 5
r-- 100 4
-wx 011 3
-w- 010 2
--x 001 1
--- 000 0A three-digit octal number represents owner, group, and others in order. Use the Unix Permissions Calculator to convert between formats interactively.
Common Permission Values
755 — Standard for Executables and Directories
chmod 755 sets: owner can read, write, and execute; group and others can read and execute but not write. This is the default for most directories and executable scripts.
chmod 755 deploy.sh
# Owner: rwx (7) → full control
# Group: r-x (5) → read and execute
# Others: r-x (5) → read and execute644 — Standard for Regular Files
chmod 644 sets: owner can read and write; group and others can only read. This is the default for most files — HTML, CSS, images, configuration files.
chmod 644 index.html
# Owner: rw- (6) → read and write
# Group: r-- (4) → read only
# Others: r-- (4) → read only777 — Full Access (Usually Wrong)
chmod 777 gives everyone full read, write, and execute access. This is almost always a security mistake in production. If you find yourself using 777 to "fix" a permission error, stop and find the real problem — the issue is usually ownership, not permissions.
# ❌ Don't do this on a server
chmod 777 /var/www/html
# ✅ Fix ownership instead
sudo chown -R www-data:www-data /var/www/html
chmod -R 755 /var/www/html
find /var/www/html -type f -exec chmod 644 {} \;Other Common Values
600 — Owner read/write only (private files, SSH keys)
700 — Owner full access only (private directories, .ssh/)
664 — Owner/group read/write, others read (shared project files)
775 — Owner/group full, others read/execute (shared directories)
400 — Owner read only (immutable config, certificates)
444 — Everyone read only (public read-only files)The chmod Command
chmod (change mode) modifies file permissions. It supports both octal and symbolic notation.
Octal Syntax
chmod 755 script.sh # Set exact permissions
chmod -R 644 ./public/ # Recursive — apply to all files insideSymbolic Syntax
Symbolic notation lets you add or remove specific permissions without affecting others:
chmod u+x script.sh # Add execute for owner (u=user/owner)
chmod g+w file.txt # Add write for group
chmod o-rwx secret.key # Remove all permissions for others
chmod a+r readme.md # Add read for all (a=all)
chmod u=rwx,g=rx,o=r file # Set exact permissions symbolicallyThe notation follows the pattern:
chmod [who][operator][permissions] file
Who: u (owner), g (group), o (others), a (all)
Operator: + (add), - (remove), = (set exactly)
Perms: r (read), w (write), x (execute)When to Use Octal vs. Symbolic
- Octal — Best when setting all permissions at once. Clear and unambiguous. Preferred in scripts and documentation.
- Symbolic — Best when modifying one specific permission without touching others. More readable for one-off changes.
The chown and chgrp Commands
chown (change owner) and chgrp (change group) modify file ownership. Only root can change the owner of a file; regular users can only change the group to a group they belong to.
# Change owner
sudo chown alice file.txt
# Change owner and group
sudo chown alice:developers file.txt
# Change group only
chgrp developers file.txt
# or equivalently:
sudo chown :developers file.txt
# Recursive — change all files in directory
sudo chown -R www-data:www-data /var/www/htmlChecking Current Ownership
ls -la
# Output:
# -rw-r--r-- 1 alice developers 4096 Feb 25 10:30 index.html
# ^^^^^ ^^^^^^^^^^
# owner groupDirectory Permissions
Directory permissions work differently than file permissions, and this trips up many developers:
- Read (r) on a directory — Allows listing the contents (
ls). Without it, you can't see what files exist inside. - Write (w) on a directory — Allows creating, deleting, and renaming files inside. This is true even if you don't own the files themselves.
- Execute (x) on a directory — Allows entering the directory (
cd) and accessing files by name. Without it, you can't access anything inside, even if you know the filename.
Key insight: A directory needs both read and execute permissions to be useful. Read without execute lets you see filenames but not access them. Execute without read lets you access files by name but not list the directory. Most directories should be 755 or 750.
Special Permissions: Setuid, Setgid, and Sticky Bit
Beyond the basic nine permission bits, three special bits modify behavior in important ways.
Setuid (Set User ID) — Octal 4xxx
When set on an executable, the program runs with the permissions of the file's owner rather than the user running it. The classic example is /usr/bin/passwd, which needs root access to modify /etc/shadow but is run by regular users.
chmod u+s program # Set setuid
chmod 4755 program # Octal: setuid + rwxr-xr-x
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root 68208 Feb 25 passwd
# ^
# 's' instead of 'x' means setuid is setSetgid (Set Group ID) — Octal 2xxx
On an executable, it runs with the group's permissions. On a directory, new files created inside inherit the directory's group instead of the creator's primary group. This is invaluable for shared project directories:
# Create a shared project directory
sudo mkdir /opt/project
sudo chown :developers /opt/project
sudo chmod 2775 /opt/project
# Now any file created inside belongs to 'developers' group
# regardless of who creates itSticky Bit — Octal 1xxx
On a directory, the sticky bit prevents users from deleting files they don't own, even if they have write permission on the directory. The canonical example is /tmp:
ls -ld /tmp
# drwxrwxrwt 22 root root 4096 Feb 25 /tmp
# ^
# 't' instead of 'x' means sticky bit is set
chmod +t shared_dir # Set sticky bit
chmod 1777 shared_dir # Octal: sticky + rwxrwxrwxAccess Control Lists (ACLs)
Standard Unix permissions only support one owner and one group. When you need to give specific access to additional users or groups without changing ownership, use ACLs:
# Check if ACLs are supported (most modern filesystems support them)
# A '+' at the end of ls -l output indicates ACLs are set
# Grant read access to a specific user
setfacl -m u:bob:r file.txt
# Grant read/write to a specific group
setfacl -m g:testers:rw file.txt
# View ACLs
getfacl file.txt
# Output:
# # file: file.txt
# # owner: alice
# # group: developers
# user::rw-
# user:bob:r--
# group::r--
# group:testers:rw-
# mask::rw-
# other::r--
# Remove a specific ACL entry
setfacl -x u:bob file.txt
# Remove all ACLs
setfacl -b file.txtPractical Recipes
Web Server Document Root
# Set correct ownership for Nginx/Apache
sudo chown -R www-data:www-data /var/www/html
# Directories: 755 (readable, traversable)
find /var/www/html -type d -exec chmod 755 {} \;
# Files: 644 (readable)
find /var/www/html -type f -exec chmod 644 {} \;
# Scripts/CGI: 755 (executable)
find /var/www/html -name "*.sh" -exec chmod 755 {} \;SSH Key Permissions
# SSH is strict about permissions — wrong permissions = access denied
chmod 700 ~/.ssh # Directory: owner-only access
chmod 600 ~/.ssh/id_rsa # Private key: owner read/write only
chmod 644 ~/.ssh/id_rsa.pub # Public key: readable by all
chmod 600 ~/.ssh/config # SSH config: owner only
chmod 600 ~/.ssh/authorized_keys # Authorized keys: owner onlyShared Development Directory
# Create a directory where all developers can collaborate
sudo mkdir -p /opt/project
sudo groupadd devteam
sudo chown root:devteam /opt/project
sudo chmod 2775 /opt/project
# setgid ensures new files inherit the devteam group
# All members of devteam can read/write/create filesCommon Mistakes and How to Fix Them
1. Using chmod 777 to Fix Permission Errors
This is the equivalent of leaving your front door wide open because you lost your key. Fix ownership with chown instead.
2. Forgetting Execute on Directories
Setting a directory to 644 (rw-r--r--) makes it inaccessible because there's no execute bit. Directories need at least 5 (r-x) for group and others to be able to enter them.
3. Not Using Recursive Flags Correctly
# ❌ This makes ALL files executable (including .html, .css, etc.)
chmod -R 755 /var/www/html
# ✅ Set directories and files separately
find /var/www/html -type d -exec chmod 755 {} \;
find /var/www/html -type f -exec chmod 644 {} \;4. Ignoring the umask
The umask determines default permissions for newly created files. A umask of 022 means new files get 644 and new directories get 755. If your files have unexpected permissions, check the umask:
umask # Show current umask (usually 022)
umask 027 # Set umask: files get 640, directories get 7505. Forgetting That Root Bypasses Permissions
The root user can read, write, and execute any file regardless of permissions. Don't test permissions as root — switch to the intended user to verify access works correctly.
Quick Reference Table
Use Case Permissions Octal
─────────────────────────────── ─────────── ─────
Standard file rw-r--r-- 644
Executable/script rwxr-xr-x 755
Private file (SSH keys) rw------- 600
Private directory (.ssh) rwx------ 700
Web server files rw-r--r-- 644
Web server directories rwxr-xr-x 755
Shared directory (setgid) rwxrwsr-x 2775
Temporary directory (sticky) rwxrwxrwt 1777
Read-only for everyone r--r--r-- 444Linux file permissions are straightforward once you understand the model — owner, group, others, each with read, write, and execute bits. Use the Unix Permissions Calculator to quickly convert between octal and symbolic notation, and refer to the Bash Commands Cheatsheet for a quick reference of chmod, chown, and other essential commands. When in doubt, start with 644 for files and 755 for directories, then tighten as needed.