Permissions
rwx, octal, owners and groups. Why a script won't run even after chmod +x.
A script won't run even though you just did chmod +x. An app in a
container reports Permission denied for a file that is definitely there.
A coworker "fixed" everything with chmod -R 777. All three stories are
about permissions, and you'll run into them in your first week of working
with servers.
Where to practice
The examples below were captured in an ubuntu:24.04 container; you can
spin up the same one:
docker run --rm -it ubuntu:24.04 bash
Inside, you're root, and root bypasses read and write checks. It reads a
file with mode 000 without an error. So create a regular user first and
check permissions as that user:
useradd -m -s /bin/bash deploy
su deploy -c 'cat /etc/shadow'
cat: /etc/shadow: Permission denied
su deploy -c '<command>' runs one command as deploy. --rm removes
the container after exit.
Three categories, three permissions
Every file has an owner (user) and a group (group). Permissions are set separately for three categories:
- u (user): the file's owner;
- g (group): members of the file's group;
- o (other): everyone else.
And three kinds of access:
- r (read): read;
- w (write): change;
- x (execute): run (for a file) or enter (for a directory).
One category applies, checked top to bottom. If you're the owner, u
applies, even if the group has more permissions. Not the owner but in the
file's group means g. Otherwise o.
Reading ls -l
-rwxr-xr-- 1 deploy www-data 4096 May 29 12:00 deploy.sh
The first block, -rwxr-xr--, breaks down like this:
- rwx r-x r--
type owner group other
- the leading
-is the type:-a file,da directory,la symlink; rwx: ownerdeploycan read, write, execute;r-x: groupwww-datacan read and execute, but not change;r--: everyone else can only read.
Octal form
The same permissions can be written as a number. Each category is one digit, the sum of its bits:
r= 4,w= 2,x= 1.
Add them up within a category: rwx = 4+2+1 = 7, r-x = 4+1 = 5,
r-- = 4. So -rwxr-xr-- is 754.
Common combinations worth recognizing on sight:
| Octal | Permissions | Where it shows up |
|---|---|---|
644 | rw-r--r-- | a regular file: owner writes, everyone reads |
600 | rw------- | a private file: owner only (SSH keys) |
755 | rwxr-xr-x | a script or binary: everyone runs it, only the owner changes it |
700 | rwx------ | a private directory or script for the owner only |
777 | rwxrwxrwx | everything for everyone. Almost always a mistake, don't do this |
Who you are and what the file has
id shows your own uid/gid and groups, and stat gives the full picture
of a file:
su deploy -c id
uid=1001(deploy) gid=1001(deploy) groups=1001(deploy)
stat /usr/bin/passwd
File: /usr/bin/passwd
Size: 72056 Blocks: 144 IO Block: 4096 regular file
...
Access: (4755/-rwsr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
...
stat shows permissions both in octal (4755) and in symbolic form,
plus the owner and group. More on the leading 4 below, in "Special
bits". Without id you don't know which uid you're working under, and
so you don't know whether you fall into category u, g, or o.
A shorter form for several files at once: stat -c '%a %U %n' <files>
prints the octal mode, owner, and name on one line.
Commands
chmod 755 deploy.sh # octal form
chmod +x deploy.sh # add execute for every category
chmod u+x,go-w deploy.sh # symbolic form: targeted changes
chown deploy file # change the owner
chown deploy:www-data file # owner and group
chgrp www-data file # group only
chmod -R 755 dir # recursive: the directory and everything inside
Only root can give a file to another owner (chown). A regular user can
change their file's group, but only to a group they already belong to.
Permissions (chmod) can be changed by the file's owner or root. You
can't change a file that isn't yours:
chmod: changing permissions of '/etc/hostname': Operation not permitted
Be careful with -R. chmod -R 755 hands out x and read access to
every single file, including keys that should be 600.
Directory permissions work differently
For a directory, the bits mean something else:
r: you can list the files inside (ls);w: you can create, delete, and rename files inside;x: you can enter the directory (cd) and reach files by name.
r and x on a directory work independently. With only r (744), ls
shows the names, but cat on any file inside answers Permission denied.
With only x (711) it's the other way around: you can read a file if
you already know its name, but ls on the directory is blocked.
The catch: to read the file /a/b/file.txt, you need x on /, /a,
and /a/b, even if the file itself has the right permissions. Without
x on any directory along the way, you can't reach the content.
namei -l shows the whole path with every link's permissions:
namei -l /a/b/file.txt
f: /a/b/file.txt
drwxr-xr-x root root /
drwx------ root root a
drwxr-xr-x root root b
-rw-r--r-- root root file.txt
Here deploy can't reach the file: on /a, others don't even
have x.
Special bits
- sticky bit (a leading
1, e.g.1777on/tmp): in such a directory only the owner of a file can delete it, even if the directory is open for everyone to write to.ls -lshows it astwhere the lastxwould be:drwxrwxrwt. - SUID (
4): the executable runs with the file owner's permissions, not the permissions of whoever ran it. That's howpasswdworks (it changes/etc/shadowas root), hence thesin-rwsr-xr-x. Powerful and dangerous. - SGID (
2): the same for the group; on a directory, new files inherit the directory's group instead of the creator's primary group.
umask
New files start from base permissions 666, and directories from 777,
and the bits set in umask are subtracted from that base. A typical
umask 022 removes w for group and others: files come out as 644,
directories as 755. With umask 077, new files end up 600. The
umask command shows the current value.
Common mistakes
chmod +xdidn't help. The bit is set, but./script.shstill errors out. Three common causes.- One of the directories on the path to the script is missing
x. The error is the same,Permission denied, but the problem isn't the file. Check withnamei -l. - The filesystem is mounted with
noexec, and nothing can run from there no matter what permissions you set. Check withfindmnt -T ./script.sh: theOPTIONScolumn will shownoexec. By the way,docker run --tmpfsmounts this way by default. - The file came from Windows, and a
\ris left at the end of the shebang line (the first line,#!/bin/bash). The system looks for an interpreter named/bin/bash\rand doesn't find one. A recent bash 5.2 reportscannot execute: required file not found, older versions say/bin/bash^M: bad interpreter. You can spot the\rwithcat -A: lines will end in^M$. Fix it withdos2unix script.shorsed -i 's/\r$//' script.sh(on macOS,sed -i '' 's/\r$//' script.sh).
- One of the directories on the path to the script is missing
- Permission denied in Docker. Inside the container, the process
runs as some uid, say
1000, while the directory mounted from the host (a bind mount) belongs to a different uid. The files are there, but there's no access: permissions look at the numeric uid, not the name. Compareidinside the container withls -lnon the directory. - Checking permissions as root. root can read and write anything, so
"it works for me" proves nothing. Check as the user the service
actually runs as:
su <user> -c '<command>'. 777"to make it work". That's how permissions get "fixed" in a rush, and it's a hole: anyone can overwrite the file. It's better to grant permissions precisely to the category that needs them.
What to remember
rwx for u/g/o. In octal: r=4, w=2, x=1; recognize 755, 644,
600 on sight.
On a directory, x means permission to enter. Without it you can't
reach the files inside, and you need it on every directory along the
path.
chmod +x won't help if a directory on the path is closed off, the
filesystem is mounted with noexec, or a Windows \r is stuck in the
shebang. 777 is almost always a mistake.
In a container, permissions look at the numeric uid, which is why a bind mount can give you Permission denied.
The next lesson covers pipes and redirects: how to connect commands and redirect output.
What you'll need for the tasks
- Enter a container with
docker exec -it <name> bashand leave withexit. - Read permissions:
ls -l,stat,namei -l. - Change permissions and owner:
chmodin octal form,chown. - Run a command as another user:
su deploy -c '<command>'. - In lab 2, the "Common mistakes" section, the point about
chmod +x.
Interview-style questions
Interview questions on this topic: 3
Included with practice access, $49 one-time. Get access
Answer out loud or in writing, then reveal the answer and honestly mark whether you knew it.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words and walks you through 2 labs for this lesson. This track is free.
Tell the agent: use MCP izi, give me lesson linux-basics-06-permissions
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress