Creating and Managing Users (useradd, usermod)
Every process on a Linux system runs as some user, and every file belongs to one. Before a person or a service can log in, own files, or run scheduled jobs safely, an administrator has to create an account for them — and later adjust its group memberships, shell, or lock it when it is no longer needed. The two workhorse commands for this are useradd, which creates new accounts, and usermod, which modifies existing ones. Together they are how you manage every human and service identity on a Linux box from the command line.
Overview: How Linux Manages Users
Linux does not really know names like alice or bob. Internally, the kernel only cares about numeric IDs: a UID (user ID) for ownership checks on files and processes, and one or more GIDs (group IDs) for group-based permission checks. The mapping from human-readable names to those numbers lives in a handful of plain text files that useradd and usermod edit for you:
/etc/passwd— one line per user: name, UID, primary GID, comment, home directory, and login shell./etc/shadow— the actual password hashes and password-aging rules, readable only by root./etc/group— one line per group: name, GID, and the list of members who have it as a supplementary group./etc/gshadow— group passwords and administrators (rarely used today).
Because the password hash lives in /etc/shadow rather than /etc/passwd, the password field in /etc/passwd is always just x — a placeholder telling the system “look in shadow.” This split exists because /etc/passwd must be world-readable (many programs need to resolve UIDs to names), while password hashes must not be.
The /etc/passwd fields
| Field | Meaning |
|---|---|
| Username | Login name, e.g. alice |
| Password | Always x — the real hash lives in /etc/shadow |
| UID | Numeric user ID the kernel actually uses for ownership checks |
| GID | Numeric ID of the user’s primary group |
| GECOS | Comment field, usually the full name (the name comes from an old General Electric operating system that used this field for job routing) |
| Home directory | Path set as $HOME on login, e.g. /home/alice |
| Shell | Program started on login, e.g. /bin/bash or /usr/sbin/nologin for accounts that should never get an interactive shell |
UIDs are not handed out randomly. /etc/login.defs defines a range (UID_MIN/UID_MAX, typically starting at 1000 on modern Debian, Ubuntu, and RHEL/Fedora systems) for regular human accounts. UIDs below that — 0 for root, then a block for daemons like www-data or sshd — are system accounts, created automatically by packages or manually with useradd -r. useradd and usermod both ship in the same package on every mainstream distro (passwd on Debian/Ubuntu, shadow-utils on RHEL/Fedora) and are installed by default, so there is normally nothing to apt install or dnf install before you can use them.
A quick note on tooling: Debian and Ubuntu also ship adduser, a friendlier Perl wrapper around useradd that prompts you interactively, creates the home directory, and sets a password by default. useradd itself is the lower-level, distro-portable tool with almost nothing enabled unless you pass flags for it — which makes it the right choice for scripts, but a trap for anyone expecting adduser-like defaults.
Syntax
Both commands take the target username last, after any options:
useradd [options] <username>
usermod [options] <username>
Common useradd options
| Option | Meaning |
|---|---|
-m |
Create the home directory, populated from /etc/skel |
-d <dir> |
Use a specific path as the home directory instead of the default /home/<username> |
-s <shell> |
Set the login shell, e.g. /bin/bash |
-c <comment> |
Set the GECOS comment field, usually the full name |
-u <uid> |
Request a specific numeric UID instead of the next free one |
-g <group> |
Set the primary group by name or GID |
-G <groups> |
Comma-separated list of supplementary groups to join |
-e <YYYY-MM-DD> |
Set an account expiration date |
-r |
Create a system account (low UID, usually no home directory, no password-aging) |
Common usermod options
| Option | Meaning |
|---|---|
-aG <groups> |
Append to the supplementary group list — always pair -a with -G |
-G <groups> |
Replace the entire supplementary group list (dangerous without -a) |
-s <shell> |
Change the login shell |
-d <dir> -m |
Move the home directory to a new path and relocate its contents |
-c <comment> |
Change the GECOS comment field |
-l <name> |
Rename the login (does not rename or move the home directory) |
-L |
Lock the account by prefixing the password hash in /etc/shadow with ! |
-U |
Unlock a previously locked account |
-e <YYYY-MM-DD> |
Change the account expiration date |
Examples
Example 1: Create a basic user with a home directory
sudo useradd -m -s /bin/bash alice
sudo passwd alice
Output of the passwd prompt:
New password:
Retype new password:
passwd: password updated successfully
-m creates /home/alice and copies the skeleton files from /etc/skel (things like .bashrc and .profile) into it, then makes alice the owner. -s /bin/bash sets the login shell. Notice that useradd by itself does not set a password — the new account is locked (its shadow entry starts with !) until you run passwd. Without that second step, alice could not log in with a password at all, though key-based SSH login would still work once a key is authorized.
Verify the account:
id alice
uid=1001(alice) gid=1001(alice) groups=1001(alice)
Example 2: Create a user with a specific UID, comment, and groups
sudo useradd -m -u 1500 -c "Bob Johnson, DevOps" -G sudo,docker -s /bin/bash bob
This pins Bob’s UID to 1500 (useful when the same UID must match across several servers sharing NFS storage), records his full name and role in the GECOS field, and immediately adds him to the sudo and docker supplementary groups at creation time — no separate usermod step needed for a brand-new account.
getent passwd bob
bob:x:1500:1500:Bob Johnson, DevOps:/home/bob:/bin/bash
groups bob
bob : bob sudo docker
Example 3: Modify an existing account with usermod
sudo usermod -aG docker alice
sudo usermod -s /bin/bash alice
sudo usermod -L bob
The first line adds alice to the docker group without touching any group she already belongs to, because -a (append) is combined with -G. The second changes her shell. The third locks Bob’s account by inserting ! in front of his password hash in /etc/shadow — he can no longer authenticate with a password, but the account, its files, and its UID still exist, so nothing referencing bob breaks.
groups alice
alice : alice docker
How It Works Step by Step
When you run sudo useradd -m -s /bin/bash alice, roughly this happens:
sudore-executesuseraddas root (via PAM authentication), since only root can write to/etc/passwdand/etc/shadow.useraddreads defaults from/etc/login.defsand/etc/default/useradd— things like the UID range and whether a private group per user should be created.- It picks the next free UID at or above
UID_MIN(unless you supplied-u). - On Debian/Ubuntu, by default it also creates a new group with the same name and a matching GID, so each user gets their own private primary group rather than sharing a generic
usersgroup. - A new line is appended to
/etc/passwdwith the username, UID, GID, GECOS, home path, and shell. - A matching line is appended to
/etc/shadowwith the password field set to!(locked, no password yet) and password-aging fields taken from/etc/login.defs. - Because
-mwas given,/home/aliceis created, the contents of/etc/skelare copied in, and the whole tree ischown‘d toalice:aliceand set to mode 700.
When you later run sudo usermod -aG docker alice, usermod reads /etc/group, finds the docker line, and appends alice to its member list, then writes the file back. It does not touch /etc/passwd, her primary GID, or her password at all — only the one group line changes.
Common Mistakes
Mistake 1: Using -G instead of -aG
Wrong — this looks like it adds a group, but it actually replaces the entire supplementary group list with just docker, silently removing every other group membership alice had:
sudo usermod -G docker alice
Correct — always pair -a (append) with -G when your intent is to add one group:
sudo usermod -aG docker alice
Mistake 2: Forgetting -m and getting a user with no home directory
Wrong — without -m, useradd still creates the account, but no home directory is ever made:
sudo useradd -s /bin/bash carol
ls: cannot access '/home/carol': No such file or directory
Correct — pass -m whenever the account needs a real home directory (skip it only for intentional system/service accounts):
sudo useradd -m -s /bin/bash carol
Mistake 3: Deleting a user without -r and leaving orphaned files
Wrong — plain userdel removes the account from /etc/passwd and /etc/shadow but leaves the home directory and mail spool sitting on disk, owned by a UID that no longer maps to any name:
sudo userdel carol
Correct — once you have confirmed the data is no longer needed, use -r to remove the home directory and mail spool along with the account:
sudo userdel -r carol
Mistake 4: Editing /etc/passwd or /etc/shadow by hand
Opening these files directly in a text editor risks a corrupted file — a stray line break or a process reading the file mid-edit can lock every account out of the system. Always go through useradd/usermod, or use vipw (and vipw -s for the shadow file) if manual editing is unavoidable; it locks the file and validates it on save.
Best Practices
- Always pair
-awith-Ginusermod— bare-Gsilently wipes existing group memberships. - Pass
-mfor any account that needs to log in interactively; skip it only for deliberate system/service accounts created with-r. - Give real accounts a meaningful
-ccomment sogetent passwdoutput is identifiable months later. - Use
-eto set an expiration date on temporary or contractor accounts instead of relying on someone to remember to remove them. - Lock accounts first with
usermod -L(orpasswd -l) instead of deleting immediately — you can restore access instantly if it turns out to be needed, whereasuserdel -ris not reversible. - Verify every change with
id <username>,groups <username>, orgetent passwd <username>— do not assume the command did what you intended. - Use
adduserfor interactive, manual administration on Debian/Ubuntu; useuseraddin scripts, since it behaves consistently across distros and never prompts. - Assign explicit UIDs/GIDs with
-u/-gwhen the same account must resolve to the same numeric ID across multiple servers sharing NFS-mounted storage. - Never edit
/etc/passwdor/etc/shadowdirectly — use the dedicated commands, orvipwif you truly must edit by hand.
Practice Exercises
- Create a user named
devopswith a home directory, Bash as the login shell, and immediate membership in thesudogroup — do it in a singleuseraddcommand, then set a password for the account. - An existing user
mariais already a member of thedevelopersgroup. Add her to thedockergroup as well without losing her existing membership, then confirm with a command that lists all of a user’s groups. - Lock the account of a former contractor named
guest01without deleting it, verify that the account is locked by inspecting the relevant field, then decide what you would check before eventually runninguserdel -ron it.
Summary
useraddcreates accounts by writing entries to/etc/passwdand/etc/shadowand, with-m, building a home directory from/etc/skel.- A new account has no usable password until you run
passwd— it starts locked. usermodchanges an existing account’s shell, comment, groups, UID mapping in place, or lock state — it never creates a new user.- Always use
-aG, never bare-G, when adding someone to a group withusermod. - The GID and supplementary groups in
/etc/groupcontrol group-based file permissions; the UID in/etc/passwdis what the kernel actually checks. - Prefer locking (
usermod -L) over deleting when an account might need to come back, and always useuserdel -rdeliberately, not by default.
