User management
User Management Manual
Linux user accounts are the atomic unit of identity on the system. Every process runs as a user. Every file has an owner. Every access decision – read, write, execute – is made against a user identity and its group memberships. Understanding how to create, modify, lock, and destroy users is foundational sysadmin work.
Think of the user database as a cell’s membrane – it defines what is inside the system and what is outside, who gets through and at what privilege level. The kernel enforces boundaries; user management is how you define them.
The four distributions covered – Alpine, Arch, Ubuntu, and Rocky – all implement POSIX user management but differ in their default tools, shadow password handling, and in what ships by default. Alpine uses BusyBox utilities; the others use shadow-utils. The commands are mostly compatible; the differences are noted where they matter.
1. Core Files
Everything about users and groups is stored in four plain-text files. Know them.
| File | Contents |
|---|---|
/etc/passwd | User accounts: username, UID, GID, home, shell |
/etc/shadow | Hashed passwords and password policy per user |
/etc/group | Group definitions and memberships |
/etc/gshadow | Group passwords and group admins (rarely used) |
/etc/passwd Format
username:x:UID:GID:GECOS:home_dir:login_shell
xin the password field means the password is in/etc/shadow- GECOS is the comment field – typically full name, room, phone
- UID 0 is root. UIDs 1–999 are typically system accounts. UIDs 1000+ are human users
/etc/shadow Format
username:hashed_pw:last_change:min_age:max_age:warn:inactive:expire:reserved
- All values are in days since the Unix epoch (1970-01-01)
!or*in the password field means the account is locked!!means no password has ever been set
/etc/group Format
groupname:x:GID:member1,member2,member3
2. Installation
Alpine Linux
# shadow provides useradd/usermod/userdel on Alpine (BusyBox has adduser/deluser by default)
apk add shadow sudo
Arch Linux
# shadow-utils is installed by default
# sudo requires separate install
sudo pacman -S sudo
Ubuntu / Debian
# All tools are present by default
sudo apt install passwd sudo adduser
Rocky Linux / RHEL
# All tools are present by default
sudo dnf install shadow-utils sudo
3. Adding Users
useradd – Low-Level Tool
useradd is the low-level, POSIX-standard command. It does exactly what you specify – no more. You must explicitly set options for home directory creation, shell, etc.
Create a user with defaults (no home dir, no password, system default shell):
sudo useradd username
Create a user with a home directory:
sudo useradd -m username
Create a user with home directory, shell, and comment:
sudo useradd -m -s /bin/bash -c "Full Name" username
Create a user with a specific UID:
sudo useradd -m -u 1500 username
Create a user with a specific primary group:
sudo useradd -m -g groupname username
Create a user with multiple supplementary groups:
sudo useradd -m -G wheel,docker,git username
Create a user with a specific home directory path:
sudo useradd -m -d /srv/apps/myapp username
Create a user with an account expiry date:
sudo useradd -m -e 2026-12-31 username
Create a system user (no home dir, no login shell, UID < 1000):
sudo useradd -r -s /usr/sbin/nologin myservice
sudo useradd -r -s /bin/false myservice
Full example – production user with all options:
sudo useradd \
-m \
-d /home/dil \
-s /bin/bash \
-u 1100 \
-g users \
-G wheel,docker \
-c "Dil Osaigbovo" \
dil
adduser – Higher-Level Wrapper
adduser is an interactive wrapper over useradd. It is more forgiving and asks for input if options are not provided. Behaviour differs between Alpine/Debian and Arch/Rocky.
Ubuntu / Debian:
sudo adduser username
# Interactive: asks for password, full name, room, phone, etc.
Non-interactive with all options:
sudo adduser --home /home/username --shell /bin/bash --gecos "Full Name" username
Alpine Linux (BusyBox adduser):
adduser username
adduser -h /home/username -s /bin/ash username
adduser -D username # no password, non-interactive
adduser -S username # system user
adduser -u 1200 -G users username # specific UID and group
Arch / Rocky:
# adduser is not present by default; use useradd
4. Setting and Managing Passwords
passwd
Set or change a user’s password (as root – can change any user):
sudo passwd username
Set your own password (no root needed):
passwd
Set a password non-interactively (for scripting):
echo "username:newpassword" | sudo chpasswd
Set a password using a hashed value directly:
# Generate a SHA-512 hash
openssl passwd -6 "mypassword"
python3 -c "import crypt; print(crypt.crypt('mypassword', crypt.mksalt(crypt.METHOD_SHA512)))"
# Apply the hash directly
sudo usermod -p '$6$salt$hashedvalue...' username
Lock a user’s password (prefix with ! in shadow, disables password auth):
sudo passwd -l username
Unlock a user’s password:
sudo passwd -u username
Expire a password immediately (force change at next login):
sudo passwd -e username
Delete a password (allows passwordless login – dangerous):
sudo passwd -d username
Show password status:
sudo passwd -S username
# Output: username P 2026-01-01 0 99999 7 -1
# P = has password, L = locked, NP = no password
# Fields: name, status, last_change, min, max, warn, inactive
chpasswd (batch password setting)
# From a file
echo "user1:password1" | sudo chpasswd
echo "user2:password2" | sudo chpasswd
# Multiple users at once
sudo chpasswd << EOF
user1:pass1
user2:pass2
user3:pass3
EOF
5. Password Policy (chage)
chage manages password ageing – how long a password is valid, how soon the user is warned, and when the account expires.
Show current password ageing info for a user:
sudo chage -l username
Set maximum password age (force change every N days):
sudo chage -M 90 username
Set minimum password age (prevent immediate re-change):
sudo chage -m 7 username
Set warning period (warn user N days before expiry):
sudo chage -W 14 username
Set inactivity period (lock account N days after password expires):
sudo chage -I 30 username
Set account expiry date:
sudo chage -E 2026-12-31 username
Remove account expiry:
sudo chage -E -1 username
Force password change at next login:
sudo chage -d 0 username
Full example – set a complete password policy:
sudo chage -M 90 -m 7 -W 14 -I 30 -E 2027-01-01 username
System-Wide Password Policy
Default password policy for new accounts is defined in /etc/login.defs:
# Key fields in /etc/login.defs
PASS_MAX_DAYS 90 # maximum password age
PASS_MIN_DAYS 7 # minimum password age
PASS_WARN_AGE 14 # days before warning
UID_MIN 1000 # minimum UID for human users
UID_MAX 60000 # maximum UID for human users
GID_MIN 1000
GID_MAX 60000
ENCRYPT_METHOD SHA512 # password hashing algorithm
CREATE_HOME yes # create home by default
UMASK 022 # default umask
6. Modifying Users
usermod – All Modifications
Change username:
sudo usermod -l newname oldname
Change home directory (does not move files):
sudo usermod -d /new/home username
Change home directory and move files:
sudo usermod -d /new/home -m username
Change login shell:
sudo usermod -s /bin/zsh username
sudo usermod -s /usr/bin/fish username
sudo usermod -s /sbin/nologin username # disable interactive login
Change UID:
sudo usermod -u 1500 username
Change primary group:
sudo usermod -g newgroup username
Add to supplementary groups (append – do not replace):
sudo usermod -aG docker username
sudo usermod -aG wheel,docker,sudo username
Set supplementary groups (replaces existing):
sudo usermod -G docker,git username
Change GECOS / comment field:
sudo usermod -c "New Full Name" username
Set account expiry date:
sudo usermod -e 2027-06-01 username
Remove account expiry:
sudo usermod -e "" username
Lock account:
sudo usermod -L username
Unlock account:
sudo usermod -U username
7. Deleting Users
userdel
Delete a user (keeps home directory and files):
sudo userdel username
Delete a user and their home directory:
sudo userdel -r username
Force delete even if the user is logged in:
sudo userdel -f username
sudo userdel -rf username # force + remove home
deluser (Ubuntu / Alpine)
# Ubuntu
sudo deluser username
sudo deluser --remove-home username
sudo deluser --remove-all-files username # removes all files owned by user
# Alpine (BusyBox)
deluser username
Cleanup After Deletion
After deleting a user, orphaned files remain on the filesystem – files owned by a now-nonexistent UID:
# Find all files on the system owned by a specific UID (run before deleting)
sudo find / -uid 1100 -ls 2>/dev/null
# Find orphaned files (owned by no current user)
sudo find / -nouser -ls 2>/dev/null
# Find orphaned directories
sudo find / -nogroup -ls 2>/dev/null
# Remove or reassign orphaned files
sudo find / -nouser -exec chown root:root {} \;
8. Groups
Creating Groups
sudo groupadd groupname
sudo groupadd -g 1500 groupname # specific GID
sudo groupadd -r sysgroup # system group (GID < 1000)
Modifying Groups
sudo groupmod -n newname oldname # rename group
sudo groupmod -g 1600 groupname # change GID
Deleting Groups
sudo groupdel groupname
You cannot delete a group that is the primary group of any user. Remove the users from it first or change their primary group.
Managing Group Membership
Add a user to a group:
sudo usermod -aG groupname username
sudo gpasswd -a username groupname
Remove a user from a group:
sudo gpasswd -d username groupname
Set the complete member list for a group:
sudo gpasswd -M user1,user2,user3 groupname
Set a group administrator (can add/remove members without sudo):
sudo gpasswd -A username groupname
Set a group password (rarely used):
sudo gpasswd groupname
Viewing Group Membership
Show groups a user belongs to:
groups username
id username
Show all members of a group:
getent group groupname
grep '^groupname:' /etc/group
List all groups:
cat /etc/group
getent group
9. Switching Users and Privilege Escalation
su – Switch User
Switch to root:
su -
su -l
Switch to another user (load their environment):
su - username
Run a single command as another user:
su -c "command" username
su -c "systemctl restart nginx" root
sudo – Delegated Root Access
Run a command as root:
sudo command
Run a command as a specific user:
sudo -u username command
sudo -u www-data ls /var/www
Open a root shell:
sudo -i # login shell (loads root's environment)
sudo -s # non-login shell
sudo su - # full root shell via su
Run multiple commands as root in a subshell:
sudo bash -c 'command1 && command2'
Preserve the caller’s environment:
sudo -E command
List what the current user can do with sudo:
sudo -l
10. sudo Configuration
/etc/sudoers controls who can run what with sudo. Always edit it with visudo – it validates syntax before saving. A broken sudoers file locks you out of sudo.
sudo visudo
On systems using /etc/sudoers.d/, add per-user or per-group files instead:
sudo visudo -f /etc/sudoers.d/dil
sudoers Syntax
Grant full root access:
username ALL=(ALL:ALL) ALL
Grant access without password:
username ALL=(ALL) NOPASSWD: ALL
Grant access to a specific command only:
username ALL=(ALL) /usr/bin/systemctl restart nginx
Grant multiple specific commands:
username ALL=(ALL) NOPASSWD: /usr/bin/apt update, /usr/bin/apt upgrade
Grant a group full access:
%wheel ALL=(ALL:ALL) ALL
%sudo ALL=(ALL:ALL) ALL
Restrict to specific hosts:
username webserver=(ALL) ALL
Wheel Group (Arch / Rocky)
Arch and Rocky use the wheel group for sudo access. It is commented out by default:
sudo visudo
# Uncomment the line:
%wheel ALL=(ALL:ALL) ALL
Then add the user to wheel:
sudo usermod -aG wheel username
sudo Group (Ubuntu)
Ubuntu uses the sudo group:
sudo usermod -aG sudo username
Alpine sudo
apk add sudo
# Add to wheel group
adduser username wheel
# Edit sudoers
visudo
# Uncomment: %wheel ALL=(ALL) ALL
11. Locking and Disabling Accounts
There are multiple ways to prevent a user from logging in. They are not equivalent.
Lock the Password
Prefixes ! to the password hash in /etc/shadow. Blocks password authentication but SSH key auth still works.
sudo passwd -l username
sudo usermod -L username
Unlock:
sudo passwd -u username
sudo usermod -U username
Set a Non-Login Shell
Blocks interactive login entirely (SSH included if UsePAM yes is set):
sudo usermod -s /sbin/nologin username
sudo usermod -s /bin/false username
/sbin/nologin displays a message before refusing. /bin/false silently exits. Neither allows interactive access.
Expire the Account
sudo chage -E 0 username # expire immediately
sudo usermod -e 1970-01-02 username # past date = expired
Lock SSH Access
In /etc/ssh/sshd_config:
DenyUsers username
DenyGroups badgroup
Or create ~/.ssh/authorized_keys with no keys and set chmod 600 so no keys can be added.
Check Lock Status
sudo passwd -S username
sudo chage -l username
sudo grep username /etc/shadow
12. User Information and Inspection
Show user account info:
id username # UID, GID, groups
finger username # detailed info (if `finger` installed)
getent passwd username # entry from /etc/passwd (also queries LDAP/NIS)
getent shadow username # shadow entry (root only)
Show all users:
cat /etc/passwd
getent passwd
awk -F: '$3 >= 1000 && $3 < 65534 {print $1}' /etc/passwd # human users only
Show currently logged-in users:
who
w # more detailed -- what they're running
users # just usernames
last # login history
last username # login history for a specific user
lastb # failed login attempts (root only)
Show last login time:
lastlog
lastlog -u username
13. PAM – Pluggable Authentication Modules
PAM is the authentication framework underlying all login operations. Understanding it at a basic level is necessary for advanced user management – password complexity, account lockout, session limits.
PAM config files live in /etc/pam.d/. Each service has its own file (sshd, login, sudo, passwd).
Password Complexity (pam_pwquality)
Install:
# Alpine
apk add linux-pam
# Arch
sudo pacman -S libpwquality
# Ubuntu
sudo apt install libpam-pwquality
# Rocky
sudo dnf install libpwquality
Configure in /etc/security/pwquality.conf:
minlen = 12 # minimum length
dcredit = -1 # at least 1 digit
ucredit = -1 # at least 1 uppercase
lcredit = -1 # at least 1 lowercase
ocredit = -1 # at least 1 special character
maxrepeat = 3 # no character repeated more than 3 times
usercheck = 1 # reject passwords containing username
Enable in /etc/pam.d/passwd (or common-password on Ubuntu):
password required pam_pwquality.so retry=3
Account Lockout After Failed Attempts (pam_faillock)
Install:
# Arch
sudo pacman -S pam
# Ubuntu
sudo apt install libpam-modules
# Rocky
sudo dnf install pam
Configure /etc/security/faillock.conf:
deny = 5 # lock after 5 failures
unlock_time = 900 # unlock after 15 minutes
fail_interval = 300 # count failures within 5-minute window
Add to /etc/pam.d/system-auth (before pam_unix.so):
auth required pam_faillock.so preauth
auth [default=die] pam_faillock.so authfail
auth sufficient pam_unix.so
auth [default=die] pam_faillock.so authfail
account required pam_faillock.so
Check locked accounts:
sudo faillock
sudo faillock --user username
Manually unlock:
sudo faillock --user username --reset
Limit Simultaneous Sessions (pam_limits)
Configure /etc/security/limits.conf:
# Format: username type resource value
dil hard nproc 50 # max processes
dil soft nofile 1024 # open files (soft)
dil hard nofile 65536 # open files (hard)
@developers soft nproc 100 # group limit
* hard core 0 # disable core dumps for all
Enable in /etc/pam.d/system-auth:
session required pam_limits.so
14. Alpine-Specific Notes
Alpine uses BusyBox’s adduser/deluser by default. Install the shadow package to get full useradd/usermod/userdel compatibility.
apk add shadow
BusyBox adduser does not support all flags that shadow’s useradd does. In container environments (Incus/Docker on Alpine), prefer:
# Add a user in a Dockerfile/container init
adduser -D -H -s /sbin/nologin appuser # no password, no home, no login
adduser -D -h /app -s /bin/sh appuser # with home, ash shell
Alpine uses /etc/group and /etc/passwd normally. sudo on Alpine requires adding to the wheel group via BusyBox adduser:
adduser username wheel
15. Home Directory Management
Create a home directory manually for an existing user:
sudo mkdir -p /home/username
sudo cp -r /etc/skel/. /home/username/ # copy skeleton files
sudo chown -R username:username /home/username
sudo chmod 700 /home/username
/etc/skel/ contains the template files copied into every new home directory. Customise it to control what every new user starts with:
ls /etc/skel/
# .bash_profile .bashrc .profile (and whatever you add)
Move a home directory:
sudo usermod -m -d /new/home/username username
16. Scripting Common Tasks
Bulk User Creation
#!/bin/bash
# create-users.sh
# Input file: users.txt -- one per line: username:password:groups:shell
while IFS=: read -r username password groups shell; do
"$username" =~ ^# && continue # skip comments
sudo useradd -m -s "$shell" -G "$groups" "$username"
echo "$username:$password" | sudo chpasswd
sudo chage -M 90 -W 14 "$username"
echo "Created: $username"
done < users.txt
Audit All Users
#!/bin/bash
# List all human users with UID, groups, shell, last login
awk -F: '$3 >= 1000 && $3 < 65534 {print $1}' /etc/passwd | while read user; do
uid=$(id -u "$user")
groups=$(groups "$user" | cut -d: -f2 | xargs)
shell=$(getent passwd "$user" | cut -d: -f7)
last=$(lastlog -u "$user" | tail -1 | awk '{print $4, $5, $6, $9}')
printf "%-15s UID:%-6s SHELL:%-15s GROUPS:%-30s LAST:%s\n" \
"$user" "$uid" "$shell" "$groups" "$last"
done
Remove Inactive Users
#!/bin/bash
# Find users who haven't logged in for 180 days
CUTOFF=$(date -d '180 days ago' +%Y-%m-%d)
lastlog | awk 'NR>1 && $4 != "**Never" {print $1, $4, $5, $6, $9}' | while read user month day dow year; do
last_login="${year}-$(date -d "$month $day" +%m-%d)"
if "$last_login" < "$CUTOFF" ; then
echo "Inactive: $user (last: $last_login)"
fi
done
17. Quick Reference
Add a Standard User
sudo useradd -m -s /bin/bash -G wheel,docker -c "Full Name" username
sudo passwd username
Add a Service Account (no login)
sudo useradd -r -s /sbin/nologin -d /var/lib/myapp -m myapp
Lock a User
sudo passwd -l username # lock password
sudo usermod -s /sbin/nologin username # block shell
Delete a User Completely
sudo userdel -r username
sudo find / -nouser -ls 2>/dev/null # check for orphaned files
Grant sudo
# Arch / Rocky
sudo usermod -aG wheel username
# Ubuntu
sudo usermod -aG sudo username
Group Operations
sudo groupadd mygroup
sudo usermod -aG mygroup username
sudo gpasswd -d username mygroup
sudo groupdel mygroup
Password Policy
sudo chage -M 90 -m 7 -W 14 username
sudo chage -l username
sudo passwd -S username
Related Manuals
- 202603161901: Processes run under user identities – ownership matters for signal permissions.
- 202604090000 Linux Storage Management 3 – Filesystems and Mounting#2. Mounting and Unmounting: Mount points respect user/group ownership.
uid=andgid=are mount options. - 202604070826: Container users are mapped to host UIDs via subuid/subgid.