
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
Managing identities on a production server is the first line of defense against unauthorized access and compliance failures. This Ubuntu User Management Guide provides the exact commands and configuration patterns needed to securely create accounts, enforce least-privilege sudo access, and automate offboarding. Whether you are securing a fresh VPS or hardening an existing fleet, these practices align with modern DevOps standards and audit requirements like SOC 2.
adduser, granting least-privilege sudo access through specific /etc/sudoers.d/ drop-in files, enforcing SSH key-only authentication, and managing permissions via functional groups rather than direct user assignments to ensure auditability and security.How do you securely create users in this Ubuntu User Management Guide?
The distinction between adduser and useradd matters significantly in production environments. While useradd is the low-level binary that simply modifies system files, adduser is a Perl wrapper that interactively creates home directories, copies skeleton files from /etc/skel, and sets initial permissions correctly. For manual administration, always prefer adduser to avoid missing critical setup steps that could leave an account misconfigured or insecure.
Creating a standard user with proper defaults
# Create a user with home directory and default shell
sudo adduser deployer
# Verify home directory and skeleton files were created
ls -la /home/deployer/
# Set password expiration policy immediately
sudo chage -M 90 -W 14 deployer In automated pipelines or cloud-init scripts where interactivity is impossible, use useradd with explicit flags. This approach is common when provisioning infrastructure via Terraform or Ansible, as discussed in our Ansible server automation guide. Always specify the shell and groups explicitly to avoid inheriting unexpected defaults.
Non-interactive user creation for automation
# Create system user for services (no login shell, no home)
sudo useradd -r -s /usr/sbin/nologin app-service
# Create regular user non-interactively with specific groups
sudo useradd -m -s /bin/bash -G sudo,docker developer
echo "developer:TempPass123!" | sudo chpasswd
# Force password change on first login
sudo chage -d 0 developer A common mistake is creating users without verifying their group memberships afterward. Run id username immediately after creation to confirm the account has exactly the intended groups and nothing more. Excessive group membership at creation time violates least-privilege principles and complicates future access reviews.
How should sudo privileges be configured for least-privilege access?
Never edit /etc/sudoers directly. Always use visudo or, preferably, create drop-in files under /etc/sudoers.d/. Drop-in files allow modular permission management, make version control easier, and prevent a single syntax error from locking you out of the entire system. Each file should correspond to a specific role or team, not individual users.
Role-based sudoers drop-in configuration
# Create a role-specific sudoers file
sudo visudo -f /etc/sudoers.d/web-deployers
# Allow web deploys to restart nginx and php-fpm without password
%web-deployers ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx, \
/usr/bin/systemctl restart php8.3-fpm, \
/usr/bin/systemctl status nginx, \
/usr/bin/systemctl status php8.3-fpm
# Verify syntax before saving (visudo does this automatically)
sudo visudo -c -f /etc/sudoers.d/web-deployers Restrict commands to absolute paths and specific arguments whenever possible. Allowing systemctl without arguments grants full service control; allowing only /usr/bin/systemctl restart nginx limits the scope precisely. Avoid wildcards in command specifications unless absolutely necessary, as they can be exploited for privilege escalation through path manipulation or argument injection.
What is the difference between primary and supplementary groups?
Understanding group types prevents permission nightmares. Every user has exactly one primary group (defined in /etc/passwd) that owns files they create by default. Supplementary groups (listed in /etc/group) grant additional access to shared resources. In modern Ubuntu, each user typically gets a private primary group matching their username, while functional access comes through supplementary groups like docker, www-data, or custom roles.
| Aspect | Primary Group | Supplementary Groups |
|---|---|---|
| Definition Location | /etc/passwd (GID field) | /etc/group (member list) |
| Count Per User | Exactly one | Zero to many |
| Default File Ownership | Yes (new files inherit this GID) | No (unless SGID bit set on directory) |
| Modification Command | usermod -g groupname user | usermod -aG groupname user |
| Best Practice | Private user group (U=PG) | Functional roles (docker, deploy, dba) |
Managing group memberships safely
# Add user to supplementary groups WITHOUT removing existing ones
sudo usermod -aG docker,www-data developer
# Replace ALL supplementary groups (dangerous - removes existing)
sudo usermod -G docker developer
# Change primary group (rarely needed)
sudo usermod -g contractors freelancer
# Verify final group membership
groups developer
id developer The -a flag in usermod -aG is critical. Omitting it replaces all supplementary groups instead of appending, which can silently revoke access to essential services. I have seen this cause production outages when developers lost docker or ssh-users membership during routine maintenance. Always verify with id after modification, and consider automating group management via configuration management tools to prevent drift.
How do you enforce SSH key authentication and disable passwords?
Password authentication is incompatible with secure production infrastructure. After completing your initial Ubuntu server setup, disable password logins entirely and enforce key-based authentication. This eliminates brute-force attack vectors and enables centralized key rotation without coordinating password changes across teams.
Hardening SSHD configuration
# Edit SSH daemon configuration
sudo nano /etc/ssh/sshd_config.d/99-hardening.conf
# Apply these settings
PasswordAuthentication no
ChallengeResponseAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
AllowGroups ssh-users admins
# Validate configuration before restarting
sudo sshd -t
sudo systemctl reload sshd Use AllowGroups instead of AllowUsers for scalability. Managing individual usernames in SSH config becomes unmanageable beyond a handful of accounts. Create a dedicated ssh-users group and add authorized personnel to it. This integrates cleanly with your group-based access model and simplifies both onboarding and offboarding workflows.
Deploying SSH keys securely
# From your LOCAL machine (not the server)
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]
# Or manually for automation contexts
cat ~/.ssh/id_ed25519.pub | ssh [email protected] \
"mkdir -p ~/.ssh && chmod 700 ~/.ssh && \
cat >> ~/.ssh/authorized_keys && \
chmod 600 ~/.ssh/authorized_keys"
# Verify key permissions on server
ls -la /home/deployer/.ssh/ Never generate SSH keys on the server and copy private keys elsewhere. Keys should originate on the user's workstation or in a secrets manager, with only public keys deployed to servers. For CI/CD pipelines, use short-lived certificates via HashiCorp Vault or AWS Systems Manager Session Manager instead of long-lived static keys, as covered in our secrets management guide.
What is the correct procedure for user offboarding and auditing?
Offboarding is where most Ubuntu user management fails. Simply deleting an account leaves orphaned files, running processes, and cron jobs that become security liabilities. A proper offboarding workflow preserves evidence for audits, terminates active sessions immediately, and archives data before removal. This discipline is essential for SOC 2 compliance and incident forensics.
Complete offboarding script
#!/bin/bash
# offboard-user.sh - Safe user deprovisioning
USERNAME=$1
ARCHIVE_DIR="/var/archive/users"
if [ -z "$USERNAME" ]; then echo "Usage: $0 username"; exit 1; fi
# Step 1: Immediately lock and terminate sessions
sudo usermod -L "$USERNAME"
sudo pkill -u "$USERNAME"
sudo loginctl terminate-user "$USERNAME"
# Step 2: Archive home directory and mail spool
sudo mkdir -p "$ARCHIVE_DIR"
sudo tar czf "$ARCHIVE_DIR/${USERNAME}-$(date +%Y%m%d).tar.gz" \
/home/"$USERNAME" /var/mail/"$USERNAME" 2>/dev/null
# Step 3: Export audit logs for this user
sudo journalctl _UID=$(id -u "$USERNAME") --since "30 days ago" \
> "$ARCHIVE_DIR/${USERNAME}-journal.log"
sudo grep "$USERNAME" /var/log/auth.log \
>> "$ARCHIVE_DIR/${USERNAME}-auth.log"
# Step 4: Remove from all supplementary groups
for GROUP in $(groups "$USERNAME" | cut -d: -f2); do
sudo gpasswd -d "$USERNAME" "$GROUP" 2>/dev/null
done
# Step 5: Remove sudoers drop-in if user-specific
sudo rm -f "/etc/sudoers.d/$USERNAME"
# Step 6: Delete user (retain home if archiving separately)
sudo deluser --remove-home "$USERNAME"
echo "Offboarding complete. Archive: $ARCHIVE_DIR/${USERNAME}-*" Run regular access reviews using getent passwd and getent group to identify stale accounts. Automate this check weekly and alert on accounts inactive for more than 30 days. For teams managing multiple servers, centralize identity via LDAP or integrate with your cloud provider's IAM to avoid local account sprawl entirely. The goal is making every access decision traceable, reversible, and auditable.
Implementing Sustainable Ubuntu User Management
Effective Ubuntu user management is not about memorizing commands—it is about building repeatable, auditable processes that scale with your team. Start by codifying user creation and offboarding in Ansible playbooks or Terraform modules. Enforce SSH key-only authentication from day one. Use group-based access controls instead of individual permissions. And treat every account change as a compliance event worth logging. If your current user management relies on tribal knowledge or manual intervention, schedule a review. Reach out via my contact page to discuss hardening your Linux infrastructure for production workloads and audit readiness.