Learn to manage Linux users and groups with useradd, usermod, and groupadd commands. This tutorial covers creating accounts, setting permissions, configuring password policies, and implementing security best practices for production systems.
Prerequisites
- Root or sudo access
- Basic Linux command line knowledge
What this solves
Linux user and group management is essential for system security and access control in production environments. This tutorial teaches you to create, modify, and secure user accounts using native Linux commands like useradd, usermod, and groupadd, while implementing proper permission structures and password policies.
Understanding Linux users and groups
Linux uses a multi-user system where each user has a unique identifier (UID) and belongs to one or more groups (GID). The system distinguishes between system users (UID 1-999) for services and regular users (UID 1000+) for human access.
Key files that control user management include:
- /etc/passwd - User account information
- /etc/shadow - Encrypted passwords and password policies
- /etc/group - Group definitions and memberships
- /etc/gshadow - Group passwords and administrators
Step-by-step user management setup
Install user management utilities
Install additional user management tools that provide enhanced functionality beyond the basic commands.
sudo apt update
sudo apt install -y passwd adduser libpam-pwquality
Create a new user with useradd
Create a basic user account with useradd. This command provides fine-grained control over user creation parameters.
sudo useradd -m -s /bin/bash -c "Application User" appuser
sudo passwd appuser
The -m flag creates a home directory, -s sets the shell, and -c adds a comment describing the user.
Create a user with specific UID and group
For system consistency, you may need to specify exact UID values and primary groups.
sudo groupadd -g 2001 developers
sudo useradd -m -u 2001 -g developers -s /bin/bash -c "Developer Account" devuser
sudo passwd devuser
Create a system user for services
System users run services and applications without login capabilities. They use lower UID ranges and typically have no home directory or shell access.
sudo useradd -r -s /usr/sbin/nologin -c "Web Service User" -d /var/lib/webservice webservice
sudo mkdir -p /var/lib/webservice
sudo chown webservice:webservice /var/lib/webservice
sudo chmod 755 /var/lib/webservice
Configure user account expiration
Set account expiration dates for temporary users or security compliance.
sudo useradd -m -s /bin/bash -e 2024-12-31 -c "Temporary User" tempuser
sudo passwd tempuser
sudo chage -l tempuser
Modify existing users with usermod
Use usermod to change user properties after creation, such as adding to groups or changing the home directory.
sudo usermod -aG sudo,developers appuser
sudo usermod -s /bin/zsh appuser
sudo usermod -l newname appuser
sudo usermod -d /home/newname -m newname
Create and manage groups
Groups organize users and simplify permission management. Create groups for different roles and responsibilities.
sudo groupadd webadmins
sudo groupadd -g 3001 dbusers
sudo gpasswd -a appuser webadmins
sudo gpasswd -a devuser dbusers
sudo groups appuser
Configure password policies
Set strong password requirements and aging policies for enhanced security.
minlen = 12
minclass = 3
maxrepeat = 2
maxsequence = 3
dcredit = -1
ucredit = -1
lcredit = -1
ocredit = -1
dictcheck = 1
Configure password aging policies
Set password expiration and change requirements using chage command.
sudo chage -M 90 -m 7 -W 14 appuser
sudo chage -I 30 appuser
sudo chage -l appuser
This sets maximum age to 90 days, minimum age to 7 days, warning period to 14 days, and inactive period to 30 days.
Set default user creation policies
Configure system-wide defaults for new user creation.
GROUP=100
HOME=/home
INACTIVE=30
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes
Configure login definitions
Set system-wide login policies including UID ranges and password aging.
PASS_MAX_DAYS 90
PASS_MIN_DAYS 7
PASS_WARN_AGE 14
UID_MIN 1000
UID_MAX 60000
GID_MIN 1000
GID_MAX 60000
CREATE_HOME yes
UMASK 022
Advanced user account security
Lock and unlock user accounts
Temporarily disable accounts without deleting them for security incidents or maintenance.
sudo usermod -L appuser
sudo passwd -l appuser
sudo usermod -U appuser
sudo passwd -u appuser
Monitor user login activity
Track user login attempts and system access for security monitoring.
sudo lastlog
sudo last -10
sudo faillog -u appuser
sudo faillog -r -u appuser
Set user resource limits
Configure system resource limits to prevent resource exhaustion attacks. This integrates with our user session limits tutorial for comprehensive resource management.
appuser soft nproc 100
appuser hard nproc 150
appuser soft nofile 1024
appuser hard nofile 2048
@developers soft nproc 200
@developers hard nproc 300
Configure sudo access
Grant specific users administrative privileges with controlled access.
sudo visudo
appuser ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx
devuser ALL=(ALL) PASSWD: /usr/sbin/service, /usr/bin/systemctl
%webadmins ALL=(ALL) /var/log/, /usr/sbin/logrotate
Verify your user management setup
Test your user configuration and verify security policies are working correctly.
id appuser
groups appuser
sudo chage -l appuser
sudo passwd -S appuser
sudo faillog -u appuser
getent passwd appuser
getent group developers
sudo -l -U appuser
Check that proper file permissions are set for user directories:
ls -la /home/appuser
sudo ls -la /var/lib/webservice
stat -c "%n %a %U:%G" /home/appuser
File permissions and ownership best practices
Understanding Linux file permissions is crucial for secure user management. The permission system uses three categories: owner, group, and others, with read (4), write (2), and execute (1) permissions.
Common permission patterns for user directories:
- User home directories: 755 (drwxr-xr-x) - Owner full access, others can read and execute
- Private user files: 644 (-rw-r--r--) - Owner read/write, others read-only
- Executable scripts: 755 (-rwxr-xr-x) - Owner full access, others read and execute
- SSH private keys: 600 (-rw-------) - Owner read/write only
- Shared group directories: 2775 (drwxrwsr-x) - Group sticky bit for shared access
For more detailed information on file permissions, see our file permissions tutorial.
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| useradd: user already exists | Username conflicts with existing user | Check with getent passwd username and choose different name |
| Permission denied accessing user files | Incorrect ownership or permissions | Fix with sudo chown user:group file and proper chmod |
| User cannot login | Account locked or expired | Check with sudo chage -l username and unlock with sudo usermod -U username |
| Group membership not working | User not properly added to group | Add with sudo gpasswd -a username groupname and user must re-login |
| Password policy not enforced | PAM modules not configured | Install libpam-pwquality and configure /etc/security/pwquality.conf |
| SSH key authentication fails | Wrong permissions on .ssh directory | Set chmod 700 ~/.ssh and chmod 600 ~/.ssh/id_rsa |
| Sudo access denied | User not in sudoers configuration | Add user to sudo group or configure in /etc/sudoers.d/ |
| Home directory not created | Missing -m flag in useradd | Create manually with sudo mkdir /home/user && sudo chown user:user /home/user |
Next steps
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration variables
APP_USER="${1:-appuser}"
DEV_USER="${2:-devuser}"
SERVICE_USER="${3:-webservice}"
# Usage function
usage() {
echo "Usage: $0 [app_user] [dev_user] [service_user]"
echo "Default users: appuser, devuser, webservice"
echo "Example: $0 myapp mydev myservice"
exit 1
}
# Logging functions
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Error handling and cleanup
cleanup() {
log_warning "Script interrupted. Cleaning up..."
# Remove any partially created users if they exist
for user in "$APP_USER" "$DEV_USER" "$SERVICE_USER" tempuser; do
if id "$user" &>/dev/null; then
userdel -r "$user" 2>/dev/null || true
fi
done
# Remove created groups
for group in developers webadmins dbusers; do
groupdel "$group" 2>/dev/null || true
done
}
trap cleanup ERR
# Check if running as root
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root"
exit 1
fi
# Auto-detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PAM_PWQUALITY_CONF="/etc/security/pwquality.conf"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PAM_PWQUALITY_CONF="/etc/security/pwquality.conf"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PAM_PWQUALITY_CONF="/etc/security/pwquality.conf"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
else
log_error "Cannot detect distribution (/etc/os-release not found)"
exit 1
fi
log_info "Detected distribution: $ID using $PKG_MGR"
# Main installation process
echo "[1/10] Updating package manager and installing user management utilities..."
case "$PKG_MGR" in
apt)
apt update
$PKG_INSTALL passwd adduser libpam-pwquality
;;
dnf|yum)
$PKG_INSTALL passwd shadow-utils libpwquality
;;
esac
log_success "User management utilities installed"
echo "[2/10] Creating groups for user organization..."
groupadd -g 2001 developers 2>/dev/null || log_warning "Group 'developers' already exists"
groupadd webadmins 2>/dev/null || log_warning "Group 'webadmins' already exists"
groupadd -g 3001 dbusers 2>/dev/null || log_warning "Group 'dbusers' already exists"
log_success "Groups created successfully"
echo "[3/10] Creating application user: $APP_USER..."
if ! id "$APP_USER" &>/dev/null; then
useradd -m -s /bin/bash -c "Application User" "$APP_USER"
echo "$APP_USER:$(openssl rand -base64 12)" | chpasswd
usermod -aG developers,webadmins "$APP_USER"
log_success "Application user $APP_USER created"
else
log_warning "User $APP_USER already exists"
fi
echo "[4/10] Creating developer user: $DEV_USER..."
if ! id "$DEV_USER" &>/dev/null; then
useradd -m -u 2001 -g developers -s /bin/bash -c "Developer Account" "$DEV_USER"
echo "$DEV_USER:$(openssl rand -base64 12)" | chpasswd
gpasswd -a "$DEV_USER" dbusers
log_success "Developer user $DEV_USER created"
else
log_warning "User $DEV_USER already exists"
fi
echo "[5/10] Creating system service user: $SERVICE_USER..."
if ! id "$SERVICE_USER" &>/dev/null; then
useradd -r -s /usr/sbin/nologin -c "Web Service User" -d "/var/lib/$SERVICE_USER" "$SERVICE_USER"
mkdir -p "/var/lib/$SERVICE_USER"
chown "$SERVICE_USER:$SERVICE_USER" "/var/lib/$SERVICE_USER"
chmod 755 "/var/lib/$SERVICE_USER"
log_success "System service user $SERVICE_USER created"
else
log_warning "User $SERVICE_USER already exists"
fi
echo "[6/10] Creating temporary user with expiration..."
if ! id tempuser &>/dev/null; then
useradd -m -s /bin/bash -e 2024-12-31 -c "Temporary User" tempuser
echo "tempuser:$(openssl rand -base64 12)" | chpasswd
log_success "Temporary user created with expiration"
else
log_warning "User tempuser already exists"
fi
echo "[7/10] Configuring password quality policies..."
if [ -f "$PAM_PWQUALITY_CONF" ]; then
# Backup original config
cp "$PAM_PWQUALITY_CONF" "$PAM_PWQUALITY_CONF.backup"
# Configure password quality
cat > "$PAM_PWQUALITY_CONF" << 'EOF'
# Minimum password length
minlen = 12
# Minimum number of character classes
minclass = 3
# Maximum number of repeated characters
maxrepeat = 2
# Maximum length of sequence
maxsequence = 3
# Require at least one digit
dcredit = -1
# Require at least one uppercase letter
ucredit = -1
# Require at least one lowercase letter
lcredit = -1
# Require at least one special character
ocredit = -1
# Enable dictionary check
dictcheck = 1
EOF
chmod 644 "$PAM_PWQUALITY_CONF"
log_success "Password quality policies configured"
else
log_warning "Password quality config file not found at $PAM_PWQUALITY_CONF"
fi
echo "[8/10] Configuring password aging policies..."
chage -M 90 -m 7 -W 14 "$APP_USER" 2>/dev/null || true
chage -I 30 "$APP_USER" 2>/dev/null || true
chage -M 90 -m 7 -W 14 "$DEV_USER" 2>/dev/null || true
chage -I 30 "$DEV_USER" 2>/dev/null || true
log_success "Password aging policies configured"
echo "[9/10] Setting default user creation policies..."
# Backup original files
cp /etc/default/useradd /etc/default/useradd.backup 2>/dev/null || true
cp /etc/login.defs /etc/login.defs.backup 2>/dev/null || true
# Configure useradd defaults
cat > /etc/default/useradd << 'EOF'
GROUP=100
HOME=/home
INACTIVE=30
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes
EOF
chmod 644 /etc/default/useradd
# Configure login definitions
sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 90/' /etc/login.defs
sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS 7/' /etc/login.defs
sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE 14/' /etc/login.defs
log_success "Default user creation policies configured"
echo "[10/10] Verifying configuration..."
# Verify users exist
for user in "$APP_USER" "$DEV_USER" "$SERVICE_USER" tempuser; do
if id "$user" &>/dev/null; then
log_success "User $user exists"
else
log_error "User $user not found"
fi
done
# Verify groups exist
for group in developers webadmins dbusers; do
if getent group "$group" &>/dev/null; then
log_success "Group $group exists"
else
log_error "Group $group not found"
fi
done
# Display group memberships
log_info "Group memberships:"
echo " $APP_USER groups: $(groups $APP_USER 2>/dev/null || echo 'N/A')"
echo " $DEV_USER groups: $(groups $DEV_USER 2>/dev/null || echo 'N/A')"
# Display password aging info
log_info "Password aging for $APP_USER:"
chage -l "$APP_USER" 2>/dev/null || log_warning "Cannot display aging info"
log_success "Linux user and group management configuration completed!"
log_info "Remember to:"
log_info " - Set strong passwords for user accounts"
log_info " - Review and adjust group memberships as needed"
log_info " - Monitor account usage and disable unused accounts"
log_info " - Regularly audit user permissions"
# Clean up trap on successful completion
trap - ERR
Review the script before running. Execute with: bash install.sh