Linux User and Group Management: Complete Guide

Tested on: Ubuntu 26.04 LTS · Debian 12 · Rocky Linux 10 · Arch Linux — Last updated: June 2026
Every process on Linux runs as a user, every file has an owner, and access control flows entirely from users and groups. Getting this right is foundational — mistakes here cascade into security holes, broken services, and permission nightmares. This guide covers the full lifecycle: creating and modifying users, managing groups, reading /etc/passwd and /etc/shadow, configuring sudo, locking accounts, creating proper service accounts, and handling bulk operations. All commands are cross-distro unless noted.
Prerequisites
- Root or sudo access on the target system
- Familiarity with basic terminal usage and file paths
- For the sudo section: the
sudopackage must be installed (apt install sudoordnf install sudo)
User and Group Concepts
Before touching commands, the data model matters:
- UID (User ID): A unique numeric identifier for each user. Root is always 0. System/service accounts occupy 1–999 (or 1–499 on older RHEL). Regular human users start at 1000.
- GID (Group ID): Numeric ID for each group. By default,
useraddcreates a private group with the same name and GID as the user. - Primary group: Recorded in
/etc/passwd. New files created by the user inherit this group. - Supplementary groups: Additional group memberships listed in
/etc/group. Used to grant access to shared resources (Docker socket, sudo, www-data directories, etc.). - Home directory: Defaults to
/home/usernamefor humans. Service accounts typically use/var/lib/servicenameor no home at all.
The /etc/passwd, /etc/shadow, and /etc/group Files
/etc/passwd is world-readable and stores account metadata. /etc/shadow is root-only and stores password hashes. Never edit these directly — use the tools below.
# /etc/passwd — seven colon-separated fields:
# username:x:UID:GID:GECOS(comment):home:shell
cat /etc/passwd | grep alice
# alice:x:1001:1001:Alice Smith:/home/alice:/bin/bash
# 'x' means the hash lives in /etc/shadow
# /etc/shadow — root-readable only:
# username:hash:lastchange:mindays:maxdays:warndays:inactive:expire:reserved
sudo grep alice /etc/shadow
# alice:$6$rounds=656000$sABC...xyz:19878:0:99999:7:::
# Hash starts with $6$ = SHA-512. $y$ = yescrypt (Ubuntu 24.04+). $5$ = SHA-256.
# /etc/group — group membership:
# groupname:x:GID:member1,member2,...
grep docker /etc/group
# docker:x:998:alice,bob
# getent queries NSS (handles LDAP/NIS too, not just flat files):
getent passwd alice
getent group developersCreating Users
# Minimal: create user with home directory and bash shell:
sudo useradd -m -s /bin/bash alice
# Full options — the way you'd do it in production:
sudo useradd
-m # create home directory from /etc/skel
-s /bin/bash # login shell
-c "Alice Smith" # GECOS / full name
-u 1500 # specific UID (optional — auto-assigned if omitted)
-g developers # primary group (must exist)
-G sudo,docker,www-data # supplementary groups (comma-separated, no spaces)
alice
# Set password immediately:
sudo passwd alice
# Verify:
id alice
# uid=1500(alice) gid=1001(developers) groups=1001(developers),27(sudo),998(docker),33(www-data)
getent passwd alice
# alice:x:1500:1001:Alice Smith:/home/alice:/bin/bashuseradd vs adduser
On Debian and Ubuntu, adduser is an interactive Perl script that wraps useradd — it asks for a password, full name, and other details, then copies skeleton files automatically. It is friendlier for one-off manual account creation. On RHEL, Rocky, and Fedora, adduser is a symlink to useradd and behaves identically. For any automation or scripting, always use useradd directly to keep behavior predictable and avoid interactive prompts.
# Debian/Ubuntu interactive creation:
sudo adduser alice
# Non-interactive useradd for scripts — set password via chpasswd:
sudo useradd -m -s /bin/bash -c "Alice Smith" alice
echo "alice:$(openssl rand -base64 16)" | sudo chpasswdModifying Users
# Change login shell:
sudo usermod -s /bin/zsh alice
# Change home directory AND move existing files there:
sudo usermod -m -d /srv/home/alice alice
# Change primary group:
sudo usermod -g newprimarygroup alice
# Add to supplementary group — ALWAYS use -aG (append + group):
sudo usermod -aG docker alice
sudo usermod -aG sudo,video alice
# WARNING: omitting -a replaces ALL supplementary groups:
# sudo usermod -G docker alice <-- alice loses sudo, www-data, everything else
# Rename the account (username only — home directory path does NOT change automatically):
sudo usermod -l alicesmith alice
# Then update the home path too:
sudo usermod -d /home/alicesmith -m alicesmith
# Change UID (fixes ownership issues when migrating between servers):
sudo usermod -u 1500 alice
# Update GECOS comment field:
sudo usermod -c "Alice M. Smith, Engineering" alice
# Confirm all changes:
id alice
grep alice /etc/passwdDeleting Users
# Remove account but keep home directory and mail spool:
sudo userdel alice
# Remove account AND home directory AND mail spool:
sudo userdel -r alice
# Before deleting, audit what the user owns across the filesystem:
sudo find / -user alice 2>/dev/null
# If the account is already gone, search by UID:
sudo find / -uid 1001 2>/dev/null
# Transfer orphaned files to another user before deleting:
sudo find / -user alice -exec chown bob:bob {} ;
# Check for running processes owned by the user:
ps -u alicePassword Management
# Set password interactively:
sudo passwd alice
# Set password non-interactively (scripts, provisioning):
echo "alice:S3cur3P@ssw0rd" | sudo chpasswd
# Force password change at next login:
sudo passwd -e alice
# or equivalently:
sudo chage -d 0 alice
# Set a full password aging policy:
sudo chage -M 90 alice # password expires after 90 days
sudo chage -m 1 alice # minimum 1 day before password can be changed again
sudo chage -W 14 alice # warn user 14 days before expiry
sudo chage -I 7 alice # lock account 7 days after expiry with no change
# View current aging configuration:
sudo chage -l alice# Example chage -l output:
Last password change : Jun 01, 2026
Password expires : Aug 30, 2026
Password inactive : Sep 06, 2026
Account expires : never
Minimum number of days between password change : 1
Maximum number of days between password change : 90
Number of days of warning before password expires : 14# Lock password (disables password auth — SSH key login still works):
sudo passwd -l alice
# Unlock:
sudo passwd -u alice
# Check password status:
sudo passwd -S alice
# alice L 2026-06-01 1 90 14 7
# Status codes: P=active, L=locked, NP=no passwordGroup Management
# Create a group:
sudo groupadd developers
# Create with a specific GID (useful for consistency across servers):
sudo groupadd -g 2000 developers
# Add user to a group:
sudo usermod -aG developers alice
# Remove user from a group:
sudo gpasswd -d alice developers
# List all members of a group:
getent group developers
# developers:x:2000:alice,bob,charlie
# List all groups a user belongs to:
groups alice
id alice
# Rename a group:
sudo groupmod -n engineering developers
# Change a group's GID:
sudo groupmod -g 2500 developers
# Delete a group (fails if it's anyone's primary group):
sudo groupdel developers
# Switch primary group for the current shell session only:
newgrp developers
# Files created after this command belong to 'developers' groupNote on group changes taking effect: After adding a user to a group with usermod -aG, the change is written immediately to /etc/group, but the user's running processes still hold the old token. The user must log out and back in (or start a new session with newgrp groupname or su - username) before the new membership is visible to their processes.
sudo Configuration
The /etc/sudoers file must only ever be edited with visudo, which validates syntax before saving. A syntax error in sudoers locks out all sudo access — on a remote server, that can be unrecoverable without console access.
# Grant sudo via group membership (preferred for most use cases):
sudo usermod -aG sudo alice # Debian/Ubuntu
sudo usermod -aG wheel alice # RHEL/Rocky/Fedora/Arch
# Edit sudoers (always use visudo):
sudo visudo# /etc/sudoers examples — paste inside visudo:
# Full sudo for a user:
alice ALL=(ALL:ALL) ALL
# Full sudo without password prompt (CI runners, automation accounts):
alice ALL=(ALL:ALL) NOPASSWD: ALL
# Specific commands only (principle of least privilege):
alice ALL=(ALL) /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx
# Group-based sudo (% prefix = group):
%developers ALL=(ALL:ALL) ALL
%wheel ALL=(ALL) NOPASSWD: ALL
# Run commands as a specific non-root user:
alice ALL=(deploy) /usr/bin/git pull, /usr/bin/git status# Drop-in files in /etc/sudoers.d/ are safer than editing the main file:
echo "alice ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/alice
sudo chmod 440 /etc/sudoers.d/alice
# Validate without applying (dry-run):
sudo visudo -c -f /etc/sudoers.d/alice
# parsed OKSwitching Users
# Switch to another user — keeps current environment variables:
su alice
# Switch with a full login environment (loads their .bashrc, .profile, PATH):
su - alice
# Run a single command as another user:
su -c "whoami && env | grep HOME" - alice
# Switch to root:
su -
sudo su -
# Run a command as a specific user via sudo:
sudo -u alice whoami
sudo -u www-data php /var/www/html/artisan cache:clear
# Open an interactive shell as another user via sudo:
sudo -u alice -s /bin/bash
sudo -u alice -i # login shell equivalent of su -Locking and Expiring Accounts
passwd -l only disables password authentication — SSH key login remains possible. usermod -L does the same. To block all access including SSH keys, set the account expiry date or change the shell to /usr/sbin/nologin.
# Lock password (SSH keys still work):
sudo passwd -l alice
sudo usermod -L alice # equivalent
# Unlock:
sudo passwd -u alice
sudo usermod -U alice
# Set account expiry date (blocks ALL logins including SSH keys):
sudo usermod -e 2026-12-31 alice
sudo chage -E 2026-12-31 alice # same result
# Expire immediately (immediate lockout):
sudo usermod -e 1 alice
# Remove expiry date (restore access):
sudo usermod -e "" alice
sudo chage -E -1 alice
# Disable login by setting shell to nologin (also blocks SSH keys):
sudo usermod -s /usr/sbin/nologin alice
# Check lock status:
sudo passwd -S alice
# alice L 2026-06-01 0 99999 7 -1
# L = locked, P = password set, NP = no passwordService Accounts
Services must not run as root. Create a dedicated system user with no home directory, no valid shell, and no password. Pin the UID if you need consistent ownership across multiple servers (containers, NFS mounts).
# Standard service account creation:
sudo useradd
--system
--no-create-home
--shell /usr/sbin/nologin
--comment "MyApp service account"
myapp
# With a pinned UID and a matching system group:
sudo groupadd --system --gid 488 myapp
sudo useradd
--system
--uid 488
--gid 488
--no-create-home
--shell /usr/sbin/nologin
myapp
# Create and set ownership of runtime directories:
sudo mkdir -p /var/lib/myapp /var/log/myapp /run/myapp
sudo chown -R myapp:myapp /var/lib/myapp /var/log/myapp /run/myapp
sudo chmod 750 /var/lib/myapp /var/log/myapp
sudo chmod 755 /run/myapp
# Verify the account cannot be used for interactive
Further Reading
