Skip to content
← Linux / VPS · intermediate · 11 min · 08 / 13

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.

usersgroupssudopermissionslinux

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:

  1. Usernamedeploy
  2. Password placeholder — always x. The real hash lives in /etc/shadow, which is root-only.
  3. UID1000
  4. GID (primary group) — 1000
  5. GECOS (display name, phone, etc.) — Deploy user,,,
  6. Home directory/home/deploy
  7. 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-x for other), but only myapp can write it.
  • data/ is owned by myapp and only myapp can enter it (drwx------).
  • config.yaml is r-- for the group only — the myapp user reads it, the myapp group 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:

  1. sudo checks /etc/sudoers to see whether deploy is allowed to run that command.
  2. It prompts for deploy’s password (the first time in 5 minutes, by default).
  3. 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 deploy without -a. You just removed deploy from sudo and every other group.
  • Editing /etc/sudoers directly. Use visudo. Always.
  • Sharing the deploy user across humans. Every human gets their own login. deploy is a deploy automation user, not a person.

Recap

  • Users and groups are numbers; names are labels in /etc/passwd and /etc/group.
  • System users (UID < 1000) for services. Login users for humans. Service users have nologin shells.
  • Run services as their own user. Lock files down to that user.
  • sudo instead of working as root. Audit log via journalctl -t sudo.
  • Use visudo to edit policy. Use sudoers.d for per-task overrides. Use setcap instead of root for privileged ports.

Next chapter: where all those service logs go, and how to make sense of them.