Linux for the cloud · Lesson 5 of 14 · about 30 minutes

Permissions and users

Everything so far has run as root, the one user who can do anything. This lesson sets the bookshop up properly: its settings in /etc, its logs in /var/log, a bookshop user to run the app, and a person to look after it. Along the way: what rwxr-x--- really means, chmod by letters and by numbers, chown, why a folder needs x, and why chmod 777 is never the fix.

By the end you'll know

  • Who you are on a Linux machine, and where users and groups are kept
  • What every letter of rwxr-x--- means, for files and for folders
  • How to change permissions with chmod, by letters and by numbers
  • How to change who owns a file with chown
  • How to run a command as another user, and why servers don't run apps as root

The problem: everyone is root

So far you've read what the bookshop's server left behind. From this lesson on, you'll build it, one piece at a time, and the practice files are what it will produce.

Start with a question you haven't had to ask yet: who is running everything? So far, the answer has been root, the administrator, who can read, change and delete anything on the machine. That's convenient for learning and dangerous for a server. If an app running as root has a bug, or someone breaks into it, they can do anything root can: read every password, change every setting, delete everything.

The fix is old and simple. Every program runs as a user with only the access it needs, and permissions on every file and folder say who can do what. This lesson sets that up for the bookshop.

On your own computer, start in your practice folder as usual:

BASH
cd linux-practice && docker run -it --rm --name lesson5 -v "$PWD":/practice -w /practice ubuntu:24.04 bash

&& runs the second command only if the first one worked, so if you're not in the folder that holds linux-practice, it stops at no such file or directory instead of starting a container with the wrong files. (Lesson 2 cloned it into the folder you were in at the time, such as ~/Apps.)

Everything else runs inside the container. Nothing in this lesson changes the practice files: you'll copy what you need into the container's own folders, and it all disappears when you exit.

Who am I?

BASH
whoami
id
OUTPUT
root
uid=0(root) gid=0(root) groups=0(root)

Linux knows users by number. uid is your user ID: root is always 0. gid is your main group, and groups lists every group you're in. A group is a named set of users, so you can give several people the same access at once.

Users are listed in /etc/passwd, one per line, with fields separated by :. Lesson 4's awk -F reads it:

BASH
grep -E '^(root|nobody):' /etc/passwd
awk -F: '{print $1, $3, $7}' /etc/passwd | tail -3
OUTPUT
root:x:0:0:root:/root:/bin/bash
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt 42 /usr/sbin/nologin
nobody 65534 /usr/sbin/nologin
ubuntu 1000 /bin/bash

The seven fields are the name, x (the password lives elsewhere, in /etc/shadow), the user ID, the group ID, a description, the home folder, and the shell. Most of these users aren't people: they're system users, made for one program each, with the shell /usr/sbin/nologin so nobody can log in as them. nobody is the classic one, a user with no access to anything, which you'll use later as a stranger. The last line is the one person: ubuntu, user 1000, with a real shell. The Ubuntu image includes it, as Ubuntu cloud servers do. Groups are listed the same way, in /etc/group.

Reading permissions

Every ls -l line starts with ten characters. You've seen them since Lesson 2:

BASH
ls -l notes/todo.txt
ls -ld logs
OUTPUT
-rw------- 1 root root 127 Oct 29 12:30 notes/todo.txt
drwx------ 6 root root 192 Oct 29 12:39 logs

(Your dates will differ.) The first character is the type: - a file, d a folder. The other nine are three sets of three: what the owner can do, what the group can do, and what everyone else can do. After the number, root root names the owner and the group.

LetterNumberOn a fileOn a folder
r read4Read itList the names inside
w write2Change itAdd, rename and delete files inside
x execute1Run it as a programGo into it, and open the files inside
-0Not allowedNot allowed

So -rw------- means the owner can read and write, and nobody else can do anything. These files came from your computer through the -v link, which is why they look so locked down.

The numbers. Add up each set of three: rw- is 4 + 2 = 6, r-- is 4, --- is 0. So rw-r----- is 640, and rwxr-xr-x is 755. Three digits, one each for owner, group and everyone else. That's the form you'll see most in instructions and scripts. Try it:

Try it: permissions, three ways

for a
owner
group
others
common
ls -l shows-rw-r-----
as a number640
to set itchmod 640 FILE
  • The owner can read it and change it.
  • The group can read it.
  • Everyone else can't do anything with it.

