Linux File Permissions Explained: chmod, chown, and ACLs

Linux File Permissions Explained: chmod, chown, and ACLs

Common Permission Patterns

✅ Applies to all Linux distributions — Last updated: June 2026

Linux file permissions control who can read, write, and execute every file and directory on the system. Understanding permissions is essential for security, web server configuration, running scripts, and avoiding the dreaded "Permission denied" error. This guide covers everything: the permission model, chmod, chown, chgrp, special permissions, and Access Control Lists (ACLs).

Table of Contents

The Linux Permission Model

Linux permissions are based on three concepts:

  • Owner (user): The user who owns the file
  • Group: A group of users who share access
  • Others: Everyone else on the system

Each of these three can have three types of permissions:

  • Read (r = 4): View file contents; list directory contents
  • Write (w = 2): Modify file; create/delete files in directory
  • Execute (x = 1): Run as a program; enter a directory (cd into it)

Reading Permission Output

$ ls -la
-rwxr-xr-x  1 jm   jm    4096 Jun 08 10:30 script.sh
drwxr-x---  2 www-data www-data 4096 Jun 08 /var/www/html
-rw-r--r--  1 root root  2048 Jun 01 /etc/hosts
lrwxrwxrwx  1 root root     7 Apr 01 /bin -> usr/bin

Breaking down -rwxr-xr-x:

-  rwx  r-x  r-x
│   │    │    │
│   │    │    └── Others: read + execute (no write)
│   │    └─────── Group: read + execute (no write)
│   └──────────── Owner: read + write + execute
└──────────────── File type: - (regular), d (directory), l (link)

# Number fields:
# 1           = number of hard links
# jm          = owner
# jm          = group
# 4096        = file size in bytes
# Jun 08      = modification date
# script.sh   = filename

Permission in Octal (Numbers)

# Each permission set is a 3-bit number:
# r = 4, w = 2, x = 1

# rwx = 4+2+1 = 7
# rw- = 4+2+0 = 6
# r-x = 4+0+1 = 5
# r-- = 4+0+0 = 4
# -wx = 0+2+1 = 3
# -w- = 0+2+0 = 2
# --x = 0+0+1 = 1
# --- = 0+0+0 = 0

# So rwxr-xr-x = 7 5 5 = 755
# rw-r--r-- = 6 4 4 = 644
# rwx------ = 7 0 0 = 700
# rw------- = 6 0 0 = 600

chmod — Change Permissions

Numeric (Octal) Mode

chmod 644 file.txt          # rw-r--r-- (standard file)
chmod 755 script.sh         # rwxr-xr-x (executable)
chmod 700 private.sh        # rwx------ (owner only)
chmod 600 ~/.ssh/id_ed25519 # rw------- (private key)
chmod 755 /var/www/html/    # rwxr-xr-x (web directory)
chmod 777 file.txt          # rwxrwxrwx (everyone can do anything — usually bad)

# Recursive (apply to directory and all contents):
chmod -R 755 /var/www/html/
chmod -R 600 ~/.ssh/

Symbolic Mode

# Syntax: [who][operator][permissions]
# who: u (user/owner), g (group), o (others), a (all)
# operator: + (add), - (remove), = (set exactly)
# permissions: r, w, x

chmod +x script.sh          # add execute for everyone
chmod u+x script.sh         # add execute for owner only
chmod go-w file.txt         # remove write from group and others
chmod a+r file.txt          # add read for everyone
chmod u=rwx,go=rx file.txt  # set owner=rwx, group+others=rx (same as 755)
chmod g+s directory/        # add setgid bit to directory
chmod o-rwx secret.txt      # remove all permissions from others

Common Permission Patterns

PermissionOctalUse Case
rw-r--r--644Normal files (readable by all, writable by owner)
rwxr-xr-x755Scripts, directories, executables
rw-------600SSH keys, private config files
rwx------700Private scripts and directories
rw-rw-r--664Shared files (group can write)
rwxrwxr-x775Shared directories
rwxrwxrwx777Avoid — security risk

chown — Change File Ownership

# Change owner:
sudo chown jm file.txt              # change owner to jm

# Change owner and group:
sudo chown jm:jm file.txt           # owner=jm, group=jm
sudo chown www-data:www-data /var/www/html  # web server

# Change group only (keep owner):
sudo chown :www-data file.txt

# Recursive:
sudo chown -R jm:jm /home/jm/projects/
sudo chown -R www-data:www-data /var/www/html/

# Change owner to match another file:
sudo chown --reference=reference_file target_file

# Change ownership for all files in directory (using find for more control):
find /var/www -type f -exec chown www-data:www-data {} ;
find /var/www -type d -exec chmod 755 {} ;    # directories
find /var/www -type f -exec chmod 644 {} ;    # files

chgrp — Change Group

sudo chgrp www-data file.txt        # change group
sudo chgrp -R developers /opt/app/ # recursive

# View a user's groups:
groups jm
id jm

# Add user to group:
sudo usermod -aG www-data jm       # add jm to www-data group
# Log out and back in for group change to take effect

# Create a new group:
sudo groupadd developers
sudo usermod -aG developers jm

umask — Default Permissions

The umask defines what permissions are removed from newly created files and directories. Default umask is usually 022:

# View current umask:
umask
# 0022

