Skip to content

Phase 1 · FOUNDATIONS

Users, groups & permissions — chmod, chown, octal

Day 5 of 90 ~55 min 0/20 in phase Builds on Day 4

By the end of today

  • Read the owner, group and rwx bits from any ls -l line
  • Set file modes with chmod using octal like 644 and 755
  • Know when to fix ownership with chown versus reach for sudo

Who is allowed to touch this file

Section 1 of 5 · ~3 min

Every file and every process on a Linux box belongs to somebody. Linux tracks that with two numbers: a user ID (uid) and a group ID (gid). You saw your own name with whoami on Day 1 — behind that name is a uid. root is uid 0, the superuser who bypasses every permission check. Everyone else is a normal user who can only touch what they own or have been granted. Groups let several people share access without opening a file to the world: add three engineers to a deploy group, and a file owned by that group is reachable by all three and nobody else.

Because a normal user cannot do root’s job, Linux gives you sudo — “run this one command as root”. You do not log in as root; you borrow its power for a single line, and every sudo is logged. That audit trail is the point: on a shared server you want to know exactly who ran the dangerous command.

The permission triad

Every file carries permissions for three classes of people — the owner, the group, and other (everyone else) — and each class gets three bits: read (r), write (w), and execute (x). Read lets you see the contents, write lets you change them, and execute lets you run the file as a program. That execute bit is the one beginners forget — a script with perfect code but no x simply will not run. (It is exactly the bit week 3’s “Locked File” mission will hinge on.)

ls -l prints all nine bits at once:

How to read an ls -l permission string: the leading character is the file type, then three triads — owner, group, other — each of read, write, execute, which sum (r=4, w=2, x=1) to the octal digits 7, 5 and 5. - rwx r-x r-x type owner group other 7 5 5 r = 4 w = 2 x = 1 → add each triad rwx = 4+2+1 = 7 r-x = 4+0+1 = 5
Reading -rwxr-xr-x as 755: each triad of r/w/x sums to one octal digit.

Read it in four chunks. The first character is the type (- file, d directory). Then three triads: owner rwx, group r-x, other r-x. A dash means that bit is off.

Octal — the shorthand

Typing rwxr-xr-x gets old, so Linux lets you say the same thing in numbers. Each triad is a 3-bit value: r = 4, w = 2, x = 1, added up. So rwx = 7, r-x = 5, r-- = 4, and three triads become three digits:

  • 755 = rwxr-xr-x — owner does everything; everyone else may read and execute. The default for directories and scripts/programs (you need x on a directory to enter it).
  • 644 = rw-r--r-- — owner reads and writes; everyone else only reads. The default for ordinary files — a text file should not be executable.

Real world: Think of a file as an apartment. The owner holds the front-door key, the group is the flatmates who share a copy, and other is the whole street. r lets you read the mail, w lets you rearrange the furniture, and x is the key that actually lets you step inside and act. Handing someone your key (chmod) is different from signing the lease over to them (chown): one grants access, the other changes who owns the place.

Two commands set all of this:

  • chmod (“change mode”) sets the permission bits: chmod 755 deploy.sh, or chmod +x deploy.sh to just add the execute bit.
  • chown (“change owner”) changes who owns the file: chown pushkar:deploy app.log. Changing ownership usually needs sudo, because you cannot give away files you do not control.

A named example ties it together. Every image you build in Phase 2 bakes these bits in. The official NGINX container image ships its entrypoint script with the execute bit already set (-rwxr-xr-x) so the container starts. Strip that x — a classic slip when copying files into a Dockerfile — and the container dies on boot with permission denied. Same rule, from your laptop to a cluster.

Hands-On Lab

Section 2 of 5 · ~2 min

Budget about 25 minutes in your Ubuntu (WSL2) terminal. Type each command, then read the permission bits before and after every change — watching the rwx string move is the whole lesson.

