Linux
Beginner 15 min read Linux

File Permissions & Ownership

Every file, directory and device in Linux is guarded by a permission model: read, write and execute for the owner, the group and everyone else. Learn to read ls -l output, change modes with chmod in octal and symbolic form, manage ownership with chown and chgrp, control default modes with umask, and understand the setuid, setgid and sticky bits that production systems rely on every day.

linux permissionschmodchownchgrpumasksetuidsetgidsticky bitrwxoctal permissionsls -l
Prerequisites: User Management
Introduction

What is this?

Every file, directory and device on Linux carries an owner (a user), a group, and nine permission bits arranged into three triples — read, write and execute — that apply to the owner, the group, and everyone else. The kernel enforces these bits on every open, read, write and execute system call, so a wrong mode is either a security hole or the exact reason a service suddenly refuses to start. Get this model right and you can make any file readable only by its owner, share a directory with a team, and keep an attacker out of your secrets.

Motivation

Why do you need it?

  • Keeps secrets secret: a 600-mode file is readable only by its owner, so credentials, keys and configs never leak to other users on the same host.
  • Root is one typo away from disaster: chmod -R 777 /etc can cripple a server, so disciplined permissions are a survival skill, not a nicety.
  • Shared infrastructure works without root: directories owned by a shared group let whole teams read and write the same code safely.
  • Containers and CI/CD bake permissions into images: deployments fail at startup when the runtime user cannot read config or write logs.
  • Interview fundamentals: permission checks, setuid binaries and umask are core Linux internals every SRE is expected to explain cold.
Think about it

Mental model

Imagine

A locked filing cabinet with a key for the owner, a shared key for the team, and a glass panel everyone else can look through

Think of

The rwx permission model

Each file is a cabinet. The owner holds a personal key (user bits), the owner's group shares a team key (group bits), and everyone else can only read the label (other bits). Root is the building super who opens any cabinet regardless. Linux decides which key applies by checking the caller's identity once, from most specific to least.

Mechanics

How it works

  • Every file is described by an inode that stores a numeric user id (uid), a numeric group id (gid) and a mode — a 16-bit number whose lower nine bits are the read/write/execute triples for user, group and other.
  • The kernel resolves access on every system call: if the caller's uid matches the file owner, the user triple applies; otherwise if the uid is in the file's group, the group triple applies; otherwise the other triple applies — exactly one triple is ever consulted.
  • Octal is a compact encoding: each triple is a 3-bit number where read = 4, write = 2, execute = 1, so chmod 755 means owner 7 (rwx), group 5 (r-x), other 5 (r-x). The three digits are additive, never decimal.
  • The special bits live above the nine: setuid = 4000, setgid = 2000, sticky = 1000. chmod 4755 sets setuid, which makes an executable run with the file owner's privileges — this is why unprivileged users can change their password via /usr/bin/passwd.
  • On directories the bits mean something extra: write lets you add or delete entries, execute lets you traverse into the directory, setgid makes new entries inherit the directory's group, and the sticky bit means only the owner (or root) can delete entries inside.

How the kernel decides whether a process may open a file.

Process requests access
Kernel reads the inode
Is the caller the owner?
Check the user triple
Is the caller in the group?
Check the group triple
Check the others triple
Grant or deny
Reference

Key commands

ls -l

The permission string is ten characters: a file type, then the user/group/other triples, then link count, owner, group, size, date and name.

drwxrwsr-x 2 root web 4096 Aug  8 10:00 /srv/www
-rw-r--r-- 1 root root 120 Aug  8 10:01 notes.txt
-rwsr-xr-x 1 root root 22176 Aug  8 10:02 helper
chmod 755 deploy.sh

Set rwxr-xr-x in octal: owner reads/writes/executes, group and others only read and execute.

No output on success — confirm with ls -l deploy.sh
chmod u+x,g-w,o=r deploy.sh

Symbolic mode: add execute for the owner, remove write for the group, force others to read-only.

No output on success — the mode is applied atomically.
chown deploy:web app.conf

Change owner to user deploy and group to web in one command. Only root may change a file's owner.

No output on success — verify with stat or ls -l.
chgrp -R web /srv/www

Recursively change only the group; the owner is left untouched.

No output on success — every entry under /srv/www now belongs to group web.
umask

Print the current umask — the bits subtracted from the default 666 (files) and 777 (directories).

0022
stat -c '%a %U:%G %n' notes.txt

Print mode in octal, owner, group and filename — far easier to script than parsing ls output.

644 root:root notes.txt
Hands-on

Examples

Lock down a shared deployment directory

Lock down a shared deployment directory
# 1. Put the directory under the web group
sudo chgrp web /srv/www
# 2. Owner and group get full access, everyone else gets none
sudo chmod 770 /srv/www
# 3. Set the setgid bit so new files inherit the web group
sudo chmod g+s /srv/www
# 4. Remove a stale world-readable file
sudo chmod o-r /srv/www/old-notes.txt
# 5. Verify with ls -l
ls -ld /srv/www
ls -l /srv/www

chmod 770 gives root and the web group full read/write/execute while blocking everyone else. The g+s (setgid) bit makes every new file and subdirectory inherit group web, so developers never need chown. ls -ld confirms the directory shows drwxrws--- — the s in the group position is the setgid bit.

Real world

