Managing Linux Users and Groups
AI generated
$
/etc
Linux · User Management · Server Security · Administration
Managing Linux Users and Groups
from useradd to a systematic audit

Running Linux servers with shared logins and unmaintained accounts risks security gaps that only surface after an incident. This article explains useradd, usermod and groupadd from the ground up, shows the principle of a dedicated unprivileged user per application and provides a practical workflow to systematically audit existing users and groups on a server.

16 min read useradd · usermod · groupadd · audit Debian · Ubuntu · RHEL · systemd

1. Why user management decides a server's security

A Linux server where several people share the same root password, or where old accounts of former employees are still active, is structurally insecure no matter how well the firewall and patch level are maintained. Every user and every group on a system represents a permission boundary. If that boundary is drawn carelessly, it creates exactly the gaps that get exploited for lateral movement through the system during a security incident. Clean user management is therefore not administrative housekeeping, it is one of the most effective security measures available.

In practice, the problem rarely shows up on day one, it accumulates over months or years of operation: a developer gets sudo rights for a deployment on short notice and they are never revoked again. A service runs as root out of convenience because that was faster to set up initially. These small shortcuts add up to a server whose actual permission structure nobody can fully explain anymore. The following sections cover the tools and principles that let you avoid this state from the start, or systematically clean it up after the fact.

2. useradd fundamentals: options, defaults and home directory

The useradd command is the fundamental tool for creating new accounts on Linux, but it works quite minimally without extra options: without -m no home directory is created, without -s the login shell often stays on a system default that does not match the actual intended use. Important options are -m for the home directory, -d for a different path, -s for the shell, -c for a comment with a plain text name, and -G for additional secondary groups right at creation time. The system wide defaults, such as which shell is set without -s, live in /etc/default/useradd and can be shown and adjusted with useradd -D.

Debian based systems additionally offer adduser, an interactive Perl script that wraps useradd with sensible prompts and automatically creates a home directory including skeleton files from /etc/skel. For automated deployments through Ansible or shell scripts, useradd is usually still the better choice because it runs deterministically without interaction and behaves identically across virtually every distribution. Important: every new user needs either an initial password or an explicitly disabled login, otherwise the account stays in an unclear intermediate state.


# Create a regular user with a home directory and bash shell
useradd -m -d /home/jdoe -s /bin/bash -c "Jane Doe" jdoe

# Set the password interactively (never pass it as a plaintext argument)
passwd jdoe

# Create a user directly with secondary groups
useradd -m -s /bin/bash -G sudo,docker jdoe

# Show system wide defaults for new users
useradd -D

# Adjust the home directory template before new users are created
ls -la /etc/skel/

# Create a user without a login shell for pure file access
useradd -m -s /usr/sbin/nologin sftpuser

3. usermod and userdel: maintaining and removing existing accounts

usermod changes properties of an already existing account without requiring a fresh creation. The most common use cases are adding a user to a group with usermod -aG group user, changing the shell with -s, and renaming with -l. The critical option here is -a for append: forget it, and usermod -G replaces all previously assigned secondary groups with the newly given list instead of adding to it. This mistake is one of the most common in Linux administration and quietly removes a user from important groups such as docker or sudo.

To fully remove an account, userdel is the tool, but by default it does not delete the home directory. Only userdel -r additionally removes the home directory and mail spool. Before deleting, it always pays off to check with find / -user username for orphaned files outside the home directory, for example in cron jobs or application directories. If you do not want to delete an account immediately but need it locked, use usermod -L to lock the password or usermod -e 1 to set an immediate expiration date in the past.


# Add a user to a group WITHOUT losing existing groups
usermod -aG docker jdoe

# WRONG: replaces all secondary groups without warning
usermod -G docker jdoe

# Lock an account immediately without deleting it
usermod -L -e 1 jdoe

# Before deleting: find orphaned files outside the home directory
find / -xdev -user jdoe -not -path "/home/jdoe/*" 2>/dev/null

# Remove a user including home directory and mail spool completely
userdel -r jdoe

# Check the current group memberships of a user
id jdoe

4. Managing groups with groupadd, groupmod and gpasswd

Groups combine multiple users into a shared permission unit and are the central tool for controlling file access without maintaining an ACL for every single case. groupadd deploy creates a new group, groupmod -n newname oldname renames it, groupdel removes it again, provided no user still has that group set as their primary group. Every user has exactly one primary group, usually a group of the same name created automatically at account creation, but any number of secondary groups through which additional privileges are granted.

For targeted adding and removing of individual members without touching the rest of the configuration, gpasswd -a user group for adding and gpasswd -d user group for removing are useful, functionally equivalent to usermod -aG but semantically clearer since it puts the group rather than the user at the center of the command. In server environments with clearly separated responsibilities, such as deploy, backup and monitoring, a well thought out group structure often replaces several individual sudo rules with a single group based rule.

5. System users vs. regular users: UID and GID ranges

