Mastering Linux File Permissions: The Complete Sysadmin Guide

Master Linux file permissions, chmod, chown, ACLs, SUID/SGID, and umask, plus the research-backed reason most permission failures come from drift, not the model.

Most guides to Linux file permissions teach you the same thing: three letters, three classes, do the math, run chmod. That part is genuinely simple, and you’ll have it down in ten minutes.

Linux Permissions

1 / 10
01

Introduction

The Foundation of Linux Security

In Linux, everything is a file. Because it’s a multi-user system, Linux needs a way to prevent User A from reading, modifying, or deleting User B’s files. This is where file permissions come in.

🛡️ The Core Concept

Permissions are the first line of defense. They dictate exactly who can access what, ensuring system stability and user privacy.

02

The Actors

User, Group, & Others

Linux divides users into three categories for every file. User (the owner), Group (a collection of users), and Others (everyone else on the system).

👥 The Breakdown

If a file is owned by Alice, and belongs to the “Developers” group, Bob (who is not Alice and not in the group) falls under the “Others” category.

03

The Rules

Read, Write, Execute (rwx)

There are three types of permissions. r (Read) allows viewing content. w (Write) allows modifying. x (Execute) allows running a file (like a script).

⚙️ The Matrix

For directories, r means listing files, w means adding/removing files, and x means entering the directory (using cd).

04

Viewing

The ls -l Command

To see the permissions of files in your current directory, open your terminal and type ls -l. The first 10 characters of the output represent the file type and permissions.

👀 Example Output

-rwxr-xr–
The first dash is the file type. The next three are User, then Group, then Others.

05

Decoding

Breaking Down the String

Let’s decode -rwxr-xr--. It means: It’s a regular file (-). The User can read, write, and execute (rwx). The Group can read and execute (r-x). Others can only read (r–).

🧠 Quick Tip

If you see a ‘d’ instead of a ‘-‘ at the very beginning, it means the item is a Directory, not a file.

06

Modifying

Symbolic Mode (Letters)

The chmod command changes permissions. In symbolic mode, you use letters. To add execute permission for the user: chmod u+x file.txt.

📝 The Syntax

Use u (user), g (group), o (others), or a (all). Followed by +, -, or =, and the permission r,w,x.

07

Modifying

Numeric Mode (Octal)

The faster way to use chmod is with numbers. Read=4, Write=2, Execute=1. You add them together for each category (User, Group, Others).

🔢 Math Example

To set rwx (4+2+1=7) for User, r-x (4+0+1=5) for Group, and r– (4+0+0=4) for Others: chmod 754 file.txt

08

Ownership

The chown Command

What if you want to change who owns the file? You use chown. This is essential when moving files between users or setting up web server directories.

👑 Command Usage

To change the owner to ‘alice’ and group to ‘admin’: chown alice:admin file.txt

09

Advanced

Special Permissions

Beyond rwx, Linux has SUID, SGID, and Sticky Bit. The most common is the Sticky Bit on directories (like /tmp). It ensures that users can only delete files they own, even if everyone has write access.

⚠️ Sticky Bit Example

A ‘t’ appears in the permissions (e.g., drwxrwxrwt). Set it using chmod +t directory.

10

Best Practices

Security & Mindset

Never give 777 permissions to files or directories. It’s a massive security risk. Always grant the minimum permissions necessary for the task to function.

✅ Golden Rule

Practice using ls -l and chmod daily. Read the full DSN Daily guide to master these commands and secure your Linux system.

What those guides skip is the part that actually causes outages and breaches. A 2022 systematic review of empirical access-control research in ACM Computing Surveys found that the recurring theme across real-world incidents wasn’t a weakness in the permission model,it was misconfiguration: permissions that were never wrong on the day they were set, but became wrong as systems changed around them. The rwx model has worked for fifty years. The way humans operate it at scale is where things fall apart.

Linux file permissions misconfiguration
Linux file permissions misconfiguration

This guide covers both halves: the mechanics you need (permission strings, chmod, chown, umask, ACLs, special bits) and the operational discipline that the mechanics alone won’t give you.

Quick takeaways
  • Permissions are checked once, against the first matching class (owner, then group, then others), a restrictive owner permission is never rescued by a looser group or “others” setting.
  • On directories, r, w, and x don’t mean what they mean on files, this single mix-up causes a large share of “but the permissions look fine” tickets.
  • chmod 777 isn’t dangerous because it’s a big number, it’s dangerous because it removes the one control that limits who can tamper with a file at all.
  • umask is not subtraction. It’s a bitwise mask, and treating it as subtraction gives the wrong answer in some configurations.
  • The biggest real-world risk isn’t picking the wrong mode today, it’s nobody re-checking the mode six months from now.

What Linux File Permissions Actually Are

Every file and directory on a Linux system stores a small block of access-control metadata directly in its inode, the filesystem’s internal record for that object. That metadata defines three things: who owns the file, which group it belongs to, and what each of three actor classes is allowed to do with it.

Linux file access control metadata
Linux file access control metadata

This is Discretionary Access Control (DAC): the owner of a resource gets to decide who else can touch it, and the kernel enforces that decision on every single open(), read(), write(), and execve() call. When a process tries to act on a file, the kernel compares the process’s user ID and group IDs against the file’s owner and group, in a fixed order:

  1. Is the process’s user ID the same as the file’s owner? If yes, the owner permissions apply, and the check stops there.
  2. If not, does the process belong to the file’s group? If yes, group permissions apply, and the check stops there.
  3. Otherwise, the “others” permissions apply.

That “stops there” detail trips people up constantly. If you own a file and you’ve explicitly removed your own read access, you cannot fall back on a more generous group or “others” setting, the kernel already matched you as the owner and stopped looking. The reverse confusion happens too: people assume group membership is a fallback path, when it’s really a separate, earlier-evaluated tier that can grant access the owner permissions alone wouldn’t.

Bottom line: Permissions aren’t combined or layered, exactly one of the three classes applies to any given process, decided by ownership, not by which setting happens to be more permissive.

📚 Recommended Insight

Linux Commands Cheat Sheet: 101 Essential Commands Every Beginner Should Know (2026 Edition)

Master Linux Commands with 101 essential commands, organized by task and safety-rated. The modern, real-world cheat sheet for beginners and professionals.

Read the Full Article →

Reading the Permission String

Run ls -l on any file and the first column is a ten-character string that encodes everything you need:

Terminal
$ ls -l deploy.sh

-rwxr-xr-- 1 alice devops 2048 Jun 12 09:14 deploy.sh
Position Segment Value Meaning
1 File type - Regular file
2–4 Owner rwx alice can read, write, execute
5–7 Group r-x devops members can read and execute
8–10 Others r-- everyone else can only read

That first character is its own piece of information, the file-type flag. The common ones:

  • — a regular file
  • d — a directory
  • l — a symbolic link
  • c / b — character or block device nodes (rare outside /dev)
  • s / p — a socket or named pipe, used for inter-process communication

Files vs. Directories: Where Most Confusion Actually Lives

This is the section most tutorials rush past, and it’s the one that causes the most “why can’t I do X even though the permissions say I can” tickets. The same three letters mean structurally different things depending on what they’re attached to.

BitOn a FileOn a Directory
readOpen and view the file’s contentsList the names of entries inside (ls)
writeModify or truncate the file’s contentsCreate, delete, or rename entries inside — regardless of the permissions on those entries themselves
xecuteRun the file as a programTraverse into the directory (cd) and access metadata of entries inside

Two consequences fall out of this that are worth internalizing:

File and directory permissions
File and directory permissions
  • Deleting a file is a directory permission, not a file permission: Whether you can remove secret.txt depends on whether you have write access to the directory it lives in, not on the permissions of secret.txt itself. You can have a file locked down to 000 and still delete it outright, if you own the parent directory and it’s writable.
  • Without execute on a directory, read is nearly useless: With r– on a directory you can see the list of filenames (lsworks), but you can’t cd into it, and ls -l on the contents fails because the kernel can’t fetch their metadata without traversal rights. With –x alone, the reverse happens, you can’t list the directory’s contents, but if you already know an exact filename, you can still reach the file inside it. This is why some shared directories are deliberately set to 711: people can use what they know the path to, but can’t browse the contents.