640 is common for settings the app may read.

Two surprises are worth knowing now. Deleting a file depends on the folder's w, not the file's: a file you can't change, in a folder you can write to, can still be deleted. And a folder needs x to be any use at all, which "Break it on purpose" shows.

Set up the bookshop

A Linux server keeps things in standard places (Lesson 2): settings in /etc, logs in /var/log. Make a folder in each for the app, and copy in its settings and its log:

BASH
mkdir -p /etc/books-api /var/log/books-api
cp config/app/app.conf config/app/override.conf /etc/books-api/
cp logs/app.log /var/log/books-api/
ls -l /etc/books-api /var/log/books-api
OUTPUT
/etc/books-api:
total 8
-rw------- 1 root root 151 Oct 29 19:12 app.conf
-rw------- 1 root root  75 Oct 29 19:12 override.conf

/var/log/books-api:
total 36
-rw------- 1 root root 33063 Oct 29 19:12 app.log

The copies kept the originals' permissions, -rw-------, and belong to root, because root made them.

Now two users. The app gets a system user that can't log in, and Sam, who the notes say is on call this week, gets a normal account with a home folder:

BASH
useradd --system --shell /usr/sbin/nologin bookshop
useradd --create-home --shell /bin/bash sam
id bookshop
id sam
ls -ld /home/sam
OUTPUT
uid=999(bookshop) gid=999(bookshop) groups=999(bookshop)
uid=1001(sam) gid=1001(sam) groups=1001(sam)
drwxr-x--- 2 sam sam 4096 Oct 29 19:12 /home/sam

useradd made a group with the same name for each, and gave them the next free numbers: system users get numbers below 1000, people get 1000 and up. ubuntu already has 1000, so Sam got 1001. Sam's home folder is drwxr-x---, so other users can't look inside it.

Act as another user: su

su (switch user) runs a command as someone else. As root, you don't need their password. bookshop has no real shell, so name one with -s for the test:

BASH
su -s /bin/bash bookshop -c 'cat /etc/books-api/app.conf'
su -s /bin/bash bookshop -c 'echo "2026-10-29 09:00:00 INFO  started" >> /var/log/books-api/app.log'
OUTPUT
cat: /etc/books-api/app.conf: Permission denied
bash: line 1: /var/log/books-api/app.log: Permission denied

Exactly what you'd expect from -rw------- root root: the app can't read its own settings or write its own log. This is the most common reason a freshly installed service won't start.

Change the owner: chown

chown (change owner) takes user:group. The app should own its logs, so it can write them. Its settings should stay owned by root, so the app can't change them, but belong to the bookshop group, so it can read them:

BASH
chown -R bookshop:bookshop /var/log/books-api
chown root:bookshop /etc/books-api/app.conf /etc/books-api/override.conf
chmod 640 /etc/books-api/app.conf /etc/books-api/override.conf /var/log/books-api/app.log
ls -l /etc/books-api /var/log/books-api
OUTPUT
/etc/books-api:
total 8
-rw-r----- 1 root bookshop 151 Oct 29 19:12 app.conf
-rw-r----- 1 root bookshop  75 Oct 29 19:12 override.conf

/var/log/books-api:
total 36
-rw-r----- 1 bookshop bookshop 33063 Oct 29 19:12 app.log

-R (recursive) changes the folder and everything in it. chmod 640 gives the group read access: changing the owner alone isn't enough, because the copies' group permissions were ---. Try again:

BASH
su -s /bin/bash bookshop -c 'head -2 /etc/books-api/app.conf'
su -s /bin/bash bookshop -c 'echo "2026-10-29 09:00:00 INFO  started" >> /var/log/books-api/app.log'
tail -1 /var/log/books-api/app.log
su -s /bin/bash bookshop -c 'echo "port = 1" >> /etc/books-api/app.conf'
OUTPUT
# Settings for the practice app
name = books-api
2026-10-29 09:00:00 INFO  started
bash: line 1: /etc/books-api/app.conf: Permission denied

The app can read its settings and write its log, and can't change its settings. If someone breaks into the app, they can't rewrite its configuration either. That's the goal: the access it needs, and nothing more. It's called least privilege.

Groups: giving a person access

Sam needs to read the app's log while on call:

