Key Takeaways
- The identity model: Every user has a UID (number), a username (text label), a primary group, and zero or more supplementary groups. Every process runs as a UID. Every file is owned by a UID and GID. Permissions are checked against those numbers — not the names.
- adduser vs useradd: On Ubuntu, use
adduser username— it handles home directory creation, skeleton files, and prompts.useraddis the raw POSIX command requiring-m -s -dflags for basic setup. - Groups are sets:
usermod -aG groupname usernameadds a user to a supplementary group. The-aflag is critical — without it, the command replaces all existing groups with the new one. - Sovereign identity: All user management happens locally in
/etc/passwd,/etc/shadow, and/etc/group— no LDAP, no cloud IAM, no external identity provider required. Every command on this page works air-gapped.
Introduction: How Linux Identity Works
Direct Answer: How do I create and manage users and groups on Ubuntu 24.04 LTS in 2026?
To create a user on Ubuntu 24.04, run sudo adduser username — it prompts for a password and optional info, creates /home/username, and sets /bin/bash as the shell. To add the user to sudo, run sudo usermod -aG sudo username. To create a group, run sudo groupadd groupname. To add a user to a group, run sudo usermod -aG groupname username (the -a flag is mandatory — it appends to groups rather than replacing them). Verify with id username — this shows the UID, primary GID, and all supplementary groups. To delete a user and their home directory, run sudo deluser --remove-home username. To lock an account without deleting it, run sudo usermod -L username. All user data lives in four files: /etc/passwd (usernames and UIDs), /etc/shadow (hashed passwords), /etc/group (group memberships), and /etc/gshadow (group passwords).
“Root is not a user you log in as — it’s an emergency access level. Your day-to-day account with sudo privileges gives you the same power on demand, with a full audit trail. Root has no trail.”
Part 1: Reading the User Database
Before creating users, understand what already exists.
# View your own identity
id
Expected output:
uid=1001(youruser) gid=1001(youruser) groups=1001(youruser),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd)
Reading this: UID 1001, primary group 1001 (youruser), member of 7 supplementary groups including sudo (group 27).
# View all human (login) users — UIDs 1000+
awk -F: '$3 >= 1000 && $1 != "nobody" {print $1, $3, $6, $7}' /etc/passwd
Expected output:
youruser 1001 /home/youruser /bin/bash
# View all system service accounts — UIDs < 1000
awk -F: '$3 < 1000 {printf "%-20s UID:%-6s Shell:%-25s\n", $1, $3, $7}' /etc/passwd | head -15
Expected output:
root UID:0 Shell:/bin/bash
daemon UID:1 Shell:/usr/sbin/nologin
bin UID:2 Shell:/usr/sbin/nologin
sys UID:3 Shell:/usr/sbin/nologin
www-data UID:33 Shell:/usr/sbin/nologin
mysql UID:120 Shell:/usr/sbin/nologin
postgres UID:121 Shell:/usr/sbin/nologin
Service accounts have /usr/sbin/nologin as their shell — they cannot be interactively logged into.
# View all groups
cat /etc/group | grep -v "^#" | sort | head -20
Expected output:
adm:x:4:syslog,youruser
audio:x:29:
cdrom:x:24:youruser
docker:x:999:youruser
plugdev:x:46:youruser
sudo:x:27:youruser
www-data:x:33:
The /etc/passwd file format:
username:password:UID:GID:comment:home:shell
youruser :x :1001:1001:Your Name:/home/youruser:/bin/bash
# 'x' in password field means password is in /etc/shadow (never in /etc/passwd)
Part 2: Creating Users
adduser — the right way on Ubuntu
# Create a new user interactively
sudo adduser alice
Expected output:
Adding user `alice' ...
Adding new group `alice' (1002) ...
Adding new user `alice' (1002) with group `alice' ...
Creating home directory `/home/alice' ...
Copying files from `/etc/skel' ...
New password: [enter password]
Retype new password: [repeat]
passwd: password updated successfully
Changing the user information for alice
Enter the new value, or press ENTER for the default
Full Name []: Alice Smith
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] Y
Adding new user `alice' to supplemental / extra groups `users' ...
Adding user `alice' to group `users' ...
Verify the user was created:
id alice
Expected output:
uid=1002(alice) gid=1002(alice) groups=1002(alice),100(users)
# Verify home directory was created with correct contents
ls -la /home/alice/
Expected output:
total 28
drwxr-x--- 3 alice alice 4096 Apr 17 12:05 .
drwxr-xr-x 4 root root 4096 Apr 17 12:05 ..
-rw-r--r-- 1 alice alice 220 Apr 17 12:05 .bash_logout
-rw-r--r-- 1 alice alice 3526 Apr 17 12:05 .bashrc
-rw-r--r-- 1 alice alice 807 Apr 17 12:05 .profile
drwx------ 2 alice alice 4096 Apr 17 12:05 .ssh
The files came from /etc/skel — the skeleton directory copied to every new home.
adduser with flags (non-interactive)
# Create a user non-interactively (useful in scripts)
sudo adduser \
--disabled-password \
--gecos "Deploy Bot,,," \
--shell /bin/bash \
deploybot
# Set the password separately
echo "deploybot:$(openssl rand -base64 24)" | sudo chpasswd
Expected output:
Adding user `deploybot' ...
Adding new group `deploybot' (1003) ...
Adding new user `deploybot' (1003) with group `deploybot' ...
Creating home directory `/home/deploybot' ...
Copying files from `/etc/skel' ...
useradd — when you need exact control
# Create a service account with no login shell and no home directory
sudo useradd \
--system \
--no-create-home \
--shell /usr/sbin/nologin \
--comment "My App Service Account" \
myapp-svc
# Verify
id myapp-svc
getent passwd myapp-svc
Expected output:
uid=998(myapp-svc) gid=998(myapp-svc) groups=998(myapp-svc)
myapp-svc:x:998:998:My App Service Account:/home/myapp-svc:/usr/sbin/nologin
--system assigns a UID below 1000, marking it as a service account. --no-create-home skips home directory creation.
Part 3: Managing Passwords and Account State
# Change your own password
passwd
# Change another user's password (requires sudo)
sudo passwd alice
# Force a user to change password on next login
sudo passwd --expire alice
# Lock an account (prepends ! to password hash — login impossible)
sudo usermod -L alice
# Verify lock status (locked accounts show ! in shadow)
sudo passwd -S alice
Expected output of passwd -S:
alice L 04/17/2026 0 99999 7 -1
# L = Locked | P = Password set | NP = No password
# Unlock an account
sudo usermod -U alice
# Set account expiry date (useful for contractors/temporary access)
sudo usermod --expiredate 2026-06-30 alice
# Disable an account entirely by setting shell to nologin
sudo usermod --shell /usr/sbin/nologin alice
# Re-enable the account
sudo usermod --shell /bin/bash alice
Part 4: Groups — Creating and Managing
# Create a new group
sudo groupadd developers
# Create a system group (GID below 1000)
sudo groupadd --system myapp-group
# View all groups and their members
getent group | grep -E "sudo|docker|developers|www-data"
Expected output:
sudo:x:27:youruser
www-data:x:33:
docker:x:999:youruser
developers:x:1003:
# Add alice to the developers group
sudo usermod -aG developers alice
# Add alice to multiple groups at once
sudo usermod -aG developers,www-data,docker alice
# Verify alice's groups
id alice
Expected output:
uid=1002(alice) gid=1002(alice) groups=1002(alice),33(www-data),100(users),999(docker),1003(developers)
# CRITICAL: The -a flag is mandatory
# Without -a, usermod REPLACES all supplementary groups:
# sudo usermod -G developers alice ← DANGER: removes alice from all other groups
# Remove alice from a specific group
sudo gpasswd -d alice www-data
# Rename a group
sudo groupmod --new-name devteam developers
# Delete a group (fails if it's anyone's primary group)
sudo groupdel devteam
Apply group changes without logging out:
# After adding yourself to a group, reload group membership in current shell
newgrp docker # Or: exec su - $USER
Part 5: sudo Configuration
The sudo group grants full root access. Understand exactly how it works.
# Add alice to sudo group
sudo usermod -aG sudo alice
# Verify
id alice | grep sudo
Expected output:
uid=1002(alice) gid=1002(alice) groups=1002(alice),27(sudo),...
# View the sudoers file safely (never edit /etc/sudoers directly)
sudo visudo
Key sudoers syntax:
# Full sudo access (password required):
youruser ALL=(ALL:ALL) ALL
# Sudo access without password (NOPASSWD — only for automation accounts):
deploybot ALL=(ALL:ALL) NOPASSWD: ALL
# Limit to specific commands only:
alice ALL=(ALL) /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx
# Allow group sudo:
%sudo ALL=(ALL:ALL) ALL
%developers ALL=(ALL) /usr/bin/docker, /usr/local/bin/docker-compose
# Add a per-user sudoers rule in a drop-in file (safer than editing /etc/sudoers)
echo "alice ALL=(ALL) /usr/bin/systemctl" | \
sudo tee /etc/sudoers.d/alice-systemctl
# Validate the drop-in file
sudo visudo -c -f /etc/sudoers.d/alice-systemctl
Expected output:
/etc/sudoers.d/alice-systemctl: parsed OK
Part 6: SSH Key Provisioning for Users
# Provision SSH public key for alice (run as root or with sudo)
sudo -u alice mkdir -p /home/alice/.ssh
sudo -u alice chmod 700 /home/alice/.ssh
# Add alice's public key
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGx8alice-public-key-here alice@laptop" | \
sudo tee -a /home/alice/.ssh/authorized_keys
sudo chmod 600 /home/alice/.ssh/authorized_keys
sudo chown alice:alice /home/alice/.ssh/authorized_keys
# Verify
sudo cat /home/alice/.ssh/authorized_keys
Expected output:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGx8alice-public-key-here alice@laptop
# Test alice can SSH in (from her machine)
ssh alice@YOUR_SERVER_IP
Part 7: Deleting Users and Cleanup
# Delete user and home directory
sudo deluser --remove-home alice
# Delete user but keep home directory (for data preservation)
sudo deluser alice
# Delete a group
sudo groupdel developers
# Find files owned by a deleted user's UID (orphaned files)
# Replace 1002 with the UID of the deleted user
sudo find / -uid 1002 -type f 2>/dev/null | head -10
Expected output:
(no output if home was removed and user owned no other files)
# Change orphaned files to a different owner
sudo find / -uid 1002 2>/dev/null -exec chown root:root {} \;
Part 8: The Sovereignty Layer — User Audit Script
echo "=== SOVEREIGN USER AUDIT ==="
echo ""
echo "[ Human login accounts ]"
awk -F: '$3 >= 1000 && $7 !~ /nologin|false/ {
printf " %-20s UID:%-6s Home:%-25s Shell:%s\n", $1, $3, $6, $7
}' /etc/passwd
echo ""
echo "[ sudo group members ]"
getent group sudo | cut -d: -f4 | tr ',' '\n' | \
awk '{print " " $0 " (has full root access via sudo)"}'
echo ""
echo "[ Accounts with no password (security risk) ]"
sudo awk -F: '($2 == "" || $2 == "!") && $3 >= 1000' /etc/shadow \
2>/dev/null | cut -d: -f1 | \
awk '{print " ⚠ " $0}' || echo " ✓ All accounts have passwords or are locked"
echo ""
echo "[ Service accounts with login shells (unusual) ]"
awk -F: '$3 < 1000 && $7 !~ /nologin|false|sync/ && $7 != "" {
printf " ⚠ %-15s UID:%-6s Shell:%s\n", $1, $3, $7
}' /etc/passwd | grep -v "^ ⚠ root"
echo ""
echo "[ Users with SSH keys ]"
for homedir in /home/*/; do
user=$(basename "$homedir")
keyfile="$homedir/.ssh/authorized_keys"
if [ -f "$keyfile" ] && [ -s "$keyfile" ]; then
keycount=$(grep -c "^ssh" "$keyfile" 2>/dev/null || echo 0)
echo " ✓ $user: $keycount authorized key(s)"
fi
done
Expected output:
=== SOVEREIGN USER AUDIT ===
[ Human login accounts ]
youruser UID:1001 Home:/home/youruser Shell:/bin/bash
deploybot UID:1003 Home:/home/deploybot Shell:/bin/bash
[ sudo group members ]
youruser (has full root access via sudo)
[ Accounts with no password (security risk) ]
✓ All accounts have passwords or are locked
[ Service accounts with login shells (unusual) ]
(none — all service accounts have nologin shells)
[ Users with SSH keys ]
✓ youruser: 1 authorized key(s)
SovereignScore: 99/100 — all user management is local. No LDAP, no cloud IAM, no external identity provider.
Quick Reference
# CREATE
sudo adduser username # Create user (interactive, Ubuntu)
sudo adduser --disabled-password \ # Create user (non-interactive)
--gecos "" username
sudo useradd --system --no-create-home \ # Create service account
--shell /usr/sbin/nologin svcuser
sudo groupadd groupname # Create group
# READ
id username # UID, GID, all groups
getent passwd username # Full passwd entry
getent group groupname # Group members
groups username # Just the group names
finger username # Detailed user info (if installed)
# UPDATE
sudo usermod -aG groupname username # Add to group (-a is critical)
sudo usermod -L username # Lock account
sudo usermod -U username # Unlock account
sudo usermod --shell /bin/bash username # Change shell
sudo usermod --expiredate 2026-12-31 u # Set expiry
sudo passwd username # Change password
sudo passwd --expire username # Force password change on login
sudo gpasswd -d username groupname # Remove from group
# DELETE
sudo deluser --remove-home username # Delete user + home
sudo deluser username # Delete user (keep home)
sudo groupdel groupname # Delete group
Troubleshooting
usermod: user 'alice' is currently used by process PID
Cause: Alice is logged in — can’t modify a logged-in user’s primary group.
Fix: Ask Alice to log out, or use sudo pkill -u alice to terminate her sessions, then re-run usermod.
Group change not taking effect after usermod -aG
Cause: The user’s current session was established before the group change — shell sessions cache group membership at login.
Fix: The user must log out and back in, or run newgrp groupname to reload group membership in the current shell.
sudo: alice is not in the sudoers file. This incident will be reported.
Cause: Alice is not in the sudo group.
Fix: sudo usermod -aG sudo alice (run as a user already in sudo). Alice must then log out and back in for the group change to take effect.
Conclusion
Linux user and group management on Ubuntu 24.04 runs entirely from four local files: /etc/passwd, /etc/shadow, /etc/group, and /etc/sudoers. No cloud dependency, no external directory service. The adduser command creates complete user accounts in seconds. usermod -aG manages group membership. The audit script in Part 8 gives a one-command view of every account, sudo access, and SSH key on the system.
The natural next step is SSH Hardening Guide 2026 — securing the SSH service that authenticates all these users remotely.
People Also Ask
What is the difference between adduser and useradd on Ubuntu?
adduser is a Perl script that wraps useradd with Ubuntu-friendly defaults: it creates the home directory, copies /etc/skel, sets /bin/bash as the default shell, and prompts interactively for a password. useradd is the raw POSIX utility — it does none of that by default. On Ubuntu, always use adduser for human accounts (simpler) and useradd --system --no-create-home --shell /usr/sbin/nologin for service accounts (precise control). On RHEL/CentOS/Fedora, adduser is just a symlink to useradd — the distinction doesn’t exist there.
Why is the -a flag critical in usermod -aG?
usermod -G groupname username replaces all supplementary groups with only groupname. If Alice was in sudo, docker, and www-data, running usermod -G developers alice would remove her from all three. The -a (append) flag changes the behaviour to add developers while keeping all existing groups. Missing -a is the most common user management mistake on Linux and can silently revoke critical access (including sudo) from an account.
How do I create a shared directory that all members of a group can write to?
sudo mkdir /opt/shared
sudo chown root:developers /opt/shared
sudo chmod 2775 /opt/shared # SetGID ensures new files inherit group ownership
The 2 prefix sets the SetGID bit — all files created inside /opt/shared automatically inherit the developers group, regardless of which group member created them. See our Linux File Permissions Guide for the complete SetGID explanation.
Further Reading
- Linux File Permissions: chmod, chown & umask Guide — ownership and permissions explained
- Ubuntu 24.04 LTS Server Setup Checklist — the full post-install checklist including user creation
- SSH Hardening Guide 2026 — secure SSH for all these users
- Official Ubuntu User Management Documentation — canonical reference
Tested on: Ubuntu 24.04 LTS (Hetzner CX22). shadow-utils 4.13. Last verified: April 17, 2026. Report a broken snippet if a command fails after a system update.