Setting Permissions: Symbolic and Octal Notation

You can change a file’s permissions two ways. They’re equivalent, pick whichever matches how you think.

Symbolic notation uses letters and is best for incremental changes:

Bash
chmod u+x deploy.sh      # add execute for the owner only
chmod g-w config.json   # remove write from the group
chmod o=r public.html   # set "others" to read-only, explicitly
chmod a+r notes.txt     # add read for everyone (u, g, and o)

Octal notation sets the entire permission state at once, by summing point values: read = 4, write = 2, execute = 1.

Effective digit = (4 if read) + (2 if write) + (1 if execute)
Calculated separately for Owner, Group, and Others, producing one digit each.

Interactive Permission Calculator

Toggle the boxes below to build a permission set and watch the octal mode, symbolic string, and ready-to-run chmod command update.

🔧 chmod Calculator
Owner
Group
Others
OCTAL MODE
000
SYMBOLIC STRING
———-
chmod 000 filename

Common Permission Modes: What They’re Actually For

ModeSymbolicTypical UseNotes
600rw-------SSH private keys, credential files, secretsOnly the owner can do anything
640rw-r-----Config files containing secrets, readable by one groupGroup can read, never write
644rw-r--r--Static web assets, public configsStandard read-only-for-others file
700rwx------Private directories, ~/.sshOwner-only, including traversal
750rwxr-x---Group-shared directories without world accessModern PHP-FPM / suexec hosting setups use this
755rwxr-xr-xPublic directories, scripts, binariesOwner edits, everyone else can use
775rwxrwxr-xCollaborative directoriesGroup can write — requires trustworthy group membership
1777rwxrwxrwt/tmp and shared drop foldersSticky bit stops users deleting each other’s files
777rwxrwxrwxEssentially never, outside throwaway local sandboxesRemoves the only thing limiting who can modify the object

Bottom line: if you’re reaching for 777 to make an error go away, you’re treating a symptom, wrong ownership, a missing group membership, or a misunderstood directory-vs-file rule, not the cause.

Special Permissions: SUID, SGID, and the Sticky Bit

Standard rwx bits cover most cases, but three special bits handle situations the basic model can’t.

Special file bits explained
Special file bits explained

SUID (Set User ID), set with chmod u+s or a leading 4 in octal (chmod 4755). When a SUID-marked executable runs, it executes with the privileges of the file’s owner, not the user who launched it. This is how /usr/bin/passwd, owned by root,  lets an ordinary user update an entry in the root-owned /etc/shadow file without being root themselves.

It’s also one of the most reliable privilege-escalation paths on a misconfigured system. GTFOBins, the widely used community-maintained catalog of Unix binaries with exploitable functions, documents dozens of common tools, text editors, find, even some shells, that grant a root shell almost immediately if they’re ever given the SUID bit by mistake. This is exactly why security teams routinely run an audit pass looking for SUID binaries that shouldn’t have the bit set (shown later in this guide).

One detail almost no beginner guide mentions: the Linux kernel deliberately ignores the SUID bit on interpreted scripts. Running a script requires the kernel to first open the file to read its shebang line, then separately spawn the interpreter named there, and that two-step process creates a small window where the target file could be swapped out (via a symlink race) between the permission check and execution. Modern kernels close that hole simply by refusing to honor SUID on anything that isn’t a compiled binary.

SGID (Set Group ID), chmod g+s or a leading 2 (chmod 2775). On an executable, it runs with the privileges of the owning group rather than the caller’s group. On a directory, it does something more commonly useful: every new file or subdirectory created inside automatically inherits the parent’s group ownership, instead of the creating user’s default group. This is the standard way to keep a shared team directory consistently group-owned without everyone remembering to chgrp manually.

The sticky bit, chmod +t or a leading 1 (chmod 1777). On a directory, it restricts deletion: only the file’s owner, the directory’s owner, or root can remove or rename a file inside, even if the directory itself is writable by everyone. /tmp is the textbook example, every user needs to create files there, but nobody should be able to delete someone else’s.

umask: The Part Almost Every Guide Gets Slightly Wrong

