Understanding Linux Users, Groups & File Permissions
Who is allowed to do what on your server? This foundational guide explains users, groups and file permissions with real commands – the basis of every setup.
Table of contents
Almost every “Permission denied” message, every container that won’t start and every insecure config has the same root cause: a misunderstanding of who is allowed to do what on a Linux server. This guide clears that up for good – with real commands instead of theory.
What are we building?
Not a service this time, but the foundation every other recipe stands on: the Linux permission model. By the end you’ll understand what a user and a group are, how to read and set rwx permissions and the numbers like 755, why www-data and docker keep showing up as groups – and when sudo is the right tool and when it isn’t. All examples were run on Debian 13 and work on any Linux server.
Prerequisites
- A Linux server with root or
sudoaccess (your netcup VPS will do) - Familiarity with the essential terminal commands (
ls,cd,cat) - No extra package needed – all tools ship with Debian
VPS 1000 G12
4 vCores · 8 GB RAM · 256 GB NVMe
from €10.36/month
Any server works for practice – the commands are the same on every Linux.
💶 5 € voucher for new netcup customers:36nc17844976032
(new customers only, no domains)
Step by step
Step 1: Who am I? Users and their IDs
Every process and every file belongs to a user. Who you currently are is shown by id:
iduid=0(root) gid=0(root) groups=0(root)Three things here: the UID (user ID, 0 = root, the administrator), the primary GID (group ID) and every group you’re a member of. On Debian, human users get UIDs from 1000 up; below that are system users for services.
Users live in /etc/passwd – a plain text file:
grep -E '^(root|www-data|nobody):' /etc/passwdroot:x:0:0:root:/root:/bin/bash
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologinThe fields are name, password placeholder (x = the real hash lives in /etc/shadow), UID, GID, comment, home directory and login shell. Note that www-data – the user web servers run as – has the shell /usr/sbin/nologin. Such service users deliberately can’t log in; they exist only so a process runs with minimal privileges instead of as root.
Step 2: Creating a user
For everything except the initial setup you shouldn’t work as root. Create a normal user – here cook:
useradd -m -s /bin/bash cook-m creates the home directory /home/cook, -s /bin/bash sets the login shell. Set a password with passwd cook. Check the result:
id cookuid=1000(cook) gid=1000(cook) groups=1000(cook)useradd automatically created a primary group of the same name, cook (GID 1000). That’s the Debian default: every user gets their own group – so a new file is never accidentally readable by others just because they share a common group.
Step 3: Reading permissions – the rwx model
Every file carries three sets of permissions: for the owner, for the group and for everyone else. Look at them with ls -l:
ls -l-rw-r--r-- 1 root root 0 Jul 24 19:02 file.txt
drwxr-xr-x 2 root root 40 Jul 24 19:02 folderThe first column is the key. Break down drwxr-xr-x:
- Character 1 – the type:
-file,ddirectory,lsymbolic link. - Characters 2–4 (
rwx) – the owner’s permissions: read (r), write (w), execute (x). - Characters 5–7 (
r-x) – the group’s permissions. - Characters 8–10 (
r-x) – everyone else’s permissions.
Then come the owner (root) and group (root). So for file.txt only root may write, everyone may read.
x means something different on directories
x means “executable” (a program or script). On a directory, x means “allowed to enter” (cd). A folder without x can’t be entered even if you have r – a common trap.Step 4: Permissions as a number – why 755 and 644
The same permissions are written compactly as an octal number. Each digit stands for one of the three sets, and it’s the sum of: read = 4, write = 2, execute = 1.
7= 4+2+1 =rwx(everything)6= 4+2 =rw-(read + write)5= 4+1 =r-x(read + execute)4=r--(read only)
That makes the two most common patterns fall out on their own: 644 (rw-r--r--) for normal files and 755 (rwxr-xr-x) for directories and programs. stat shows both notations side by side:
stat -c '%A %a %U %G %n' file.txt folder-rw-r--r-- 644 root root file.txt
drwxr-xr-x 755 root root folderStep 5: Setting permissions with chmod
chmod changes permissions – either numerically or symbolically. Numerically you set all three sets at once. This makes a file readable only by owner and group:
chmod 640 file.txt
stat -c '%A %a %n' file.txt-rw-r----- 640 file.txtSymbolically you flip individual bits: u (user/owner), g (group), o (others), a (all), with +/-/=:
chmod u+x,g-r file.txt
stat -c '%A %a %n' file.txt-rwx------ 700 file.txtNever chmod 777
chmod 777 gives everyone on the system full write access – a classic beginner move to quickly “make a permission problem go away”. It’s almost always the wrong fix and a real security hole. The right answer is to set the correct owner (step 6), not to weaken the permissions.Step 6: Transferring ownership with chown
Who owns a file is changed with chown – in the format user:group:
chown cook:cook file.txt
stat -c '%A %a %U %G %n' file.txt-rwx------ 700 cook cook file.txtFor a whole directory tree add -R (recursive) – exactly what you need constantly with Docker volumes when a container has to write as a specific user:
chown -R cook:cook /opt/myapp/dataWhat happens when the permissions are missing? Opening a root-only file as cook:
su - cook -c "cat /tmp/secret.txt"cat: /tmp/secret.txt: Permission deniedThat message is the most common permission error of all – now you know it doesn’t mean something is broken, but that the model is working exactly as designed.
Step 7: Groups – shared access
Groups bundle users who should access the same files. A user is added to another group with usermod -aG (append to group):
usermod -aG docker cook
id cookuid=1000(cook) gid=1000(cook) groups=1000(cook),990(docker)The -a is crucial: without -a, usermod -G replaces all secondary groups instead of adding one – that’s how people accidentally drop out of sudo or docker. Two groups show up constantly:
sudo– members may run commands as root by prefixingsudo.docker– members may control Docker. Handy, but security-relevant: access to the Docker socket is effectively root access (see the warning below).
A new group is active only after a new login
usermod -aG you must log out and back in (or start newgrp docker), otherwise id shows the group but docker ps keeps failing with “permission denied”.Step 8: sudo instead of permanent root
Staying logged in as root is dangerous: one typo wipes half the system, and any compromised process runs with full privileges. The safe way is a normal user in the sudo group who elevates individual commands when needed:
usermod -aG sudo cookAfter that, cook runs administrative commands by prefixing sudo (sudo apt update) and is asked for their own password. This is traceable (every action lands in /var/log/auth.log), reversible and far safer than a permanent root login. This is exactly what Harden SSH builds on when it disables direct root login.
When things go wrong
Permission denied even though the permissions look right. Check the permissions of the parent directory: if it’s missing the x bit, you can’t reach the file at all, no matter what its own permissions are. ls -ld /path/to/folder shows it.
docker ps still says “permission denied” after usermod -aG docker. The new group isn’t active in the current session yet – log out and back in (see the warning box in step 7). id in the new session must show docker.
New files have unexpected permissions. That’s set by the umask – it subtracts permissions from the defaults. The Debian default umask 0022 produces 644 for files and 755 for directories. Check it with the umask command (no argument).
A container won’t write to the volume. The process inside the container runs under a specific UID (often not root). Set the owner of the host directory to match: chown -R 1000:1000 ./data – the image’s docs name the correct UID (environment variables like PUID/PGID).
usermod dropped the user from groups. You used -G without -a. -G replaces the secondary groups. Always use usermod -aG. Repair: re-add the missing groups with usermod -aG group1,group2 user.
Maintenance & backups
- Permissions rarely need maintenance, but an audit pays off. Occasionally look for world-writable files – a common security leak:
find /opt -perm -0002 -type f. Defuse any hits withchmod o-w. - The Docker socket is root-equivalent.
ls -l /var/run/docker.sockshows ownershiproot:docker– anyone in thedockergroup can start containers with the host filesystem mounted and thereby read and write everything. Only add trusted accounts to that group. - Backups need to carry the permissions along. When you back up data, make sure ownership and permissions are preserved –
resticandrsync -ado this automatically. Otherwise everything belongs to root after a restore and services won’t start. How to do it cleanly is shown in Backups with Restic.
Send feedback: feedback@serverkueche.de
You might also like

Restic backups: encrypted and off-site
Off-site backups with Restic: set up encrypted, restore snapshots, prune with retention and automate via a systemd …

Setting up Fail2ban: block brute-force attacks automatically
Fail2ban watches your logs and bans IPs after too many failed attempts – with a safe whitelist for your own address so …

Understanding Docker Networks: bridge, internal DNS & the proxy network
Why does every app recipe declare a proxy network? This guide explains Docker networks, internal DNS and container …