How Linux Permissions Work
Every file and directory on a Linux system has a set of permission bits that control who can read it, write to it, or execute it. Get permissions wrong and you either lock legitimate users out or leave sensitive files open to anyone on the system. Understanding the model takes about ten minutes. Applying it correctly takes practice.
The three-part model
Linux permissions are built around three identities and three actions.
The three identities are the owner (a single user account), the group (a single group that can contain multiple users), and others (everyone else on the system). Every file and directory is owned by exactly one user and exactly one group.
The three actions are read (r), write (w), and execute (x). What these mean depends on whether you are looking at a file or a directory.
For a file: read means you can view the contents, write means you can modify or delete it, and execute means you can run it as a program.
For a directory: read means you can list the filenames inside it, write means you can create, rename, or delete files within it, and execute means you can enter the directory and access files inside it. The execute bit on a directory is often called the “search” bit and is required to do anything useful inside the directory.
Reading permissions with ls -l
The ls -l command shows permissions as a ten-character string:
ls -l /etc/passwd /etc/shadow /usr/bin/passwd
Example output:
-rw-r--r-- 1 root root 2048 Jun 28 09:00 /etc/passwd
-rw-r----- 1 root shadow 912 Jun 28 09:00 /etc/shadow
-rwsr-xr-x 1 root root 59976 Jan 15 12:00 /usr/bin/passwd
The ten characters break down as follows. The first character is the file type: - for a regular file, d for a directory, l for a symbolic link. The next nine characters are three sets of three: owner permissions, group permissions, and others permissions, each as rwx with - replacing any permission that is not granted.
So -rw-r--r-- means: regular file, owner can read and write, group can read, others can read. -rwsr-xr-x has an s in the owner execute position, which is the setuid bit (covered below).
Octal notation
Every permission set maps to a three-bit binary number, which is commonly written in octal (base 8):
| Permission | Binary | Octal |
|---|---|---|
--- | 000 | 0 |
--x | 001 | 1 |
-w- | 010 | 2 |
-wx | 011 | 3 |
r-- | 100 | 4 |
r-x | 101 | 5 |
rw- | 110 | 6 |
rwx | 111 | 7 |
A three-octal-digit number covers all three sets: owner, group, others. The most common values you will see in practice:
644(rw-r--r--): standard file. Owner reads and writes, everyone else reads. Correct for config files, web assets, scripts that are not meant to be run directly.755(rwxr-xr-x): standard executable or directory. Owner has full control, everyone else can read and execute/enter.600(rw-------): private file. Only the owner can read or write. Correct for SSH private keys (~/.ssh/id_rsa), credentials files.700(rwx------): private directory. Only the owner can enter or list contents.640(rw-r-----): owner reads and writes, group reads, others have no access. Common for application config files that a service account needs to read.
chmod: changing permissions
chmod changes the permission bits. It accepts either octal notation or symbolic notation.
# Octal: set exact permissions
chmod 644 config.ini
chmod 755 deploy.sh
chmod 600 ~/.ssh/id_ed25519
# Symbolic: add, remove, or set permissions for specific identities
chmod u+x script.sh # add execute for owner
chmod g-w shared.txt # remove write from group
chmod o= private.conf # remove all permissions from others
chmod a+r public.html # add read for all (owner, group, others)
chmod u=rwx,g=rx,o=r file # set all three explicitly
# Recursive: apply to a directory and everything inside it
chmod -R 755 /var/www/html
Be careful with recursive chmod. Running chmod -R 644 on a directory removes execute from directories inside it, which prevents you from entering them. If you need to set files to 644 and directories to 755 recursively:
# Files to 644, directories to 755
find /var/www/html -type f -exec chmod 644 {} \;
find /var/www/html -type d -exec chmod 755 {} \;
chown: changing ownership
chown changes which user and group own a file. Only root can change ownership to a different user.
# Change owner only
chown alice file.txt
# Change owner and group
chown alice:developers file.txt
# Change group only
chown :developers file.txt
# Recursive
chown -R www-data:www-data /var/www/html
chgrp changes only the group and is equivalent to chown :groupname:
chgrp developers project/
The special permission bits
Beyond the standard rwx bits, three special bits add more nuanced behaviour.
Setuid (4000)
When set on an executable, the program runs with the file owner’s permissions rather than the caller’s. The canonical example is /usr/bin/passwd: a regular user needs to change their password, but /etc/shadow is only writable by root. The setuid bit lets passwd run as root temporarily to make that change.
# The s in the owner execute position indicates setuid
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root ...
# Find all setuid binaries on the system
find / -perm -4000 -type f 2>/dev/null
Setuid on a directory has no effect on most Linux systems.
Setgid (2000)
On an executable, setgid causes the program to run with the group of the file rather than the caller’s primary group. On a directory, setgid has a different and very useful effect: new files created inside the directory automatically inherit the directory’s group rather than the creator’s primary group. This is the standard way to set up shared project directories where multiple users need consistent group ownership.
# Create a shared directory with setgid
sudo mkdir /srv/team
sudo chown :developers /srv/team
sudo chmod 2775 /srv/team
# Verify: new files created here will belong to the developers group
ls -ld /srv/team
# drwxrwsr-x 2 root developers ...
Sticky bit (1000)
On a directory, the sticky bit restricts deletion: users can only delete files they own, even if they have write permission on the directory itself. /tmp uses this so that any user can create files in /tmp, but no user can delete another user’s files.
ls -ld /tmp
# drwxrwxrwt 20 root root ...
# The t at the end is the sticky bit
# Set it on a shared upload directory
chmod +t /srv/uploads
To set special bits in octal, prepend a fourth digit: chmod 4755 (setuid), chmod 2755 (setgid), chmod 1777 (sticky).
umask: default permissions
When a new file or directory is created, Linux does not start from scratch. It starts from a default and then subtracts the umask (user file creation mask). The umask specifies which bits to remove from new files.
# Check the current umask
umask
# 0022
# See it symbolically
umask -S
# u=rwx,g=rx,o=rx
With umask 0022: new files start at 0666 (rw-rw-rw-) and have 022 subtracted, giving 0644 (rw-r—r—). New directories start at 0777 and have 022 subtracted, giving 0755 (rwxr-xr-x). These are the standard defaults.
A more restrictive umask like 0027 means group gets no write, others get nothing. Useful on multi-user systems or for applications handling sensitive data:
# Set umask for the current shell session
umask 027
# Set it permanently in ~/.bashrc or /etc/profile
echo "umask 027" >> ~/.bashrc
ACLs: permissions beyond the three-set model
Standard Linux permissions only allow one owner and one group per file. If you need different permissions for multiple users or groups, you need Access Control Lists (ACLs).
# Check if ACLs are supported on a filesystem
mount | grep acl
# Enable ACLs at mount time (in /etc/fstab, add 'acl' to options)
# Most modern filesystems enable them by default
# View ACLs on a file
getfacl /srv/project/report.pdf
# Grant a specific user read access without changing ownership
setfacl -m u:bob:r-- /srv/project/report.pdf
# Grant a group write access
setfacl -m g:auditors:r-- /srv/project/
# Set a default ACL on a directory (inherited by new files)
setfacl -d -m g:developers:rwx /srv/project/
# Remove an ACL entry
setfacl -x u:bob /srv/project/report.pdf
# Remove all ACLs
setfacl -b /srv/project/report.pdf
When a file has ACLs, ls -l shows a + at the end of the permission string. getfacl shows the full picture.
Practical permission audit
A few commands worth running periodically on any Linux system:
# World-writable files (potential security risk)
find / -xdev -perm -o+w -not -type l 2>/dev/null
# World-writable directories
find / -xdev -type d -perm -o+w 2>/dev/null
# Setuid binaries (review any that are unexpected)
find / -xdev -perm -4000 -type f 2>/dev/null
# Setgid binaries
find / -xdev -perm -2000 -type f 2>/dev/null
# Files with no owner (leftover from deleted accounts)
find / -xdev -nouser 2>/dev/null
# Files with no group
find / -xdev -nogroup 2>/dev/null
The -xdev flag prevents find from crossing filesystem boundaries, which avoids false positives from /proc and /sys and keeps the search fast.
Common mistakes
Setting 777 to fix a permission problem. It works, but it means anyone on the system can read, write, and execute the file. The right fix is to understand who needs access and give them specifically that, usually through group membership and setgid directories rather than opening everything to everyone.
Forgetting that write permission on a directory controls deletion. If a user has write permission on a directory, they can delete files inside it regardless of the permissions on those files. The sticky bit exists to address this in shared directories.
Recursive chmod on mixed file and directory trees. As noted above, chmod -R 644 breaks directories. Use find with -type f and -type d to apply different permissions to files and directories separately.
SSH key permissions. The SSH client refuses to use a private key that is too permissive. If your key is world-readable, SSH will not use it:
# Correct permissions for SSH keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 644 ~/.ssh/authorized_keys
Linux permissions are one of the first things to understand deeply when moving beyond basic usage. Most access problems on Linux trace back to a permission or ownership issue, and once the model is clear, those problems become straightforward to diagnose and fix.
Frequently Asked Questions
How do Linux file permissions work?
Every file and directory in Linux has three sets of permissions: one for the owner, one for the group, and one for everyone else. Each set can grant read (r), write (w), and execute (x) access. You can see these with ls -l, which shows permissions as a string like -rwxr-xr—. You change them with chmod and change ownership with chown.
What does chmod 755 mean?
chmod 755 sets read, write, and execute for the owner (7), and read and execute for the group and others (5 each). In binary: 7 = 111 (rwx), 5 = 101 (r-x). It is the standard permission for executable files and directories that should be publicly readable but only writable by the owner.
What is the difference between chmod and chown?
chmod changes the permission bits on a file (who can read, write, or execute it). chown changes who owns the file and which group it belongs to. Both affect access control, but they change different attributes. You typically need both: chown to assign ownership and chmod to set what that owner and others can do.
What does the execute permission mean on a directory?
On a directory, execute permission means the ability to enter the directory (cd into it) and access files inside it. Without execute permission on a directory, you cannot list or access its contents even if you have read permission. Read permission on a directory lets you list filenames; execute lets you actually use them.
What is setuid and when should I use it?
The setuid bit on an executable causes it to run with the permissions of the file owner rather than the user who launched it. The classic example is /usr/bin/passwd, which needs to write to /etc/shadow (owned by root) even when run by a regular user. Setuid should be used sparingly; unnecessary setuid binaries are a security risk.