New files don’t appear with permissions of 0. The kernel starts from a base mode, 666 for files, 777 for directories, and your shell’s umask strips bits from that base every time something new is created.

Umask bitwise AND explained
Umask bitwise AND explained

The common shorthand you’ll see everywhere is “umask subtracts from the default.” That’s a useful approximation, but it isn’t actually how it works, and the approximation breaks in specific cases (an executable getting created with a non-standard base mode, for instance). The real operation is a bitwise AND between the base mode and the complement of the umask:

effective mode = base mode AND (NOT umask)

Worked example, with the standard umask 022 and the directory base of 777:

Permission Calculation Example
base 777      = 111 111 111
umask 022     = 000 010 010
NOT umask     = 111 101 101
-----------------------------
result        = 111 101 101 = 755

For most everyday umask values, subtraction and the bitwise method land on the same answer, which is exactly why the shortcut stuck around. It stops matching once a base mode has a 0 in a position where the umask also has a 0, subtraction can swing negative or roll over in ways the bitwise AND simply can’t, because AND can only ever remove bits, never add them. If you’re scripting permission logic anywhere, Ansible, Terraform, a deployment hook,  use the bitwise rule, not the shortcut.

umaskNew file modeNew directory mode
022644755
027640750
077600700
002664775

Access Control Lists (ACLs): When Three Classes Aren’t Enough

POSIX ACLs for file permissions
POSIX ACLs for file permissions

The owner/group/others model has a hard limit: exactly one user and one group can be named per file. The moment you need “this one contractor gets read access, without changing the file’s group,” POSIX ACLs are the answer.

Bash
# Grant a specific user read/write, without touching ownership
setfacl -m u:jcontractor:rw project-data.csv

# View the effective ACL
getfacl project-data.csv

# Remove that specific entry again
setfacl -x u:jcontractor project-data.csv

The detail that catches people out is the ACL mask: it caps the effective permissions for every named user, named group, and the owning group, but it does not touch the file owner or the “others” class. Grant a user rwx via ACL, and if the mask is r-x, their real-world access is capped at r-x no matter what the explicit entry says. setfacl recalculates the mask automatically by default (as the union of every granted permission), which avoids most surprises,  but if you’ve manually pinned the mask with -n, an ACL entry can silently do less than it appears to.

This is a real trade-off, not a knock against ACLs: more granular control means more state to hold in your head, and a quick getfacl before debugging any “permissions look right but access fails” issue on an ACL-managed file will save you time.

Real-World Configurations

Web server roots SSH keys
Web server roots SSH keys

Web Server Roots

The official WordPress.org file-permissions documentation lays out the standard that generalizes to most PHP/web stacks: directories at 755, files at 644, and a tighter 440 or 400 for anything holding credentials, their guidance specifically calls out that no directory should ever be set to 777, including upload directories, because on shared hosting that opens every file to every other tenant’s process, and even on a dedicated VPS it removes the one barrier between “the web server can write here” and “anyone who gets a foothold can write anywhere.”

Bash
chown -R deploy:www-data /var/www/html

find /var/www/html -type d -exec chmod 755 {} +
find /var/www/html -type f -exec chmod 644 {} +

mkdir -p /var/www/html/uploads
chown deploy:www-data /var/www/html/uploads
chmod 2775 /var/www/html/uploads  # SGID keeps new uploads group-owned

SSH Keys

OpenSSH actively refuses to use a private key if it’s readable by your group or by anyone else, this isn’t a suggestion, the connection will simply fail with an “UNPROTECTED PRIVATE KEY FILE” warning until it’s fixed.

Bash
chmod 700 ~/.ssh

chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub

chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/config

Shared Team Directories

Bash
mkdir -p /opt/team-share

chown root:developers /opt/team-share

chmod 3770 /opt/team-share  # SGID + Sticky Bit

SGID keeps every new file group-owned by developers automatically; the sticky bit means a teammate can edit your file but can’t delete it out from under you.

Containers Didn’t Remove Permission Problems, They Relocated Them

Linux container UID mismatch fix
Linux container UID mismatch fix