BASH
su - sam -c 'tail -1 /var/log/books-api/app.log'
usermod -aG bookshop sam
id sam
su - sam -c 'tail -1 /var/log/books-api/app.log'
su - sam -c 'echo "a note from sam" >> /var/log/books-api/app.log'
OUTPUT
tail: cannot open '/var/log/books-api/app.log' for reading: Permission denied
uid=1001(sam) gid=1001(sam) groups=1001(sam),999(bookshop)
2026-10-29 09:00:00 INFO  started
-bash: line 1: /var/log/books-api/app.log: Permission denied

usermod -aG bookshop sam adds Sam to the Group. Leave out -a and -G replaces all of Sam's groups instead, a classic mistake. Now Sam can read the log, through the group's r, but not change it, because the group has no w. The - in su - sam starts a full login as Sam, in Sam's home folder, the way Sam would really log in.

On a real server, a user added to a group has to log out and back in before it takes effect. su starts a fresh login each time, so here it's immediate.

Folders: who can get in

Lock the log folder so only the owner and the group can enter it, and test with nobody, a user in neither:

BASH
chmod 750 /var/log/books-api
ls -ld /var/log/books-api
su - sam -c 'ls /var/log/books-api'
su -s /bin/bash nobody -c 'ls /var/log/books-api'
OUTPUT
drwxr-x--- 2 bookshop bookshop 4096 Oct 29 19:12 /var/log/books-api
app.log
ls: cannot open directory '/var/log/books-api': Permission denied

750 is rwxr-x---: the owner can do everything, the group can look and read, and everyone else can't get in. Even a file that everyone could read is safe inside a folder they can't enter.

Making a file runnable: chmod +x

A program is just a file with x set. Make a one-line report with Lesson 4's pipeline. The first line, #!/bin/bash, says which program runs the file; Lesson 12 covers scripts properly:

BASH
echo '#!/bin/bash' > /usr/local/bin/books-report
echo "awk '{print \$3}' /var/log/books-api/app.log | sort | uniq -c | sort -rn" >> /usr/local/bin/books-report
ls -l /usr/local/bin/books-report
books-report
OUTPUT
-rw-r--r-- 1 root root 84 Oct 29 19:12 /usr/local/bin/books-report
bash: /usr/local/bin/books-report: Permission denied

New files get rw-r--r-- (644): anyone can read them, nobody can run them. Add x:

BASH
chmod +x /usr/local/bin/books-report
ls -l /usr/local/bin/books-report
books-report
OUTPUT
-rwxr-xr-x 1 root root 84 Oct 29 19:12 /usr/local/bin/books-report
    449 INFO
    153 WARN
     37 ERROR

The \$3 in the echo stops the shell from swapping $3 for something else before writing the file. And /usr/local/bin is where programs you add yourself go. It's on the list of folders the shell searches (PATH, from Lesson 1), which is why books-report works without its full path.

chmod by letters

Numbers set all nine at once. Letters change just what you name: u the owner (user), g the group, o others, a all three, then + to add, - to remove, or = to set exactly:

BASH
touch /tmp/demo.txt
ls -l /tmp/demo.txt
chmod u+x /tmp/demo.txt
ls -l /tmp/demo.txt
chmod g+w,o-r /tmp/demo.txt
ls -l /tmp/demo.txt
chmod 600 /tmp/demo.txt
ls -l /tmp/demo.txt
OUTPUT
-rw-r--r-- 1 root root 0 Oct 29 19:12 /tmp/demo.txt
-rwxr--r-- 1 root root 0 Oct 29 19:12 /tmp/demo.txt
-rwxrw---- 1 root root 0 Oct 29 19:12 /tmp/demo.txt
-rw------- 1 root root 0 Oct 29 19:12 /tmp/demo.txt

Use letters to change one thing, like chmod +x script or chmod o-r secrets.conf. Use numbers when you know exactly what the result should be, like chmod 640 app.conf.

The special cases from Lesson 2

Lesson 2 pointed at three odd-looking permissions. Here they are, explained:

BASH
ls -ld /root /tmp
ls -l /etc/shadow /usr/bin/passwd
umask
OUTPUT
drwx------ 2 root root 4096 Oct 29 19:12 /root
drwxrwxrwt 2 root root 4096 Oct 29 19:12 /tmp
-rw-r----- 1 root shadow   522 Oct 29 19:12 /etc/shadow
-rwsr-xr-x 1 root root   64152 Apr  9  2024 /usr/bin/passwd
0022
  • /root is 700. Only root can get into root's home folder.
  • /tmp ends in t, the sticky bit. Everyone can write in /tmp, but the t means you can only delete your own files there. Without it, anyone could delete anyone else's.
  • /etc/shadow holds the password hashes, readable only by root and the shadow group. That's why /etc/passwd, which everyone can read, just says x.
  • passwd has an s where the owner's x would be: setuid. It runs as its owner, root, whoever starts it. That's how an ordinary user can change their own password, which means writing to /etc/shadow. A setuid program has to be very careful, which is why there are only a few of them.
  • umask is 0022: the permissions taken away from every new file. Files start from 666 and folders from 777, minus 022, which is why new files come out 644 and new folders 755.

See the sticky bit work. Sam makes a file in /tmp, and the app tries to delete it:

BASH
su - sam -c 'echo "hello" > /tmp/sams-file.txt'
ls -l /tmp/sams-file.txt
su -s /bin/bash bookshop -c 'rm /tmp/sams-file.txt'
OUTPUT
-rw-rw-r-- 1 sam sam 6 Oct 29 19:12 /tmp/sams-file.txt
rm: cannot remove '/tmp/sams-file.txt': Operation not permitted

Break it on purpose

chmod 777, the fix that isn't. When something says Permission denied, searches often suggest chmod 777. It does make the error go away:

BASH
chmod 777 /etc/books-api/app.conf
ls -l /etc/books-api/app.conf
su -s /bin/bash nobody -c 'echo "port = 1" >> /etc/books-api/app.conf'
tail -1 /etc/books-api/app.conf
OUTPUT
-rwxrwxrwx 1 root bookshop 151 Oct 29 19:12 /etc/books-api/app.conf
port = 1

nobody, a user who should have no access to anything, just changed the app's port. 777 means every user on the machine can read, change and run the file. The real fix is always the narrowest one: work out which user needs which access, and give that, with chown or a group. Put it back:

BASH
sed -i '$d' /etc/books-api/app.conf
chmod 640 /etc/books-api/app.conf
ls -l /etc/books-api/app.conf
OUTPUT
-rw-r----- 1 root bookshop 151 Oct 29 19:12 /etc/books-api/app.conf

sed -i '$d' deletes the last line (Lesson 4). $ means the last line.

Run it once as root. Someone tests the app by hand, as root, and it writes a new log file. Then the real app takes over:

BASH
echo "2026-10-29 10:00:00 DEBUG run by hand" >> /var/log/books-api/debug.log
ls -l /var/log/books-api
su -s /bin/bash bookshop -c 'echo "2026-10-29 10:05:00 DEBUG from the app" >> /var/log/books-api/debug.log'
chown bookshop:bookshop /var/log/books-api/debug.log
OUTPUT
total 40
-rw-r----- 1 bookshop bookshop 33097 Oct 29 19:12 app.log
-rw-r--r-- 1 root     root        38 Oct 29 19:12 debug.log
bash: line 1: /var/log/books-api/debug.log: Permission denied

A new file belongs to whoever made it. Root made debug.log, so the app can't write to it, and on a real server the app would fail to start. When a service suddenly can't write its own files, ls -l and look for root. The last line, chown, fixes it.

A folder without x. Take x off the log folder, leaving r:

BASH
chmod 640 /var/log/books-api
ls -ld /var/log/books-api
su - sam -c 'ls /var/log/books-api'
su - sam -c 'ls -l /var/log/books-api'
su - sam -c 'cd /var/log/books-api'
chmod 750 /var/log/books-api
OUTPUT
drw-r----- 2 bookshop bookshop 4096 Oct 29 19:12 /var/log/books-api
app.log
debug.log
ls: cannot access '/var/log/books-api/debug.log': Permission denied
ls: cannot access '/var/log/books-api/app.log': Permission denied
total 0
-????????? ? ? ? ?            ? app.log
-????????? ? ? ? ?            ? debug.log
-bash: line 1: cd: /var/log/books-api: Permission denied

With r alone, Sam can list the names, but not open the files, see their details (the ?s), or cd in. x on a folder is what lets you use what's inside. Folders almost always need x wherever they have r, so it's 755 or 750, never 644 or 640.

