Users, Groups, and Sudo
How Linux identifies who is doing what, why services run as their own users, and how sudo gives you root without making you root.
Real-World Analogy
Users and sudo are like office key cards — most employees get the main door, but only a few authorized people get the server room.
A user is a number
To the kernel, you are a UID — a 32-bit integer. The username deploy is just a friendly label that resolves to UID 1000 via /etc/passwd. The kernel only ever sees 1000. Same for groups: a GID is the real identity; /etc/group maps it to a name.
Look at yours:
$ id
uid=1000(deploy) gid=1000(deploy) groups=1000(deploy),27(sudo) You are UID 1000, primary group GID 1000, also a member of group 27 (sudo). Every file you create gets owned by 1000:1000 until you say otherwise.
/etc/passwd, /etc/shadow, /etc/group
These three plaintext files are the entire user database on most Linux systems.
$ getent passwd deploy
deploy:x:1000:1000:Deploy user,,,:/home/deploy:/bin/bash Seven colon-separated fields:
- Username —
deploy - Password placeholder — always
x. The real hash lives in/etc/shadow, which is root-only. - UID —
1000 - GID (primary group) —
1000 - GECOS (display name, phone, etc.) —
Deploy user,,, - Home directory —
/home/deploy - Shell —
/bin/bash
The shell is set when you SSH in or run su - deploy. /usr/sbin/nologin (used by service users) means “this account cannot log in interactively.”
$ getent shadow deploy
deploy:$y$j9T$abc...:19815:0:99999:7::: The second field is the password hash ($y$ is yescrypt, the modern default). If it is * or !, the account has no password and login is disabled — exactly what you want for service users.
$ getent group sudo
sudo:x:27:deploy,alice Group 27 is sudo, with two members.
Primary group vs supplementary groups
Every user has one primary group (field 4 of /etc/passwd) and zero or more supplementary groups (the comma-separated list in /etc/group).
When you create a file, it gets owned by your primary group by default. id shows both:
$ id
uid=1000(deploy) gid=1000(deploy) groups=1000(deploy),27(sudo),100(users) Add yourself to a group:
sudo usermod -aG docker deploy -a means append (without it, usermod -G replaces all your groups — a classic foot-gun). The change does not take effect until you log out and back in, because supplementary groups are loaded at login.
System users vs login users
There is a soft convention:
- UIDs 0–999 — system users (root, daemon users like
nginx,postgres). - UIDs 1000+ — real human users.
adduser and useradd honor this. When you provision a service, give it a system user:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp That creates a UID in the system range (e.g., 998), no home, no shell. This is the user your systemd unit’s User=myapp directive refers to. The account exists in the kernel’s eyes for permission checks, but no one can log in as it.
File ownership in practice
$ ls -l /opt/myapp/
total 12
-rwxr-xr-x 1 myapp myapp 8192 May 4 10:42 binary
drwx------ 2 myapp myapp 4096 May 4 10:42 data
-rw-r----- 1 myapp myapp 340 May 4 10:42 config.yaml - The binary is executable by everyone (
r-xfor other), but onlymyappcan write it. data/is owned bymyappand onlymyappcan enter it (drwx------).config.yamlisr--for the group only — themyappuser reads it, themyappgroup reads it, no one else can.
When your systemd unit declares User=myapp Group=myapp, the running process becomes UID/GID of myapp — and these permissions all line up. Other users on the box cannot read the config or the data directory, even if they SSH in.
The root account
UID 0 is special. The kernel grants UID 0 every privilege regardless of file permissions. Root can read /etc/shadow, kill any process, mount any filesystem, write to any disk.
That is exactly why you do not run as root day-to-day. One typo (rm -rf $UNDEFINED/) erases the entire box. One compromised process running as root owns everything. Working as a non-root user with sudo for elevation gives you a deliberate moment to think before each privileged action.
sudo — root with paperwork
sudo lets a normal user run a single command as root, after authenticating with their own password (not root’s), and logs every invocation.
$ sudo systemctl restart nginx
[sudo] password for deploy:
$ Three things happen:
sudochecks/etc/sudoersto see whetherdeployis allowed to run that command.- It prompts for
deploy’s password (the first time in 5 minutes, by default). - It logs the command to
journalctl -t sudo.
Look at the log:
$ sudo journalctl -t sudo -n 5
May 04 10:42:11 web-01 sudo[2345]: deploy : TTY=pts/0 ; PWD=/home/deploy ; USER=root ; COMMAND=/usr/bin/systemctl restart nginx Every privileged action is attributable. That alone is worth the friction.
/etc/sudoers and visudo
/etc/sudoers is the sudo policy file. Never edit it directly — use visudo, which validates the file before saving. A broken sudoers file means no one can run sudo until you boot from rescue media.
sudo visudo The default Debian/Ubuntu file ends with:
# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) ALL
# See sudoers(5) for more information on "@include" directives:
@includedir /etc/sudoers.d The percent-sign means “group”. So any member of sudo can run any command as any user.
For finer-grained policies, drop a file in /etc/sudoers.d/:
sudo visudo -f /etc/sudoers.d/deploy-restart-nginx deploy ALL=(root) NOPASSWD: /bin/systemctl restart nginx
deploy ALL=(root) NOPASSWD: /bin/systemctl reload nginx deploy can now restart or reload nginx without entering a password — but not, say, edit /etc/passwd. Useful for deploy scripts that need exactly one privileged action.
NOPASSWD is a sharp tool.
Anyone who lands a shell as deploy can run those commands without authentication. If the command takes a path argument, they could potentially do more than you intended — systemctl edit and similar will open an editor with full root powers. Lock down to exact command lines, never patterns.
su vs sudo -i vs sudo -s
Three ways to “become root”:
sudo -i # become root with root's full login environment (recommended)
sudo -s # root shell, but with your environment
su - # switch user, become root, requires root's password
sudo su - # equivalent to sudo -i, but uglier Use sudo -i when you legitimately need to do many things as root in a row — building from source, debugging weird permission issues. Always exit back to your normal user when done.
Capabilities — fine-grained root powers
Linux has split root’s omnipotence into ~40 capabilities. Examples:
CAP_NET_BIND_SERVICE— bind to ports below 1024.CAP_SYS_TIME— change the system clock.CAP_SYS_PTRACE— attach a debugger to other processes.CAP_DAC_OVERRIDE— bypass file permission checks.
You can grant a capability to a specific binary:
sudo setcap 'cap_net_bind_service=+ep' /opt/myapp/bin/server Now /opt/myapp/bin/server can listen on port 80 without being root. This is a much safer pattern than running the whole process as root just to bind a privileged port. systemd’s AmbientCapabilities=CAP_NET_BIND_SERVICE does the same thing for you.
Common mistakes
- Running services as root. A web server bug at root is a kernel-level compromise. Run services as their own users.
usermod -G docker deploywithout-a. You just removeddeployfromsudoand every other group.- Editing
/etc/sudoersdirectly. Usevisudo. Always. - Sharing the
deployuser across humans. Every human gets their own login.deployis a deploy automation user, not a person.
Recap
- Users and groups are numbers; names are labels in
/etc/passwdand/etc/group. - System users (UID < 1000) for services. Login users for humans. Service users have
nologinshells. - Run services as their own user. Lock files down to that user.
sudoinstead of working as root. Audit log viajournalctl -t sudo.- Use
visudoto edit policy. Usesudoers.dfor per-task overrides. Usesetcapinstead of root for privileged ports.
Next chapter: where all those service logs go, and how to make sense of them.