The single most common Linux-permissions support thread in container-focused communities isn’t about chmod syntax at all. It’s a UID mismatch: an application inside a container runs as a non-root user (Node.js images commonly default to UID 1000, Postgres to UID 999), and that container mounts a host directory that’s owned by root, or by a completely different UID. The app crashes with Permission denied on startup, and the instinctive fix, chmod -R 777 on the host directory, works, but trades a 30-second crash for an open door.

The actual fix is matching identities across the boundary instead of removing the boundary:

Bash
# Run the container as the same UID/GID that owns the host directory
docker run -d -u "$(id -u):$(id -g)" -v /var/local/data:/data my-app

# Or fix ownership during the image build itself
# Dockerfile:
COPY --chown=appuser:appgroup ./src /app

Bottom line: the permission model inside a container is the same Unix model covered in this whole guide, containers just add an extra identity boundary (the host UID vs. the container UID) that has to be reconciled, not bypassed.

Why Misconfiguration Beats the Model: What the Research Actually Shows

It’s tempting to assume that stronger tools, ACLs instead of plain rwx, SELinux instead of DAC, automatically mean a more secure system. The research doesn’t fully support that.

Access control failure over time
Access control failure over time

The 2022 ACM Computing Surveys review of empirical access-control analysis found that the dominant operational risk across the literature it surveyed wasn’t an inherent weakness in any particular access-control model,  it was the accumulation of problematic permissions over time: rights granted to solve an urgent problem and never revisited, group memberships that outlived the project they were created for, and exceptions nobody owns the job of cleaning up. None of that is a flaw in rwx. It’s an organizational pattern that any access-control system, however granular, is equally exposed to.

That pattern shows up clearly when you look at why the strongest available Linux access control, SELinux,  isn’t more widely run in enforcing mode.

A 2023 comparative study presented at the European Workshop on System Security found that SELinux policies introduce measurable performance overhead and, more importantly for day-to-day operations, are difficult enough to debug that administrators frequently disable enforcement entirely rather than fix a policy that’s blocking a legitimate action, the same paper found that newer eBPF-based enforcement (built on the kernel’s KRSI hooks) offered better performance and was easier to reason about than comparable SELinux policy.

The theoretically strongest tool, in other words, is only as strong as the operational discipline behind running it.

You don’t need a Linux-specific incident to see the same governance failure play out. In 2018, security researchers at RedLock discovered that Tesla’s Kubernetes administrative console was reachable from the open internet with no authentication configured at all.

Attackers found it first, used the exposed console to deploy cryptocurrency-mining software across Tesla’s cloud infrastructure, and along the way picked up AWS credentials that had been left accessible inside the environment, reported in detail by CNBC and CyberScoop at the time. Tesla patched it within hours of disclosure and reported no customer-data impact, but the root cause wasn’t a sophisticated exploit. It was an access-control setting that was permissive by default and nobody had gone back to tighten.

The throughline across all three of these: the access-control mechanism, POSIX permissions, SELinux, a Kubernetes dashboard’s auth setting, is rarely the point of failure on the day it’s configured. It’s the point of failure months later, after nobody’s job description includes checking it again.

Common Mistakes (And the Actual Fix)

MistakeWhy It HappensReal Fix
chmod -R 777 to “fix” an errorFastest way to make a Permission Denied message disappearFind the actual broken link — usually ownership, or a missing execute bit on a parent directory
chmod -R 755 across a whole treeConvenient one-linerSplits the execute bit onto plain text files. Use find ... -type d -exec chmod 755 {} + and a separate pass for files
SSH private key left at 644Copied from a backup or another machineOpenSSH will refuse it outright — set keys to 600, the .ssh directory to 700
Assuming file permissions control deletionReasonable but wrongDeletion is governed by the parent directory’s write permission, not the file’s own mode
Forgetting the sticky bit on a shared writable directoryEasy to omit when copying a snippetWithout it, any group member can delete anyone else’s files in that directory
Treating SELinux denials as a permissions bugls -l shows the “right” modeCheck ls -Z and the audit log — SELinux can block access even when DAC permissions allow it
Container Permission denied “fixed” with host-side 777Quickest unblock under deploy pressureMatch the container’s UID to the host directory’s owning UID instead

