Linux File Permissions Explained: chmod, chown, and ACLs

Linux File Permissions Explained: chmod, chown, and ACLs

✅ 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).

Practicing this on a live server, or need one to follow along? See our comparison of the best Linux VPS hosting providers for 2026 if you need a Linux box to work on.

Contents
  1. The Linux Permission Model
  2. Reading Permission Output
    1. Permission in Octal (Numbers)
  3. chmod: Change Permissions
    1. Numeric (Octal) Mode
    2. Symbolic Mode
    3. Common Permission Patterns
  4. chown: Change File Ownership
  5. chgrp: Change Group
  6. umask: Default Permissions
  7. Special Permissions: setuid, setgid, sticky bit
    1. setuid (SUID): Run as Owner
    2. setgid (SGID): Group Inheritance
    3. Sticky Bit: Protect Directory Contents
  8. Access Control Lists (ACLs)
  9. Common Permission Scenarios
    1. Set Up a Web Server Directory
    2. Make a Script Executable
    3. Fix Broken Permissions on Home Directory
  10. Troubleshooting Permission Errors
    1. Permission denied running a script
    2. Web server can't read files
    3. SSH key rejected: "bad permissions"
  11. Frequently Asked Questions
    1. Why is 777 a bad idea?
    2. What's the difference between 666 and 644?
    3. Can I change permissions on files I don't own?
    4. Further Reading

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/myscript
sudo chmod 755 /usr/local/bin/myscript

Fix Broken Permissions on Home Directory

# If permissions got messed up in home directory:
chmod 755 ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519
chmod 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

# Check who runs the web server:
ps aux | grep nginx | grep -v grep
# www-data  1234  ...  nginx: worker process

# Check file permissions:
ls -la /var/www/html/index.php

# Fix: web server needs at least read (r) permission, directories need x:
sudo chown www-data:www-data /var/www/html/index.php
sudo chmod 644 /var/www/html/index.php

SSH key rejected: "bad permissions"

# SSH is strict about key file permissions:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/authorized_keys
chmod 644 ~/.ssh/known_hosts
chmod 644 ~/.ssh/config

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?

Both give the owner read+write access. 666 also gives group and others write access (rw-rw-rw-): anyone can modify the file. 644 only gives group and others read access (rw-r--r--): others can read but not write. Default file creation with umask 022 gives 644.

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

No (unless you're root). Only the file owner or root can change permissions with chmod or ownership with chown. If you need access to another user's files, ask root to adjust ownership or group membership, or use ACLs.

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.


JM Carreira

JM Carreira

I’ve used Linux since 2008 and it has been my main system since 2021. When an article has a “Tested on” box, I ran the steps on my Lenovo Tiny. More about me and the test machine.

Go up

This site uses cookies for analytics and advertising (Google AdSense). By continuing to browse, you accept our use of cookies. Read our privacy policy