# How do Linux file permissions work? (And what are attributes?)
Source: https://vpsdime.com/knowledgebase/technical-questions/linux-file-permissions
Last reviewed: 2026-07-02
Summary: The complete model, read/write/execute for owner, group, and others, how to read ls -l output, octal notation decoded, chmod/chown/chgrp, why execute on directories means traverse, the special bits (setuid, setgid, sticky), umask, file attributes via chattr/lsattr (the immutable flag), and a note on ACLs.

## What this is

The permission model behind every `Permission denied`, every [403](https://vpsdime.com/knowledgebase/troubleshooting/403-forbidden), and every "why can't the web server read my upload". It's a small, consistent system, ten minutes here and `ls -l` output becomes legible forever.

## The model: three actions, three audiences

Every file and directory carries permissions for **three actions**, read (`r`), write (`w`), execute (`x`), granted separately to **three audiences**: the **owner** (one user), the **group** (one group of users), and **others** (everyone else). It also carries exactly one owner and one group, that's the whole state.

Reading `ls -l`:

```
-rw-r--r-- 1 www-data www-data  4096 Jul  2 10:00 index.php
drwxr-xr-x 2 deploy   deploy    4096 Jul  2 10:00 uploads
```

First character: type (`-` file, `d` directory, `l` symlink). Then three triplets, owner/group/others: `rw-` `r--` `r--` means the owner reads and writes, everyone else reads. Then the owner and group names.

## What r, w, x mean, files vs directories

- On a **file**: read the contents / change the contents / run it as a program.
- On a **directory**, the part that trips everyone: `r` = list the names inside, `w` = create/delete/rename entries inside, and **`x` = traverse**, enter it, or reach anything through it. A file you can read inside a directory you can't traverse is unreachable, which is why a `700` home directory above a web root [produces 403s with perfect file permissions](https://vpsdime.com/knowledgebase/troubleshooting/403-forbidden).

## Octal notation, decoded once

Each triplet is a number: r=4, w=2, x=1, summed. So:

- `644` = `rw-r--r--`, the standard **file**: owner edits, world reads.
- `755` = `rwxr-xr-x`, the standard **directory** (and executable): owner full, world can enter/read.
- `600`/`700`, private file/directory, [what `.env` files and `~/.ssh` want](https://vpsdime.com/knowledgebase/getting-started/setting-up-ssh-keys).
- `777` = everyone can do everything, [the "fix" that's always wrong](https://vpsdime.com/knowledgebase/troubleshooting/403-forbidden): on a server it means any compromised process can rewrite your code.

## Changing things: chmod, chown, chgrp

```
chmod 644 file.php                 # set exact permissions (octal)
chmod u+x deploy.sh                # symbolic: add execute for the owner
chmod -R g+w /var/www/shared       # recursive, careful with -R and x!
chown www-data:www-data -R /var/www/site    # owner and group together
```

One recursion trap: `chmod -R 644` strips the `x` from directories and breaks traversal. The idiom that sets files and directories correctly in one pass is [in the 403 guide](https://vpsdime.com/knowledgebase/troubleshooting/403-forbidden) (`find -type d` → 755, `find -type f` → 644).

## The special bits: setuid, setgid, sticky

A fourth, leading octal digit covers three special cases you'll mostly *read* rather than set:

- **setuid (4xxx, `s` in the owner triplet)**: the program runs as its owner regardless of who launches it, how `passwd` edits a root-owned file. On your own files, almost never; a stray setuid-root binary is a [compromise artifact](https://vpsdime.com/knowledgebase/troubleshooting/compromised-vps-recovery) worth noticing.
- **setgid (2xxx)**: on a **directory**, new files inherit the directory's *group* instead of the creator's, genuinely useful for shared web roots (`chmod g+s /var/www/shared`).
- **sticky (1xxx, `t` on the end)**: in a world-writable directory, only a file's owner may delete it, why `/tmp` is `1777` and everyone's temp files survive each other.

## umask: where default permissions come from

New files don't appear as 666/777, the **umask** subtracts bits at creation. The usual `022` yields 644 files and 755 directories, which is why fresh files are usually right without thought. If a service creates files your web server can't read, its umask (settable per systemd unit with `UMask=`) is the suspect.

## Attributes: the layer beneath permissions

Separate from permissions, ext4 supports **file attributes**, flags on the file itself, managed with `chattr` and listed with `lsattr`. The two that matter in practice:

- **`i` (immutable)**: `chattr +i file`, the file can't be modified, deleted, or renamed **by anyone, root included**, until `chattr -i`. Legitimate for lock-down-this-config; also a known [malware persistence trick](https://vpsdime.com/knowledgebase/troubleshooting/compromised-vps-recovery), when a file refuses deletion as root, `lsattr` it.
- **`a` (append-only)**: writes can only add, never rewrite, occasionally used to protect logs.

If a permission mystery survives everything above, check `lsattr` before questioning reality.

## A note on ACLs

When one-owner-one-group isn't expressive enough ("these *two* users, plus the web server, but nobody else"), POSIX **ACLs** add per-user/per-group entries on top: `setfacl -m u:deploy:rw file`, inspect with `getfacl`, and a `+` at the end of `ls -l`'s permission block is the tell that ACLs are in play. Most single-admin servers never need them; knowing the `+` exists prevents one specific afternoon of confusion.

## Still need help?

You can [open a support ticket](https://vpsdime.com/open-ticket/). So we can help on the first reply, it's worth mentioning:

- the VPS hostname or IP,
- the `ls -l` line for the file or folder, and who needs which access.

## Related questions

- "What do 644, 755, and 777 actually mean?"
- "Why does execute on a directory matter?"
- "What are setuid, setgid, and the sticky bit?"
- "Why can't root delete this file (chattr immutable)?"
- "What does the + at the end of ls -l permissions mean?"