Troubleshooting “Permission Denied”: A Systematic Path

Troubleshooting Permission Denied
Troubleshooting Permission Denied
  1. Read the exact error. “Permission denied” on open, versus on execute, versus on a directory listing, point to different bits.
  2. Check the object itself: ls -l (or ls -ld for a directory) and confirm ownership matches who you expect.
  3. Walk the entire path, not just the final file. namei -l /path/to/file shows the permissions on every directory in the chain, a missing execute bit two levels up is one of the most common causes of a confusing failure on an otherwise-correctly-permissioned file.
  4. Confirm group membership with id username, group changes made with usermod don’t apply to an already-open session; the user needs to log back in or run newgrp.
  5. Rule out a security module. If standard permissions look correct and access is still denied, check ls -Z for an SELinux context, or aa-status for AppArmor,  these can override DAC permissions entirely and won’t show up in a plain ls -l.
  6. Check the mount flags. A noexec mount will block script execution no matter what the file’s mode says.
  7. Apply the smallest fix that resolves the actual cause, not the broadest one that makes the symptom disappear.

Auditing: Finding the Permissions You Forgot About

Auditing forgotten permissions
Auditing forgotten permissions
Bash
# World-writable files — a classic forgotten-777 finder
find / -xdev -perm -0002 -type f 2>/dev/null

# SUID / SGID binaries outside the expected system set
find / -xdev -perm /6000 -type f 2>/dev/null

# Files with no valid owner (left behind by a deleted account)
find / -xdev \( -nouser -o -nogroup \) 2>/dev/null

# Snapshot current ACLs before you change anything
getfacl -R /path/to/directory > acl-backup.txt

Cross-reference anything unexpected in the SUID/SGID list against GTFOBins,  if a binary on that list shows up with the SUID bit set and it isn’t supposed to have it, treat it as an active finding, not a curiosity.

Conclusion

The mechanics in this guide, reading a permission string, picking the right octal mode, knowing when to reach for SGID or an ACL, will get you through nearly everything you’ll encounter day to day. They’re also the easy half.

The harder half, and the one backed by actual incident research rather than convention, is treating permissions as something that needs periodic re-checking, not a setting you get right once and forget. Start restrictive and loosen deliberately.

Prefer group ownership over 777 every time the urge to use it shows up. Audit on a schedule, not just when something breaks. And remember that even the strongest available access-control tooling only protects you for as long as someone keeps it tuned.

Frequently Asked Questions

What does chmod 777 actually do?

It grants read, write, and execute permissions to the owner, group, and everyone else on the system. On multi-user systems, this means any user or compromised process can modify or delete the file.

What’s the difference between chmod and chown?

chmod changes permissions (read/write/execute), while chown changes ownership (user and group). Permission issues are sometimes actually ownership problems, which chmod alone cannot fix.

Why does my script say “permission denied” even after adding read permission?

Scripts require the execute bit, not just read permission. Use chmod +x script.sh. If it still fails, check for noexec mount options or run it via interpreter: bash script.sh.

What is umask, really?

It’s a bitwise mask applied to default permissions (666 for files, 777 for directories) when new files are created. It is not simple subtraction — it operates at the bit level.

What are ACLs and when do I need them?

ACLs allow fine-grained permissions beyond owner/group/others. They are useful when a specific user needs access without changing group ownership. Check them using getfacl.

Is chmod 777 ever acceptable?

Only in isolated, non-production environments. In real systems, it introduces serious security risks and should be avoided.

My permissions look correct but I still get “permission denied” — why?

Common causes include missing execute permission on a parent directory, SELinux/AppArmor restrictions, or a noexec mount option blocking execution.

Ahmed Gadallah
Founder & Lead Writer

Ahmed Gadallah

Computer Scientist & Data Architect specializing in software engineering, advanced data analytics, and AI. As the founder of DSN Daily, he delivers data-driven insights across science, technology, and business, turning complex knowledge into actionable strategies that help readers make smarter decisions and stay ahead of emerging trends.

Was this article helpful?

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *

1

📚 Reading List

×
Image Preview