Linux File Permissions Explained: chmod, chown, and umask in Practice

File permissions are one of those Linux fundamentals that every user encounters early and every sysadmin revisits constantly. Yet the topic is rarely covered end-to-end in a practical way. Most guides stop at chmod 755 and call it a day.

This guide goes further. We’ll cover how permissions actually work, the symbolic and octal methods, chown for ownership, umask for defaults, special bits like setuid and sticky, and real-world scenarios where getting permissions wrong causes real problems.

How Linux File Permissions Work

Every file and directory in Linux has three permission sets assigned to three identity categories:

  • User (u): the file’s owner
  • Group (g): the owning group
  • Others (o): everyone else

Each set has three permission bits:

  • Read (r): value of 4
  • Write (w): value of 2
  • Execute (x): value of 1

Run ls -l on any file and you’ll see this in action:

-rwxr-xr--  1 hayden  www-data  4096 Jan 10 09:00 deploy.sh

Breaking that first column down:

  • -: file type (dash for regular file, d for directory, l for symlink)
  • rwx: owner can read, write, execute
  • r-x: group can read and execute, not write
  • r--: others can only read

The owner is hayden, the group is www-data. Simple enough. Now let’s look at how to change all of this.

chmod: Changing Permissions

You have two methods: symbolic and octal. Both do the same thing. Use whichever makes sense in context.

Symbolic Method

Symbolic mode lets you add, remove, or set permissions using letters. The syntax is:

chmod [who][operator][permissions] filename

Where who is u, g, o, or a (all). The operator is + (add), - (remove), or = (set exactly).

Some practical examples:

# Add execute for the owner
chmod u+x script.sh

# Remove write from group and others
chmod go-w config.yml

# Give owner full permissions, group read+execute, others nothing
chmod u=rwx,g=rx,o= deploy.sh

# Add execute for everyone
chmod a+x run.sh

Symbolic is handy when you want to make a targeted change without touching the other bits. Great for scripts where you don’t want to accidentally wipe permissions you didn’t intend to.

Octal Method

Octal mode uses numbers. Each permission set is a sum of its bits: read=4, write=2, execute=1.

Common combinations:

  • 7 = rwx (4+2+1)
  • 6 = rw- (4+2)
  • 5 = r-x (4+1)
  • 4 = r– (4)
  • 0 = — (nothing)

You write three digits: owner, group, others.

# rwxr-xr-x (common for executables and directories)
chmod 755 deploy.sh

# rw-r--r-- (common for config files)
chmod 644 nginx.conf

# rw------- (private files, only owner can read/write)
chmod 600 ~/.ssh/id_rsa

# rwx------ (scripts only the owner should run)
chmod 700 backup.sh

Octal is faster to type when you know the exact value you want. Most sysadmins default to octal for one-off commands.

Recursive chmod

The -R flag applies changes recursively to a directory and everything inside it.

chmod -R 755 /var/www/html

Be careful here. Applying 755 recursively to a directory will also give execute permission to all files, which is usually not what you want for regular files. A safer approach for web directories:

# Directories get 755, files get 644
find /var/www/html -type d -exec chmod 755 {} \;
find /var/www/html -type f -exec chmod 644 {} \;

That find approach is a habit worth building. It prevents accidentally making config files or logs executable. For more on using find effectively, see Using the find Command in Linux with Examples.

chown: Changing Ownership

Permissions only mean something in the context of ownership. chown controls who owns a file and which group it belongs to.

# Change owner only
chown hayden file.txt

# Change owner and group
chown hayden:www-data /var/www/html/index.php

# Change group only
chown :www-data /var/www/html/uploads

# Recursive ownership change
chown -R www-data:www-data /var/www/html

The -R flag works the same as with chmod. You need root or sudo to change ownership to another user.

A common real-world scenario: you upload files as your own user via SCP or rsync, then the web server (running as www-data on Debian/Ubuntu or nginx on some distros) can’t read them. The fix:

sudo chown -R www-data:www-data /var/www/mysite

Note: If you’re regularly deploying files this way, consider adding your user to the www-data group instead, and setting group write permissions on the web root. Cleaner long-term.

chgrp: Changing Group Separately

chgrp is just chown for the group alone. Some admins prefer it for clarity:

chgrp developers /opt/project
chgrp -R www-data /var/www/html/uploads

Both chown :group and chgrp group do the same thing. Pick one and be consistent.

umask: Setting Default Permissions