Linux distinguishes between system users and regular users based on the numeric user ID, not on an explicit flag. On most distributions, UIDs from 0 to 999 are reserved for system users and services, while regular, interactive user accounts start at UID 1000. The exact threshold is defined in /etc/login.defs under UID_MIN and UID_MAX, or SYS_UID_MIN and SYS_UID_MAX, and can vary slightly by distribution, RHEL based systems historically set the boundary at 200 instead of 1000.

System users such as www-data, mysql or redis automatically get a UID from this reserved range through the useradd -r option, usually without their own home directory and without an interactive login shell. This separation is more than cosmetic: backup scripts, monitoring tools and security scanners rely on the UID boundary to distinguish real human accounts from technical service accounts. A system service with a UID above 1000 confuses these automations and makes later audits considerably harder.


# Ansible playbook: provision a dedicated system user per application
- name: Provision application service users
  hosts: webservers
  become: true
  tasks:
    - name: Create system group for PHP-FPM pool
      ansible.builtin.group:
        name: app-shop
        system: true
        state: present

    - name: Create dedicated system user without login shell
      ansible.builtin.user:
        name: app-shop
        group: app-shop
        system: true
        shell: /usr/sbin/nologin
        home: /var/www/shop
        create_home: false
        state: present

    - name: Assign application directory to the dedicated user
      ansible.builtin.file:
        path: /var/www/shop
        owner: app-shop
        group: app-shop
        mode: "0750"
        recurse: true

6. The principle of a dedicated unprivileged user per application

The web server runs as www-data, MySQL as mysql, Redis as redis: this pattern is no coincidence, it is a central security principle. Every application should run under its own unprivileged user that can only access the files and resources this one application actually needs. If a PHP-FPM pool gets compromised through a vulnerability, the dedicated user limits the damage to exactly this application scope instead of automatically handing the attacker access to every other service on the same server.

This principle becomes especially important with multiple applications on the same server, for instance several Magento stores with their own PHP-FPM pools. If every store runs under the same www-data user, a compromised store can access the files of every other store, because file system permissions provide no separation. The solution: a dedicated system user per application, its own PHP-FPM pool with matching user and group entries, and file permissions that grant write access only to that specific user and its associated group. The additional administrative overhead is small compared to the risk of a shared identity.

7. sudo and granular privilege assignment instead of shared root access

A shared root password across multiple administrators is problematic both for security and traceability: there is no individual attribution of who performed which action, and when a team member leaves, the entire password has to be changed. sudo solves both problems by letting every administrator work with their own account while receiving root privileges only temporarily for specifically permitted commands. Every action is logged in /var/log/auth.log or, depending on the distribution, in the journal, including the username and the executed command.

Instead of adding users wholesale to the sudo or wheel group, granular rules in /etc/sudoers.d/ allow only the commands actually needed for a role. A deployment account usually only needs privileges for systemctl restart php8.4-fpm and a handful of targeted commands, not unrestricted root access. Editing must always go through visudo, which validates the syntax before saving and prevents a broken sudoers file that would otherwise lock out every sudo access on the server.

8. Password policies, account locks and chage

Password policies only take effect once they are technically enforced instead of merely written down in a document. The file /etc/login.defs defines system wide default values for password aging, such as PASS_MAX_DAYS for the maximum validity period and PASS_MIN_DAYS to prevent switching back to an old password too quickly. These values, however, only apply to newly created accounts, existing accounts must be adjusted individually with chage.

chage -l user shows the current password history of an account, including the last change date and expiration date. chage -M 90 user enforces a password change interval of 90 days, chage -E 2026-12-31 user sets a hard expiration date for temporary accounts such as intern or freelancer access. Complexity itself is enforced by the PAM module pam_pwquality, configured in /etc/security/pwquality.conf, which enforces minimum length, character classes and a dictionary check before a new password is accepted at all.


# /etc/login.defs (excerpt): configure password aging system wide
PASS_MAX_DAYS   90
PASS_MIN_DAYS   7
PASS_WARN_AGE   14

# UID/GID boundaries for regular users
UID_MIN         1000
UID_MAX         60000
SYS_UID_MIN     100
SYS_UID_MAX     999

GID_MIN         1000
GID_MAX         60000
SYS_GID_MIN     100
SYS_GID_MAX     999

# Encryption scheme for new password hashes
ENCRYPT_METHOD  SHA512

9. Auditing and cleaning up existing users and groups

On servers that have grown over years in production, accounts accumulate whose reason for existing nobody can explain anymore: former employees, test accounts from a migration project, services that were decommissioned long ago. A regular audit starts with a complete look at /etc/passwd and /etc/group, filtered for accounts with a UID above 1000 that have an interactive shell. lastlog additionally shows which accounts have not logged in since when, a strong indicator of an orphaned account.

Equally important is checking which accounts are members of privileged groups such as sudo, wheel or docker, since access to the Docker daemon effectively equals root privileges. getent group sudo lists all members of a group directly without manually searching through the file. A simple, repeatable audit script that automates these checks and outputs the results in a structured way turns an occasional manual review into a reliable, repeatable process that applies the same criteria even months later.