Log in as the app. Try a full login as bookshop:

BASH
su - bookshop
OUTPUT
su: warning: cannot change directory to /home/bookshop: No such file or directory
This account is currently not available.

System users have no home folder, and their shell, /usr/sbin/nologin, prints that message and exits. That's on purpose: nobody should ever log in as the app. You can still run a single command as it, with -s /bin/bash, as you've been doing.

On a real server

  • You'll log in as an ordinary user and use sudo. On an Ubuntu cloud server, that's usually ubuntu, and sudo command runs one command as root, after checking that you're allowed. It logs who did what, unlike logging in as root. The container doesn't have sudo installed; Lesson 8 installs software.
  • Services get their own users, exactly like bookshop. In Lesson 7, systemd starts the app with User=bookshop, and nginx (Lesson 8) runs as a user called www-data.
  • SSH keys must be private. SSH refuses a private key that others can read, with an error that says UNPROTECTED PRIVATE KEY FILE. chmod 600 on the key fixes it. Lesson 11 covers this.
  • Containers do this too. The Docker track's images run as user 1000, and Kubernetes' runAsNonRoot refuses to start a container as root. Same idea, applied to containers.
  • When you see Permission denied, ask three questions: who is the process running as (ps, in Lesson 6), who owns the file (ls -l), and does every folder on the way have x for them?

Spot the problem

Work out what's wrong before you read the explanation.

The app that stopped logging. After a late-night fix, the app keeps running but its log stops growing. ls -l /var/log/books-api shows a new app.log owned by root, and the old one renamed to app.log.1. What happened? Whoever did the fix rotated the log by hand, as root, and the fresh app.log was created by root. The app, running as bookshop, can't write to it. chown bookshop:bookshop app.log fixes it, and so does making the new file as the right user in the first place.

Keep this

ToRun
Who am I, and my groupswhoami, id
Someone else's ID and groupsid sam
List users or groupscat /etc/passwd, cat /etc/group
A folder's own permissionsls -ld folder
Permissions as a numberstat -c '%a %A %U %G %n' file
Add a user who can log inuseradd --create-home --shell /bin/bash sam
Add a user for an appuseradd --system --shell /usr/sbin/nologin bookshop
Add someone to a group (keep -a)usermod -aG bookshop sam
Run one command as another usersu -s /bin/bash bookshop -c 'command'
Log in as another usersu - sam
Change owner and groupchown bookshop:bookshop file
The same, for a folder and everything in itchown -R bookshop:bookshop folder
Set permissions exactlychmod 640 file, chmod 750 folder
Change just one thingchmod +x script, chmod o-r file, chmod g+w file
Settings an app may read but not changeroot:bookshop and 640
Private: only the ownerchmod 600 file
Neverchmod 777

Where you are now

The bookshop now has a proper home: settings in /etc/books-api that the app can read but not change, logs in /var/log/books-api that it owns, a bookshop user that can't log in, and Sam in the group with read access. Lesson 6 starts things running: processes, what they're running as, and how to stop one that won't stop.

Check yourself

  1. What does -rw-r----- mean?

  2. Which number is rwxr-xr-x?

  3. The app can't write its log, and gets Permission denied. What's the best fix?

  4. A folder is drw-r-----. Its owner can list the names inside, but not open the files. Why?

  5. What does usermod -aG bookshop sam do?

  6. Why does the bookshop user have the shell /usr/sbin/nologin?

Key terms

  • User and group: who a process or person is, by name and number. A group gives several users the same access.
  • Root: user 0, the administrator, who can do anything.
  • System user: a user made for one program, with no login, like bookshop or nobody.
  • Permissions: read, write and execute, for the owner, the group and everyone else.
  • Least privilege: giving each user or program only the access it needs.
  • Sticky bit: the t on /tmp: you can only delete your own files there.
  • Setuid: the s on a program like passwd: it runs as its owner, whoever starts it.
  • umask: the permissions taken away from every new file and folder.

Go deeper

  • Make a books-admins group, add Sam to it, and give it write access to /etc/books-api without changing the owner. Which commands does it take?
  • Run ls -l /usr/bin | grep rws to list every setuid program in the container. There are only a few.
  • man 5 passwd explains every field of /etc/passwd, on a machine that has the manuals.
  • Lesson 2, The shell, for reading ls -l.