When you create a new file or directory, the permissions aren’t chosen randomly. They start from a base and are filtered by the umask value.

The base is:

  • 666 (rw-rw-rw-) for files
  • 777 (rwxrwxrwx) for directories

The umask subtracts from that base. Check your current umask:

umask
0022

With a umask of 022:

  • New files get 666 - 022 = 644 (rw-r–r–)
  • New directories get 777 - 022 = 755 (rwxr-xr-x)

Another common value is 027:

  • New files: 640 (rw-r—–)
  • New directories: 750 (rwxr-x—)

This is a reasonable default for servers where others should have zero access to newly created files.

To set umask for the current session:

umask 027

To make it permanent for a user, add it to ~/.bashrc or ~/.profile. For system-wide defaults, edit /etc/profile or a file in /etc/profile.d/.

Special Permission Bits

Beyond the standard rwx bits, Linux has three special bits that are often misunderstood. They matter a lot in specific contexts.

Setuid (SUID)

When set on an executable, it runs with the permissions of the file owner, not the user who launched it. The classic example is passwd:

ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 59976 Mar 14 2023 /usr/bin/passwd

That s in the owner execute position is the setuid bit. This allows regular users to change their password, which requires writing to /etc/shadow (a root-owned file). Without setuid, that would be impossible.

# Set SUID
chmod u+s /path/to/binary
# or octal: prepend 4
chmod 4755 /path/to/binary

Note: Be very careful with SUID. Setting it on the wrong binary is a serious security risk. Audit SUID files on your servers periodically with:

find / -perm /4000 -type f 2>/dev/null

Setgid (SGID)

On a file, SGID works like SUID but for the group. On a directory, it means new files created inside inherit the directory’s group rather than the creator’s primary group. This is extremely useful for shared project directories.

mkdir /opt/team-project
chown :developers /opt/team-project
chmod g+s /opt/team-project

Now any file created inside /opt/team-project will automatically belong to the developers group, regardless of which team member created it.

# Set SGID
chmod g+s /opt/team-project
# or octal: prepend 2
chmod 2775 /opt/team-project

Sticky Bit

On a directory, the sticky bit means only the file’s owner (or root) can delete or rename files inside, even if others have write permission. The classic example is /tmp:

ls -ld /tmp
drwxrwxrwt 18 root root 4096 Jan 10 10:23 /tmp

That t at the end is the sticky bit. Everyone can write to /tmp, but you can only delete your own files.

# Set sticky bit
chmod +t /shared/uploads
# or octal: prepend 1
chmod 1777 /shared/uploads

Useful for any shared upload or scratch directory where multiple users write files.

Combining Special Bits with Octal

When using octal with special bits, you add a fourth leading digit:

  • 4 = SUID
  • 2 = SGID
  • 1 = sticky
# SUID + rwxr-xr-x
chmod 4755 /usr/local/bin/mytool

# SGID + rwxrwxr-x (shared directory)
chmod 2775 /opt/shared

# Sticky + rwxrwxrwx (/tmp style)
chmod 1777 /var/scratch

ACLs: Fine-Grained Access Control

Standard Linux permissions give you three categories: owner, group, and others. That works for most cases, but sometimes you need to grant access to a second group or a specific user without changing the file’s ownership. That is where POSIX Access Control Lists (ACLs) come in.

ACLs extend the permission model by letting you assign read, write, and execute permissions to any number of additional users or groups per file. Most modern Linux filesystems (ext4, XFS, Btrfs) support ACLs out of the box. You may need to install the acl package if getfacl and setfacl are not already available:

sudo apt install acl

Grant a specific user read access to a file they would otherwise have no access to:

setfacl -m u:alice:r /var/log/myapp/app.log

Grant a group read and execute access to a directory, recursively:

setfacl -R -m g:auditors:rx /opt/reports

View the ACL entries on a file:

getfacl /opt/reports

You will see output like this:

# file: opt/reports
# owner: root
# group: root
user::rwx
group::r-x
group:auditors:r-x
mask::r-x
other::---

The mask line is the effective maximum permission for all named users and groups (excluding the owner). If you set a more permissive ACL entry, the mask can limit it. Set the mask explicitly with:

setfacl -m m::rwx /opt/reports

To set a default ACL on a directory so that new files inherit the ACL automatically (similar to how SGID inherits group ownership):

setfacl -d -m g:auditors:rx /opt/reports

Remove a specific ACL entry:

setfacl -x u:alice /var/log/myapp/app.log

Remove all ACL entries and revert to standard permissions:

setfacl -b /opt/reports

When an ACL is present, ls -l shows a + after the permission string:

drwxr-x---+ 2 root root 4096 Jan 10 10:00 /opt/reports

That + is your signal to run getfacl for the full picture.

ACLs are common in environments with multiple teams sharing directories, application log files that need to be readable by a monitoring user, and backup scripts that run as a service account. If the standard owner/group/others model is too coarse for your setup, ACLs are the next step before reaching for something heavier like SELinux or AppArmor.

Real-World Permission Scenarios

Web Server Files

A very common setup for an Nginx or Apache site:

# Directories: 755, files: 644, owner: your deploy user, group: web server user
sudo chown -R deployuser:www-data /var/www/mysite
find /var/www/mysite -type d -exec chmod 755 {} \;
find /var/www/mysite -type f -exec chmod 644 {} \;

# Writable upload directory
chmod 775 /var/www/mysite/public/uploads
chown www-data:www-data /var/www/mysite/public/uploads

SSH Key Files

SSH is strict about key file permissions. Wrong permissions will cause authentication to fail. See the SSH security guide for more on locking down your server.

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
chmod 600 ~/.ssh/authorized_keys
chmod 644 ~/.ssh/known_hosts

Shared Group Directories

# Create shared directory with SGID so group ownership is inherited
mkdir /opt/devteam
groupadd devteam
chown root:devteam /opt/devteam
chmod 2770 /opt/devteam

# Add users to the group
usermod -aG devteam alice
usermod -aG devteam bob

Now Alice and Bob can both read and write inside /opt/devteam, and all files they create will belong to the devteam group automatically.

Protecting Config Files

# Only root should read this
chmod 600 /etc/secrets.conf
chown root:root /etc/secrets.conf

# Web app config: owner and group can read, others cannot
chmod 640 /etc/myapp/database.conf
chown root:www-data /etc/myapp/database.conf

This pattern (640 with a specific group) is cleaner than 600 when an application needs to read the file but you don’t want to run the app as root. Also relevant when working with SELinux or AppArmor, which add another enforcement layer on top of standard permissions.

Checking and Auditing Permissions

A few commands worth keeping handy:

# Show permissions in long format
ls -la /path/to/dir

# Show numeric permissions
stat -c "%a %n" filename

# Find world-writable files (potential security issue)
find /var/www -perm -o+w -type f

# Find SUID binaries system-wide
find / -perm /4000 -type f 2>/dev/null

# Find files owned by a specific user
find /home -user alice -type f

# Find files with no owner (orphaned files)
find / -nouser -o -nogroup 2>/dev/null

That orphaned file check is useful after removing a user account. Files that belonged to a deleted UID show up as numeric IDs in ls -l output. Clean them up or reassign ownership.

Also see 20 Powerful Linux Administration Commands for more commands that fit into a practical sysadmin workflow.

Quick Reference: Common Permission Values

  • 400: Owner read-only. Useful for private key backups.
  • 600: Owner read/write. SSH keys, private config files.
  • 640: Owner read/write, group read. App config files.
  • 644: Owner read/write, group and others read. Web files, most configs.
  • 700: Owner full access only. Private scripts, home directories.
  • 750: Owner full, group read/execute. Group-shared scripts.
  • 755: Owner full, group and others read/execute. Directories, public scripts.
  • 775: Owner and group full, others read/execute. Shared project directories.
  • 777: Everyone full access. Almost never appropriate on a server.

Note: 777 shows up in tutorials constantly as a quick fix. It works but it is lazy and often insecure. If you find yourself using it, stop and figure out the proper ownership and group setup instead.

Conclusion

File permissions aren’t complicated once the logic clicks. The read/write/execute bits, the three identity categories, octal notation, and special bits like SGID all follow consistent rules. The skill is in applying them correctly to real situations rather than just running chmod 777 to make something work.

Get in the habit of using the find commands above to audit permissions on new deployments. It takes a few minutes and regularly turns up issues before they become problems. For the next layer of server hardening, see the guide on generating secure passwords and the notes on increasing max open files, since permission-related limits often appear together when locking down a Linux server. If you are new to Linux server management altogether, What’s Next After Installation is a good starting point.

Ready to optimize your server performance?

Get expert Linux consulting or stay updated with our latest insights.

Book a Consultation   Subscribe
Top ↑