In production

  • Web servers run as a dedicated user (www-data) that can only read the web root — a compromised app process cannot rewrite its own code or configs.
  • Setuid helpers are everywhere: /usr/bin/passwd, sudo and ping carry setuid root so unprivileged users can perform specific privileged operations without full root.
  • Shared CI artifacts use setgid directories so every pipeline job writes into the same group without per-user chown dances.
  • The sticky bit on /tmp and /run lets everyone create files but only their owner (or root) delete them, stopping denial-of-service via deletion of other users' sockets.
  • Container images set read-only mounts and per-user ownership so a compromised container process cannot modify host-visible data.
Guidelines

Best practices

  • Apply least privilege: start at 600 or 700 and widen only as far as the app genuinely needs.
  • Share with groups, never 777: chgrp a directory to the team group and chmod g+rwX instead of world-writable.
  • Verify with stat -c '%a %U:%G %n' rather than eyeballing ls -l.
  • Reserve setuid for packaged, reviewed binaries — never write your own setuid shell scripts.
  • Keep secrets at 600 and owned by a dedicated service account, never world-readable.
  • Audit for dangerous modes regularly: find / -type f -perm -0002 lists every world-writable file.
Watch out

Common mistakes

  • Reaching for chmod 777 'to make it work' — it works until the host is compromised, then it is a gift to every user.
  • Running chmod -R 777 on a whole tree, silently opening logs, sockets and configs you never thought about.
  • Forgetting that directory write permission controls creating and deleting entries: a 777 directory means anyone can delete files they don't own unless the sticky bit is set.
  • Assuming octal is decimal: 700 grants full access to the owner only, while 777 grants it to everyone.
  • Changing ownership to a name when the kernel checks numbers — a typo'd uid can quietly hand files to the wrong account.
Root cause analysis

Common issues

Error
Permission denied for a user who should have access
Root cause

The user is neither the owner nor a member of the file's group, so only the others triple applies — or an ancestor directory lacks execute (x), so the path cannot be traversed.

Fix

Run stat -c '%A %U:%G' on the file and id on the user; chgrp the file to the user's group, and ensure every directory in the path is +x.

Error
Service cannot write its log or pid file after a deploy
Root cause

The runtime user owns nothing in the directory — files were created by a different uid (often root).

Fix

chown -R the runtime user over the directory, or apply chmod g+s so new files inherit the shared group, then chmod g+rwX.

Error
New files don't belong to the shared group
Root cause

The creator's primary group differs from the intended shared group and the directory has no setgid bit.

Fix

Run chmod g+s on the directory; every new entry will inherit the directory's group from then on.

Error
sudo: /usr/bin/sudo must be owned by uid 0 and have the setuid bit set
Root cause

A botched chmod -R stripped the setuid bit from /usr/bin/sudo (its correct mode is 4755).

Fix

Boot into single-user or recovery mode and run chmod 4755 /usr/bin/sudo, then audit what else lost its special bits.

When things break

Debugging tips

  • ls -ld shows the directory's own mode; ls -l on the parent tells you whether you can actually create files inside it.
  • stat -c '%a %u %g' prints mode, uid and gid numerically — compare those against id to catch mismatch.
  • namei -l /path/to/file walks every path component and prints its permissions, revealing which ancestor blocks you.
  • Test as the real user: sudo -u alice cat /srv/app.conf instead of reasoning about the model in your head.
  • Watch for a trailing + in ls -l: that signals extended ACLs layered on top of the classic bits (getfacl shows them).
Interviews

Interview checkpoint

Practice

Hands-on lab

Master file permissions, ownership and umask
15 min · beginner
In progress
Objectives
  • Decode the 10-character permission string into rwx triples
  • Change modes with octal and symbolic chmod
  • Change owner and group with chown and chgrp
  • Control default modes with umask
  • Apply setgid to a shared directory and verify inheritance

Master file permissions, ownership and umask

15 min · beginner

0/7 steps
0%
sandbox · Linux sandbox
$
Try these commands
Test yourself

Quick quiz

0/6
1

What do the letters rwx stand for in the output of ls -l?

2

Which octal mode grants rw-r--r--?

3

On a world-writable directory without the sticky bit, any user can delete a file owned by someone else.

4

What does the s in the permission string -rwsr-xr-x indicate?

5

A CI job must let a build script (owned by root) write deployment files as root when an unprivileged runner triggers it — without giving anyone the root password. Which mechanism fits best?

6

Which command prints a file's mode as a number, its owner, its group and its name?

Quick reference

Cheat sheet

Reading ls -l
-rwxr-xr-xtype, then owner/group/others triples
d, -, l, b, c, p, sdirectory, file, symlink, block/char device, fifo, socket
1 root root 4096hard links, owner, group, size
trailing +extended ACLs active — inspect with getfacl
Octal modes (4-2-1)
4 = read2 = write, 1 = execute — sum to build a triple
755rwxr-xr-x — owner full, group/others read + execute
644rw-r--r-- — the default mode for files
600rw------- — secrets and keys
700rwx------ — private directories
777rwxrwxrwx — dangerous; everyone gets everything
Symbolic chmod
chmod u+x script.shadd execute for the owner
chmod g-w,g+r fileremove write, add read for the group
chmod a+r READMEa = all three classes at once
chmod -R g+rwX shared/X = execute only for directories, not files
Ownership
chown alice filechange owner (root only)
chown alice:web fileowner and group in one step
chown :web filechange only the group
chgrp -R web dirchange group recursively, owner untouched
Special bits & umask
chmod 4755 /usr/bin/sudosetuid — runs as the file owner
chmod 2755 run.shsetgid — runs as the group; dirs inherit group
chmod 1777 /tmpsticky — only owner/root delete entries
umask 022default files 644, default dirs 755
umask 077default files 600, default dirs 700 — private by default
Go deeper

Official documentation