# 1. Which user am I, and which groups do I belong to?
id
# Output (the group list varies by machine — the "sudo" group is the one that matters):
# uid=1000(pushkar) gid=1000(pushkar) groups=1000(pushkar),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users)
# 2. Make a directory and two files to experiment on, then read the bits.
mkdir perms-demo && cd perms-demo
echo 'echo "hello from pushkar"' > hello.sh
touch notes.txt
ls -l
# Output (size/date differ on your machine — read the rwx string, not the numbers):
# total 4
# -rw-r--r-- 1 pushkar pushkar 26 Jul 10 10:12 hello.sh
# -rw-r--r-- 1 pushkar pushkar  0 Jul 10 10:12 notes.txt
# 3. Try to run the script — with default umask it has NO execute bit yet.
./hello.sh
# Output:
# bash: ./hello.sh: Permission denied
# 4. Add the execute bit for everyone, then look and run.
chmod +x hello.sh
ls -l hello.sh
# Output — three x's appeared (owner, group, other): rw-r--r-- became rwxr-xr-x
# -rwxr-xr-x 1 pushkar pushkar 26 Jul 10 10:12 hello.sh
./hello.sh
# Output:
# hello from pushkar
# 5. Set an explicit mode with octal — 644 strips execute, back to a plain file.
chmod 644 hello.sh
ls -l hello.sh
# Output:
# -rw-r--r-- 1 pushkar pushkar 26 Jul 10 10:12 hello.sh
# 6. 755 = rwxr-xr-x — the standard mode for a script anyone may run, only you may edit.
chmod 755 hello.sh
ls -l hello.sh
# Output:
# -rwxr-xr-x 1 pushkar pushkar 26 Jul 10 10:12 hello.sh
# 7. Read the octal straight back with stat — no counting r/w/x by hand.
stat -c '%a %n' hello.sh
# Output:
# 755 hello.sh
# 8. A directory carries the same bits — mkdir gave 755, because you need x to ENTER it.
ls -ld .
# Output:
# drwxr-xr-x 2 pushkar pushkar 4096 Jul 10 10:12 .
# 9. Change ownership — handing a file to another user needs root, so use sudo.
sudo chown root:root notes.txt
# Output (sudo prompts for your password the first time in a session):
# [sudo] password for pushkar:
ls -l notes.txt
# Output — owner and group are now root, not you:
# -rw-r--r-- 1 root root 0 Jul 10 10:12 notes.txt
# 10. Give the file back to yourself so you own your own files again.
sudo chown pushkar:pushkar notes.txt
ls -l notes.txt
# Output:
# -rw-r--r-- 1 pushkar pushkar 0 Jul 10 10:12 notes.txt

Before you move on, read the last ls -l line back in both forms out loud — “rwxr-xr-x, which is 755” — because on a real server you will switch between the letters and the numbers in the same sentence.

Common Errors & Fixes

Section 3 of 5 · ~2 min

These are the three permission errors nearly everyone hits in their first week. Read the error text slowly — learning to parse it is the skill that outlasts any one command.

Common error: Trying to hand a file to another user with chown root:root notes.txt (no sudo) prints:

chown: changing ownership of 'notes.txt': Operation not permitted

Why: A normal user cannot change who owns a file — only root can. Linux blocks this on purpose: if anyone could give files away, they could dodge disk quotas and muddy the audit trail of who is responsible for what.

Fix: Borrow root for the one command: sudo chown root:root notes.txt. Note that chmod on your own file never needs sudo — only changing ownership does.

How you’d spot it in prod: Operation not permitted on chown in a deploy script means the script runs as a non-root user. The fix is to set ownership once (at build time, or in the step that runs as root) — not to wrap every line in sudo.

Common error: After chmod +x hello.sh, running it by bare name — hello.sh instead of ./hello.sh — prints:

hello.sh: command not found

Why: For a bare name the shell only searches the directories in $PATH, and the current directory is deliberately not on $PATH (a security choice — otherwise a stray ls script in a folder could hijack the real ls). Your script lives in ., which is never searched.

Fix: Run it with an explicit path: ./hello.sh. For a tool you use often, move it into a $PATH directory such as ~/.local/bin.

How you’d spot it in prod: command not found for your own tool in a CI log almost always means it is not on the runner’s $PATH. Reference it by absolute path, or add its directory to PATH in the pipeline — don’t assume the runner’s shell matches your laptop.