{
  "audit_date": "2026-07-12",
  "host": "shop-prod-01",
  "findings": [
    {
      "user": "ex-intern",
      "uid": 1042,
      "shell": "/bin/bash",
      "last_login": "2025-11-03",
      "sudo_member": false,
      "recommendation": "Lock account and delete after confirmation"
    },
    {
      "user": "deploy-old",
      "uid": 1015,
      "shell": "/bin/bash",
      "last_login": "2024-06-12",
      "sudo_member": true,
      "recommendation": "Remove from sudo group immediately, then lock"
    },
    {
      "user": "app-shop",
      "uid": 998,
      "shell": "/usr/sbin/nologin",
      "last_login": "never",
      "sudo_member": false,
      "recommendation": "No action needed, dedicated system user"
    }
  ]
}

The findings of such an audit fall into recurring cleanup patterns. The following table sets risky everyday practices against the recommended alternatives, applicable both when creating new accounts and when retroactively cleaning up existing servers.

Task Risky practice Recommended approach Benefit
Starting an application Service runs as root Dedicated system user per app Limits damage in case of compromise
Extending groups usermod -G group usermod -aG group Existing groups are preserved
Sharing root access Shared root password Individual accounts with sudo Logged, attributable actions
Detecting orphaned accounts Never checked lastlog + audit script Inactive accounts become visible
Granting sudo rights Blanket membership in sudo/wheel Granular rules in /etc/sudoers.d Least privilege instead of full access

These five patterns cover the bulk of security issues that typically surface in a server audit. Applying them consistently not only reduces the attack surface, it also builds the foundation for traceable compliance evidence if a client or auditor requests insight into the access structure.

Mironsoft

Server hardening, user management and deployment infrastructure

Time to finally structure server access cleanly?

We audit existing users and groups on your servers, remove orphaned accounts and set up dedicated, unprivileged service users with granular sudo rules for your deployment stack.

User audit

Complete inventory of all accounts, groups and sudo rights

Service user setup

Dedicated system users per application with matching file permissions

sudo hardening

Granular sudoers rules instead of blanket group privileges

10. Summary

Clean Linux user and group management starts with the correct use of useradd, usermod and groupadd and ends with a recurring audit process that uncovers orphaned accounts and excessive privileges. System users in the UID range below 1000 cleanly separate technical service accounts from regular human accounts. The principle of a dedicated, unprivileged user per application, as demonstrated by www-data or mysql, limits the damage of a compromised application to exactly that one application.

Individual sudo rules instead of shared root passwords create traceability for every administrative action. Password policies via chage and PAM ensure accounts do not remain valid indefinitely. The biggest lever, however, is not any single tool but consistency: a server that is systematically checked for orphaned accounts and excessive group privileges once a quarter stays manageable for years, while a server that is never checked eventually becomes something nobody can fully explain anymore.

Managing Linux users and groups, the essentials at a glance

Core tools

useradd, usermod and groupadd for creating and maintaining accounts. Do not forget -aG instead of -G when changing groups.

UID ranges

System users mostly below UID 1000, regular users from UID 1000. Boundaries live in /etc/login.defs.

Dedicated service user

Run every application under its own unprivileged user, analogous to www-data or mysql.

Regular auditing

lastlog, getent group and a repeatable audit script uncover orphaned accounts and excessive privileges.

11. FAQ: Managing Linux users and groups

1What is the difference between useradd and adduser?
useradd is the low level command with no prompts. adduser wraps it interactively, automatically creates a home directory from /etc/skel and is mostly available on Debian based systems.
2How do I create a system user without a login shell?
useradd --system --shell /usr/sbin/nologin --no-create-home username. --system automatically assigns a UID from the reserved system range.
3Why should every application get its own user?
A dedicated user limits the damage of a compromise to exactly that application, instead of giving every app on the server shared access.
4How do I find orphaned or inactive user accounts?
lastlog -b 90 shows accounts with no login in 90 days. Combined with filtering /etc/passwd by UID and shell, this yields a repeatable audit.
5Primary vs. secondary group, what is the difference?
Exactly one primary group per user, the default for new files. Any number of secondary groups for additional privileges such as sudo or docker.
6How do I safely remove a user including their home directory?
First check find / -xdev -user username, then run userdel -r username, which fully removes the account and home directory.
7Which UID ranges are reserved for system vs. regular users?
Mostly UID below 1000 for system users, from 1000 for regular accounts. The thresholds live in /etc/login.defs and vary by distribution.
8How do I grant sudo rights without full root access?
Via /etc/sudoers.d/, edited with visudo, with only specifically needed commands instead of blanket membership in the sudo group.
9What does chage do and why do I need it?
Manages the password aging of a single account: validity period, minimum interval, warning period and expiration date, complementing /etc/login.defs.
10How do I check which groups a user belongs to?
id username shows all groups of an account. getent group groupname conversely shows all members of a specific group.