All Articles

Linux File Permissions Explained: chmod, chown, and Beyond

·18 min read

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:

For each of these three categories, three permission bits control access:

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 owner

Octal (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      0

A 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 execute

644 — 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 only

777 — 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 inside

Symbolic 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 symbolically

The 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

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/html

Checking Current Ownership

ls -la
# Output:
# -rw-r--r-- 1 alice developers 4096 Feb 25 10:30 index.html
#               ^^^^^  ^^^^^^^^^^
#               owner  group

Directory Permissions

Directory permissions work differently than file permissions, and this trips up many developers:

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 set

Setgid (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 it

Sticky 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 + rwxrwxrwx

Access 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.txt

Practical 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 only

Shared 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 files

Common 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 750

5. 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--    444

Linux 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.

Try These Tools

Related Articles

Enjoy this article? Buy us a coffee to support free tools and guides.