Common error: Appending to a root-owned system file as a normal user — echo "127.0.0.1 test" >> /etc/hosts — prints:

bash: /etc/hosts: Permission denied

Why: /etc/hosts is owned root:root with mode 644, so other has read only — no write. The shell opens the redirect target as you before the command runs, so it is denied immediately.

Fix: Edit it with root privileges: sudo nano /etc/hosts. Watch out — sudo echo x >> /etc/hosts still fails, because your shell (not sudo) opens the >> target; use echo x | sudo tee -a /etc/hosts instead.

How you’d spot it in prod: A config-write Permission denied means the process lacks write on a root-owned path. Fix ownership, or write to a path the service’s own user owns — never loosen the file to 666 to make the error go away.

Permissions Interview Questions

Section 4 of 5 · ~1 min

The questions below are the ones that actually come up in screens — reading ls -l, what chmod 755 means, chmod versus chown, and when to reach for sudo. Cover each answer, say your own version out loud first, then compare; recalling before revealing is what makes it stick for interview day.

Go Deeper

Section 5 of 5 · ~1 min

Optional extras if you have ~30 more minutes today:

  • 10 min — Run man chmod, then read up on umask (man umask): it is the mask that decides why new files default to 644 and new directories to 755, and you will meet it again in Dockerfiles and CI.
  • 5 min — A quick aside on special bits: setuid, setgid, and the sticky bit (that t on /tmp). You will rarely set them, but you should recognise a s or t in an ls -l line and know it is not a typo.
  • 15 min — Reread the permissions section of the Linux for DevOps guide to see the same rwx/octal model applied to real server paths and service accounts.
How do you read the permissions in an ls -l line? Both

Read it left to right in four parts. The first character is the file type — a dash for a normal file, d for a directory. Then come three groups of three: the owner's permissions, the group's, and everyone else's. Within each group the letters are always in the same order — read, write, execute — and a dash means that permission is off. So -rwxr-xr-- is a file whose owner can read, write and run it, whose group can read and run it, and where everyone else can only read. I say it out loud in those chunks; once you can, ls -l stops looking like noise.

What does chmod 755 mean? Both

Each octal digit sets one class of user — owner, group, other — and the digit is the sum of read (4), write (2) and execute (1). So 7 is 4+2+1, all three; 5 is 4+0+1, read and execute but not write. chmod 755 therefore means the owner can read, write and execute, while the group and everyone else can read and execute but not modify it. It's the standard mode for a script or program you want anyone to run but only you to change, and for directories — because you need the execute bit on a directory just to enter it.

What is the difference between chmod and chown? Both

chmod changes the permission bits — what the owner, group and others are allowed to do with a file. chown changes who the owner and group actually are. They answer different questions: chmod is 'what can be done here', chown is 'whose file is this'. A quick example: if a log file is owned by root and your app can't write to it, chmod 666 would be the wrong, insecure fix; the right fix is usually chown to the user the app runs as. Changing ownership normally needs sudo, because Linux won't let you hand files around to dodge accountability.

Why do files usually get 644 and directories 755? Both

Because the execute bit means different things for each. On a regular file, execute means 'run me as a program', and a text file, config or log should never be runnable — so 644 gives the owner read and write, everyone else read-only, and no execute anywhere. On a directory, though, the execute bit means 'you may enter this directory and reach the files inside'. Without it you can't cd in or open anything under it, even if you can see the name. So directories need 755 — read and execute for everyone, write only for the owner. Same numbers, but x flips meaning between the two.

When should you use sudo versus fixing ownership? Service

Reach for sudo when you genuinely need to act as root for one command — installing a package, editing a system config, restarting a service. But if you find yourself prefixing every command with sudo just to work with your own project's files, that's a smell: the real problem is that the files are owned by the wrong user, and the fix is a one-time chown to set ownership correctly. Sudo everywhere is how permissions rot on a shared server — nobody can tell who's allowed to do what. In production I treat a pile of sudo as a sign the ownership model needs fixing, not more privilege.

Mark Day 5 complete

A permission decides who may run a program — tomorrow you meet the running program itself: processes, and the signals that stop them.

Stuck on today’s lab? Ask in Mission 90 Q&A