File Permissions๐
Part of a deep dive: Users & Access
Consult the map
-
Users & Access โ step 1 of 2
โ (first step) ยท you are here ยท Users and Groups โ
Prerequisites
This article is about managing permissions. If you need to understand the permission system (how to read rwxr-xr-x, why you get "Permission denied," or what the permission check order is), start with Understanding Your Permissions in Day One.
Inheriting a web application with broken permissions is a common first encounter: files the web server should read owned by the wrong user, a deployment script missing its execute bit, a shared directory letting users delete each other's work.
Knowing how to fix these, quickly and correctly, is a core sysadmin skill. This article is about taking action: changing permissions, adjusting ownership, controlling defaults, and understanding the special permission bits that show up in production environments.
Where You Might Have Seen This๐
If you've administered Windows, the concepts map directly onto NTFS permissions (Read, Write, Execute, Modify, Full Control), just simpler: three entities instead of a dialog full of ACL entries, and ls -l shows you the exact permissions as a readable string instead of burying them behind a Security tab.
| Windows | Linux Equivalent |
|---|---|
| Read (R) | r (read) |
| Write (W) | w (write) |
| Execute (X) | x (execute) |
| Full Control | rwx for owner |
| Users and groups | Users and groups (same concept) |
| Owner | Owner (u) |
| Everyone | Other (o) |
SUID and SGID have no clean Windows equivalent: they're Linux-specific elevation mechanisms, covered later in this article.
The Tools at a Glance๐
graph LR
A["๐ Problem:\nWrong access to a file or directory"] --> B{What needs\nto change?}
B -->|"The permissions\n(rwx)"| C["chmod\nChange file mode bits"]
B -->|"The owner\nor group"| D["chown / chgrp\nChange ownership"]
B -->|"Default permissions\nfor new files"| E["umask\nDefault permission mask"]
B -->|"Special behavior\n(SUID, SGID, sticky)"| F["chmod\nwith special bits"]
style A fill:#1a202c,stroke:#cbd5e0,stroke-width:2px,color:#fff
style C fill:#2d3748,stroke:#68d391,stroke-width:2px,color:#fff
style D fill:#2d3748,stroke:#63b3ed,stroke-width:2px,color:#fff
style E fill:#2d3748,stroke:#d69e2e,stroke-width:2px,color:#fff
style F fill:#2d3748,stroke:#fc8181,stroke-width:2px,color:#fff
Four tools, four different questions. Start with the one that comes up constantly: changing the permissions themselves.
chmod โ Changing Permissions๐
chmod (change file mode) has two notations: symbolic for surgical changes to specific bits, and octal for setting exact permissions in one step.
Symbolic Notation๐
Symbolic notation describes what to change: who, what operation, and which permissions.
| Who | Meaning |
|---|---|
u |
User (owner) |
g |
Group |
o |
Other |
a |
All (user + group + other) |
| Operator | Meaning |
|---|---|
+ |
Add permission |
- |
Remove permission |
= |
Set exactly (overwrite existing) |
chmod u+x script.sh # (1)!
chmod g-w config.conf # (2)!
chmod o= sensitive.key # (3)!
chmod a+r shared.txt # (4)!
chmod u+x,g-w,o= deploy.sh # (5)!
chmod +x deploy.sh # (6)!
- Add execute for the owner.
- Remove write from the group.
- Remove all permissions from others.
- Add read for everyone.
- Combine multiple changes in one command.
- Make a script executable โ a common pattern, equivalent to
chmod a+x.
When to use symbolic: When you want to add or remove a specific bit without disturbing the rest. chmod +x for shell scripts is one of the most common uses. See Your First Bash Script for how this fits into the scripting workflow.
Octal Notation๐
Octal assigns a number to each permission bit and sums them:
| Permission | Value |
|---|---|
Read (r) |
4 |
Write (w) |
2 |
Execute (x) |
1 |
None (-) |
0 |
Three digits cover owner, group, and other:
chmod 644 config.conf # (1)!
chmod 755 deploy.sh # (2)!
chmod 600 ~/.ssh/id_rsa # (3)!
chmod 700 ~/.ssh/ # (4)!
chmod 775 /var/www/html/ # (5)!
rw-r--r--โ owner read/write, group read, other read.rwxr-xr-xโ owner full, group and other read/execute.rw-------โ owner read/write, nobody else.rwx------โ owner full, nobody else.rwxrwxr-xโ owner and group full, other read/execute.
Calculating octal: Add the values for each permission set.
So rwxr-xr-- = 7 5 4 = 754.
When to use octal: When you want to set all permissions at once and you know exactly what you want. Common in scripts and documentation.
Common Permission Patterns๐
| Pattern | Octal | Who It's For | Typical Use |
|---|---|---|---|
rw-r--r-- |
644 | Owner rw, everyone r | Config files, web content |
rw------- |
600 | Owner only | SSH private keys, secrets |
rwxr-xr-x |
755 | Owner rwx, everyone rx | Executables, directories |
rwx------ |
700 | Owner only | Private directories |
rwxrwxr-x |
775 | Owner+group rwx, others rx | Shared team directories |
rw-rw-r-- |
664 | Owner+group rw, others r | Shared files |
Recursive chmod๐
chmod -R recursively changes permissions on a directory and all its contents:
chmod -R Can Cause Problems
chmod -R 755 sets the same permissions on both directories and files. For web content, you usually want 755 on directories (so the web server can traverse them) but 644 on files (no execute for web content). Running chmod -R 755 on files is usually a mistake.
The right approach for web directories:
find /var/www/html -type d -exec chmod 755 {} + # (1)!
find /var/www/html -type f -exec chmod 644 {} + # (2)!
- Directories get
755โ the web server needs execute to traverse them. - Files get
644โ readable, not executable.
This is the pattern you'll use repeatedly on production web servers.
chmod only ever touches the rwx bits. It has no opinion on who owns the file in the first place โ that's a separate question, and a separate command.
chown โ Changing Ownership๐
chown changes who owns a file and which group it belongs to. You'll need sudo unless you own the file.
chown jsmith file.txt # (1)!
chown jsmith:developers file.txt # (2)!
chown :developers file.txt # (3)!
chown -R www-data:www-data /var/www/html/ # (4)!
ls -la file.txt # (5)!
- Change owner only.
- Change owner and group.
- Change group only โ note the colon prefix; equivalent to
chgrp developers file.txt. - Recursive: change everything in a directory.
- Verify the change.
chgrp โ Changing Group Only๐
chgrp is a dedicated command for changing just the group:
In practice, chown :group and chgrp group are interchangeable. Most engineers use chown for both since it handles both owner and group in one command.
Common Ownership Patterns๐
chown -R www-data:www-data /var/www/myapp/ # (1)!
chown -R jsmith:jsmith /opt/myapp/ # (2)!
chown root:root /etc/nginx/nginx.conf # (3)!
ls -la /etc/nginx/ # (4)!
- Fix web server file ownership after a deploy.
- Give a developer ownership of their application directory.
- Reset ownership after copying from another system.
- Check who owns everything in a directory.
umask โ Default Permissions๐
When you create a new file or directory, what permissions does it get? The answer is umask.
umask is a subtraction mask applied to the maximum possible permissions:
- New files start at
0666(rw-rw-rw-) - New directories start at
0777(rwxrwxrwx) - The umask is subtracted from these defaults
0022is the common default. The mask is subtracted from the base permissions:0666 - 022 = 644(files becomerw-r--r--) and0777 - 022 = 755(directories becomerwxr-xr-x).
Common umask values:
| umask | File Default | Directory Default | Use Case |
|---|---|---|---|
022 |
644 | 755 | Standard โ others can read, not write |
027 |
640 | 750 | Secure โ group can read, others nothing |
077 |
600 | 700 | Very private โ owner only |
002 |
664 | 775 | Collaborative โ group can write too |
- Applies to the current shell session only.
- Make it permanent โ add it to
~/.bashrc(or~/.profile), then it loads in every new shell.
umask in Production
In many production environments, a umask of 027 is set system-wide in /etc/profile or /etc/profile.d/ for security compliance. This ensures new files are never world-readable by default. If you're creating files that the web server needs to read, you may need to explicitly chmod them after creation.
Special Permission Bits๐
Beyond rwx, Linux has three special permission bits. You'll encounter all three on production systems.
SUID โ Set User ID๐
SUID causes a file to execute as its owner, not the user who runs it. This is how non-root users can do things that require elevated privileges.
ls -la /usr/bin/passwd # (1)!
# -rwsr-xr-x 1 root root 63960 Feb 7 12:00 /usr/bin/passwd
# ^
# 's' in the owner execute position = SUID set
- Any user can run
passwdand it executes as root (because root owns it) โ that's how users change their own passwords withoutsudo.
The s in the execute position means SUID is set. If the execute bit is also set, it shows as lowercase s. If execute is not set, it shows as uppercase S (meaning SUID is set but the file isn't executable โ usually a misconfiguration).
- Symbolic.
- Octal โ the
4prefix sets SUID.
SUID is a Security Concern
Any SUID binary owned by root runs with full root privileges, regardless of who launches it. A vulnerable SUID binary can be exploited for privilege escalation. Security hardening includes auditing SUID files:
Know what's on that list. Anything unexpected deserves investigation.
SGID โ Set Group ID๐
SGID on a file causes it to execute as the file's group (similar to SUID). On a directory, it causes new files created inside to inherit the directory's group automatically โ extremely useful for shared team directories.
mkdir /opt/project # (1)!
chown root:developers /opt/project
chmod g+s /opt/project # (2)!
ls -la /opt/
# drwxrwsr-x 2 root developers 4096 Mar 10 09:00 project
# ^
# 's' in group execute position = SGID set
touch /opt/project/README.md # (3)!
ls -la /opt/project/README.md
# -rw-rw-r-- 1 jsmith developers 0 Mar 10 09:01 README.md
# ^^^^^^^^^^
# inherited from directory, not jsmith's primary group
- Create a shared project directory.
- Set SGID โ now any file created inside inherits the
developersgroup, even if the creator's primary group is different. - Create a file to prove it: the new file picks up the
developersgroup from the directory, not the creator's primary group.
- Symbolic.
- Octal โ the
2prefix sets SGID.
When to use SGID on directories: Any shared team directory where multiple users create files and all team members need to access them. Without SGID, files end up owned by whoever created them with their primary group, causing access headaches.
Sticky Bit๐
The sticky bit on a directory means that only the file's owner (or root) can delete or rename files inside it, even if other users have write permission on the directory.
ls -la /tmp # (1)!
# drwxrwxrwt 18 root root 420 Mar 10 09:35 /tmp
# ^
# 't' in other execute position = sticky bit set
/tmpis writable by everyone, but you can only delete your own files.
This is why /tmp works safely โ everyone can write temporary files, but nobody can delete someone else's work.
- Symbolic.
- Octal โ the
1prefix sets the sticky bit.
When to use sticky bit: Any directory where multiple users write files and shouldn't be able to delete each other's work. Upload directories, shared scratch space.
Common Scenarios๐
A new application was deployed but the web server can't read the files. The files are owned by the deploy user but the web server runs as www-data.
ls -la /var/www/myapp/ # (1)!
chown -R www-data:www-data /var/www/myapp/ # (2)!
find /var/www/myapp -type d -exec chmod 755 {} + # (3)!
find /var/www/myapp -type f -exec chmod 644 {} + # (4)!
chmod 755 /var/www/myapp/run.sh # (5)!
- Diagnose โ check what's there.
- Fix ownership so the web server owns the files.
- Directories traversable (
755). - Files readable (
644). - If there are executable scripts the web server needs to run.
Multiple developers need to write to /opt/project/. New files should automatically be accessible to the whole team.
mkdir /opt/project # (1)!
chown root:developers /opt/project
chmod 775 /opt/project # (2)!
chmod g+s /opt/project # (3)!
ls -la /opt/ # (4)!
# drwxrwsr-x 2 root developers 4096 Mar 10 09:00 project
- Create and own the directory.
- Team can read/write, others can read.
- Set SGID so new files inherit the
developersgroup. - Verify.
Private keys, credentials, and sensitive configuration need restrictive permissions to pass security audits.
chmod 600 ~/.ssh/id_rsa # (1)!
chmod 700 ~/.ssh/
chown appuser:appuser /etc/myapp/secrets.env # (2)!
chmod 600 /etc/myapp/secrets.env
find /etc/myapp -type f -perm /o+r 2>/dev/null # (3)!
- SSH private key โ owner read-only, nobody else.
- Application secrets โ only the app user.
- Verify there are no world-readable secrets โ any output here means a file is world-readable, so review it.
Something is behaving unexpectedly and you suspect a permissions problem. Systematic audit approach:
ls -la /path/to/target # (1)!
namei -l /path/to/target/file # (2)!
find /var/www -perm -002 -type f 2>/dev/null # (3)!
find / -perm -4000 -type f 2>/dev/null # (4)!
find /opt -nouser 2>/dev/null # (5)!
- Check the file or directory in question.
- Check every directory in the path โ
namei -lshows permissions at each level of the traverse chain. - Find world-writable files (a potential security issue).
- Find SUID files โ should be a known, short list.
- Find files with no valid owner (orphaned files).
Quick Reference๐
chmod Cheat Sheet๐
| Goal | Symbolic | Octal |
|---|---|---|
| Owner can read and write | chmod u=rw file |
chmod 600 file |
| Owner rwx, others r-x | chmod u=rwx,go=rx file |
chmod 755 file |
| Owner rw, group r, others nothing | chmod u=rw,g=r,o= file |
chmod 640 file |
| Add execute for everyone | chmod a+x file |
โ |
| Remove write from group and other | chmod go-w file |
โ |
| Directories 755, files 644 | find . -type d -exec chmod 755 {} + |
โ |
| Set SUID | chmod u+s file |
chmod 4755 file |
| Set SGID on directory | chmod g+s dir/ |
chmod 2775 dir/ |
| Set sticky bit | chmod +t dir/ |
chmod 1777 dir/ |
Special Bits in ls Output๐
| ls Shows | What It Means |
|---|---|
rws in owner position |
SUID set (file runs as owner) |
rwS in owner position |
SUID set but execute bit missing (likely a problem) |
rws in group position |
SGID set (file runs as group / dir inherits group) |
rwt in other position |
Sticky bit set (only owner can delete) |
Practice Exercises๐
Exercise 1: Octal to Symbolic
Without running any commands, translate these octal permissions to the rwx notation:
chmod 644chmod 755chmod 600chmod 4755
Solution
644โrw-r--r--(owner: rw, group: r, other: r)755โrwxr-xr-x(owner: rwx, group: rx, other: rx)600โrw-------(owner: rw, nobody else)4755โrwsr-xr-x(755 + SUID =sin owner position)
To verify: stat -c "%A" /etc/hosts shows symbolic permissions; stat -c "%a" /etc/hosts shows octal.
Exercise 2: Fix Web Server Permissions
You've deployed a web application to /var/www/myapp/. The web server runs as www-data. Files are currently owned by deploy:deploy with permissions 700.
Write the commands to fix this so:
- The web server can read all files
- The web server cannot write any files (security requirement)
- The
deployuser can still read and write
Solution
chown -R deploy:www-data /var/www/myapp/ # (1)!
find /var/www/myapp -type d -exec chmod 750 {} + # (2)!
find /var/www/myapp -type f -exec chmod 640 {} + # (3)!
- Use a shared group โ
deploystays the owner,www-databecomes the group. - Directories
750(rwxr-x---) โ deploy can write, www-data can traverse, others nothing. - Files
640(rw-r-----) โ deploy can write, www-data can read, others nothing.
This setup: deploy (owner) can write files during deployment. www-data (group) can read files during serving. Others have no access.
Exercise 3: Shared Directory Setup
Your team uses /opt/shared/ for shared project files. Files created by one developer should automatically be accessible to the whole devteam group. Set this up correctly.
Solution
chown root:devteam /opt/shared/ # (1)!
chmod 775 /opt/shared/ # (2)!
chmod g+s /opt/shared/ # (3)!
ls -ld /opt/shared/ # (4)!
# drwxrwsr-x 2 root devteam 4096 Mar 10 09:00 /opt/shared/
# ^
# 's' confirms SGID is set
- Set ownership to the
devteamgroup. 775(rwxrwxr-x) โ team members can read and write, others read and traverse.- Set SGID so new files inherit the
devteamgroup. - Verify.
Quick Recap๐
chmodโ change permissions; symbolic (u+x) for surgical changes, octal (755) for exact settingschownโ change owner and/or group:chown user:group file;-Rfor recursiveumaskโ sets default permissions for new files:022gives files 644, dirs 755- SUID (
u+s/4xxx) โ file executes as its owner; security-sensitive, audit regularly - SGID (
g+s/2xxx) โ file executes as its group; on directories, new files inherit the group - Sticky bit (
+t/1xxx) โ on directories, only owners can delete their own files - Web server pattern:
chown -R www-data:www-data, thenfind -type d -exec chmod 755,find -type f -exec chmod 644
What's Next?๐
Permissions control what files users can access. But managing which users exist and which groups they belong to is a separate skill โ and one that directly shapes the permission model across your entire system.
Head to Users and Groups to learn how to create and manage user accounts, add users to groups, and understand the /etc/passwd and /etc/group files that underpin Linux's identity system.
Further Reading๐
Command References๐
man chmodโ complete chmod documentation including special bitsman chownโ ownership change optionsman chgrpโ group change commandman umaskโ umask in the bash manualman nameiโ follow a pathname and show permissions at each levelman getfaclโ Access Control Lists (when standard permissions aren't enough)man setfaclโ set fine-grained ACL permissions
Deep Dives๐
- Red Hat: Managing File Permissions โ comprehensive Red Hat guide
- Linux File Permissions and Attributes โ Arch Wiki deep dive on permissions and ACLs
- Understanding SUID, SGID, and Sticky Bits โ Red Hat's guide to special permission bits
Official Documentation๐
- Red Hat: Managing File Ownership โ RHEL permission management
- GNU Coreutils: chmod โ authoritative chmod reference
- CIS Benchmarks โ industry security configurations including permission hardening