Module 15 – BONUS: System Users vs Normal Users on Linux
+==============================================================================+
| MODULE 15 (BONUS) : SYSTEM ACCOUNTS vs HUMAN ACCOUNTS |
| Estimated duration: 1-2 hours | Level: Intermediate |
+==============================================================================+
Table of Contents
- Introduction
- The UID convention on Linux
- System users (service accounts)
- Normal users (human accounts)
- Create a system user with
useradd -r - Difference between
-rand-sinuseradd - Real-world examples of system accounts
- Link with Module 14 – The
wikijs_*accounts - Commands to list and identify users
- Best practices in production
- Summary diagram
- Practical exercises
Introduction
On Linux, not all user accounts are equal. There are two main categories:
| Category | Typical UID | Role | Example |
|---|---|---|---|
| System user (service account) | < 1000 | Run services/daemons | www-data, mysql, nobody |
| Normal user (human account) | ≥ 1000 | Physical person who logs in | alice, bob, student |
This is not an absolute rule: it is a convention that depends on the distribution and the configuration of /etc/login.defs.
The UID convention on Linux
What is a UID?
The UID (User IDentifier) is a unique number assigned to each user account. It is this number (not the name) that the Linux kernel uses to manage permissions.
UID ranges
┌──────────────────────────────────────────────────────────────────┐
│ UID RANGES ON LINUX │
├──────────┬───────────────────────────────────────────────────────┤
│ UID 0 │ root (super-user) │
├──────────┼───────────────────────────────────────────────────────┤
│ 1-999 │ SYSTEM accounts / services / daemons │
│ │ (www-data, mysql, sshd, daemon, nobody, etc.) │
├──────────┼───────────────────────────────────────────────────────┤
│ 1000+ │ NORMAL / human accounts │
│ │ (alice, bob, student, etc.) │
├──────────┼───────────────────────────────────────────────────────┤
│ 65534 │ nobody (unprivileged user) │
└──────────┴───────────────────────────────────────────────────────┘
Where are these ranges defined?
The /etc/login.defs file defines the official ranges on your system:
grep -E '^(UID_MIN|UID_MAX|SYS_UID_MIN|SYS_UID_MAX)' /etc/login.defs
Typical result on Ubuntu/Debian:
SYS_UID_MIN 201
SYS_UID_MAX 999
UID_MIN 1000
UID_MAX 60000
Interpretation:
- SYS_UID_MIN (201) → SYS_UID_MAX (999): range reserved for system accounts
- UID_MIN (1000) → UID_MAX (60000): range for human accounts
- UID 0: always
root - UID 1-200: often reserved for critical system accounts (depending on the distribution)
System users (service accounts)
Definition
A system user (or service account) is an account created to run a service, daemon, or system component. It is not intended for a human.
Typical characteristics
| Characteristic | Typical value |
|---|---|
| UID | < 1000 (often between 1 and 999) |
| Shell | /usr/sbin/nologin or /bin/false |
| Home | No home, or a technical directory (e.g., /var/www) |
| Password | Disabled (locked) |
| Interactive login | Impossible (or not desired) |
Why system users?
- Principle of least privilege: each service runs with its limited permissions, not as
root - Isolation: if a service is compromised, the attacker only has the rights of that account
- Traceability: it is known which service performed which action in the logs
- Security: interactive login is avoided (shell
nologin)
Common examples
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
sshd:x:105:65534::/run/sshd:/usr/sbin/nologin
mysql:x:114:120:MySQL Server,,,:/nonexistent:/bin/false
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
root (UID 0) is a special case: it is a system account with an interactive shell and total privileges.
Normal users (human accounts)
Definition
A normal user is an account created for a physical person who logs into the system.
Typical characteristics
| Characteristic | Typical value |
|---|---|
| UID | ≥ 1000 |
| Shell | /bin/bash or /bin/sh |
| Home | /home/<username> |
| Password | Defined (active) |
| Interactive login | Yes |
How to create them?
sudo useradd -m -s /bin/bash alice
id alice
The -m creates the home directory (/home/alice) and the -s /bin/bash provides an interactive shell.
Create a system user with useradd -r
The -r flag (system account)
The -r flag of useradd creates a system account:
sudo useradd -r myservice
What -r does:
- Assigns a UID in the system range (according to
/etc/login.defs) - By default, does not create a home directory (unless
-mis added)
Examples
sudo useradd -r myservice
sudo useradd -r -m myservice
sudo useradd -r -m -s /usr/sbin/nologin myservice
sudo useradd -r -m -s /bin/bash myservice
Verification
id myservice
getent passwd myservice
Difference between -r and -s in useradd
This is a fundamental distinction:
| Option | Role | Effect |
|---|---|---|
-r | Declares the account as system | UID in the system range |
-s | Sets the login shell | Program launched at login |
-r does not affect the shell
sudo useradd -r svc_web
-s does not affect the account type
sudo useradd -s /usr/sbin/nologin alice
Recommended combination for a service
sudo useradd -r -m -s /usr/sbin/nologin svc_web
-r = account type (system); -s = shell (login / non-login). The two are independent.
Real-world examples of system accounts
Apache / Nginx
id www-data
getent passwd www-data
MySQL / MariaDB
id mysql
getent passwd mysql
SSHD
id sshd
Link with Module 14 – The wikijs_* accounts
In the Module 14 lab, we created:
sudo useradd -r -m -s /bin/bash wikijs_dev
sudo useradd -r -m -s /bin/bash wikijs_test
sudo useradd -r -m -s /bin/bash wikijs_prod
These accounts are system because we used -r.
Verification:
id wikijs_dev
id wikijs_test
id wikijs_prod
Link with systemd:
[Service]
User=wikijs_dev
Commands to list and identify users
1) View UID ranges
grep -E '^(UID_MIN|UID_MAX|SYS_UID_MIN|SYS_UID_MAX)' /etc/login.defs
2) List human users (UID ≥ UID_MIN)
UID_MIN=$(awk '/^UID_MIN/{print $2}' /etc/login.defs)
awk -F: -v m="$UID_MIN" '$3>=m {print $1" (uid="$3")"}' /etc/passwd | sort -t'=' -k2 -n
3) List system users (UID < UID_MIN)
UID_MIN=$(awk '/^UID_MIN/{print $2}' /etc/login.defs)
awk -F: -v m="$UID_MIN" '$3<m {print $1" (uid="$3")"}' /etc/passwd | sort -t'=' -k2 -n
4) List non-login accounts (typical services)
awk -F: '($7 ~ /(nologin|false)$/) {print $1" -> "$7}' /etc/passwd
5) List probable human accounts (home + interactive shell)
awk -F: '($6 ~ /^\/home\//) && ($7 ~ /(bash|sh)$/) {print $1" home="$6" shell="$7}' /etc/passwd
Best practices in production
1) Always use -r for service accounts
sudo useradd -r -m -s /usr/sbin/nologin my_service
2) Avoid interactive shells for services
sudo usermod -s /usr/sbin/nologin wikijs_dev
3) Never run a service as root
[Service]
User=www-data
Summary diagram
Decision flow: how to identify an account?
Practical exercises
Exercise 1: identify account types (beginner)
- Display UID ranges:
grep -E '^(UID_MIN|UID_MAX|SYS_UID_MIN|SYS_UID_MAX)' /etc/login.defs
- List system accounts:
UID_MIN=$(awk '/^UID_MIN/{print $2}' /etc/login.defs)
awk -F: -v m="$UID_MIN" '$3<m {print $1" (uid="$3")"}' /etc/passwd | sort -t'=' -k2 -n
- List human accounts:
UID_MIN=$(awk '/^UID_MIN/{print $2}' /etc/login.defs)
awk -F: -v m="$UID_MIN" '$3>=m {print $1" (uid="$3")"}' /etc/passwd | sort -t'=' -k2 -n
Exercise 2: create and compare accounts (intermediate)
sudo useradd -m -s /bin/bash test_human
sudo useradd -r -m -s /usr/sbin/nologin test_service
id test_human
id test_service
Bonus lab created on: February 2026