# How umask works:
# Default file: 666 (rw-rw-rw-)
# Default dir:  777 (rwxrwxrwx)
# umask 022 removes: ----w--w-
# Result files: 644 (rw-r--r--)
# Result dirs:  755 (rwxr-xr-x)

# Change umask for current session:
umask 027    # files: 640, dirs: 750 (group can read, others nothing)
umask 077    # files: 600, dirs: 700 (owner only — maximum privacy)

# Make permanent — add to ~/.bashrc:
echo 'umask 022' >> ~/.bashrc

Special Permissions: setuid, setgid, sticky bit

setuid (SUID) — Run as Owner

When set on an executable, it runs with the permissions of the file owner (not the user running it). Used for programs that need root privileges temporarily (like passwd).

# Example: /usr/bin/passwd has setuid:
ls -la /usr/bin/passwd
# -rwsr-xr-x 1 root root 59976 ... /usr/bin/passwd
# Note the 's' in place of 'x' for owner — that's setuid

# Set setuid:
chmod u+s /usr/bin/myapp    # symbolic
chmod 4755 /usr/bin/myapp   # numeric (4 prefix = setuid)

# SECURITY WARNING: setuid programs are major security risks.
# If a setuid-root program has a vulnerability, attackers can get root.
# Never set setuid on scripts (bash ignores it for security).

setgid (SGID) — Group Inheritance

On directories: files created inside inherit the directory's group instead of the creator's primary group. Very useful for shared project directories.

# Create a shared project directory:
sudo mkdir /opt/team-project
sudo chown :developers /opt/team-project
sudo chmod 2775 /opt/team-project    # 2 prefix = setgid
# or: chmod g+s /opt/team-project

ls -la /opt/
# drwxrwsr-x  2 root developers ... team-project
# Note the 's' in group position

# Now any file created in team-project automatically belongs to developers group
# regardless of which developer creates it

Sticky Bit — Protect Directory Contents

When set on a directory, only the file owner (or root) can delete or rename files in it, even if others have write permission. Used on /tmp to prevent users from deleting each other's files.

# /tmp has sticky bit:
ls -la / | grep tmp
# drwxrwxrwt 12 root root ... tmp
# Note the 't' at the end = sticky bit

# Set sticky bit:
chmod +t /shared/uploads/     # symbolic
chmod 1777 /shared/uploads/   # numeric (1 prefix = sticky)

# Practical use: shared upload directory where users can't delete each other's files
sudo mkdir /var/shared
sudo chmod 1777 /var/shared

Access Control Lists (ACLs)

Standard Linux permissions only have owner/group/others. ACLs extend this to allow specific users or groups to have different permissions on a file.

# Install ACL tools:
sudo apt install acl -y

# View ACLs:
getfacl /var/www/html/

# Give specific user read/write access:
sudo setfacl -m u:john:rw /var/www/html/config.php

# Give specific group execute access:
sudo setfacl -m g:developers:rx /opt/deploy.sh

# Set default ACL for new files in a directory:
sudo setfacl -d -m u:john:rw /var/www/html/

# Remove ACL entry:
sudo setfacl -x u:john /var/www/html/config.php

# Remove all ACLs:
sudo setfacl -b /var/www/html/config.php

# Check if ACLs are active (look for '+' after permissions):
ls -la /var/www/html/config.php
# -rw-rw-r--+ 1 www-data www-data 1234 ... config.php
#             ^ the '+' indicates ACLs are set

Common Permission Scenarios

Set Up a Web Server Directory

# Standard web server permissions:
sudo chown -R www-data:www-data /var/www/html/
sudo find /var/www/html -type d -exec chmod 755 {} ;
sudo find /var/www/html -type f -exec chmod 644 {} ;

# Allow your user to edit web files without sudo:
sudo usermod -aG www-data $USER
sudo chmod g+w /var/www/html    # or use setgid

Make a Script Executable

chmod +x myscript.sh./myscript.sh# Make accessible system-wide:sudo cp myscript.sh /usr/local/bin/myscriptsudo chmod 755 /usr/local/bin/myscript

Fix Broken Permissions on Home Directory

# If permissions got messed up in home directory:chmod 755 ~chmod 700 ~/.sshchmod 600 ~/.ssh/authorized_keyschmod 600 ~/.ssh/id_ed25519chmod 644 ~/.ssh/id_ed25519.pub# Fix home directory ownership:sudo chown -R $USER:$USER ~

Troubleshooting Permission Errors

Permission denied running a script

# Script doesn't have execute bit:
chmod +x script.sh

# If script is on a noexec-mounted filesystem:
mount | grep noexec    # check for noexec mounts
# Run directly with bash instead:
bash script.sh

Web server can't read files

SSH key rejected: "bad permissions"

Frequently Asked Questions

Why is 777 a bad idea?

777 means any user on the system can read, write, and execute the file. On a shared server, that means any compromised web application can modify your files. For web servers, if an attacker exploits a vulnerability, they can overwrite 777 files with malicious code. Use the minimum permissions needed: 644 for files, 755 for directories.

What's the difference between 666 and 644?

Can I change permissions on files I don't own?

Understanding permissions is fundamental to Linux security and administration. Once you're comfortable with this, check out our guide on setting up SSH where permissions play a critical role in key-based authentication.


Go up