useradd Command in Linux: Complete Guide to Creating New Users and Parameters

useradd command in Linux and it perimeters

useradd command in Linux and it perimeters

If you manage Linux servers, sooner or later you have to add a human being (or a service) to the system. That’s what useradd is for. It’s the low-level, no-hand-holding tool that every distribution ships to create new user accounts, and once you understand what it actually does under the hood, user management on Linux stops feeling like guesswork.

I’ve used useradd on everything from a single Raspberry Pi to fleets of production Ubuntu and RHEL boxes, and in this guide I’m going to walk through it the way I wish someone had walked me through it the first time — starting from the absolute basics and going all the way to the internals of what happens on disk when you run it.

What useradd Actually Is

useradd is a low-level utility, part of the shadow-utils (on RHEL/Fedora) or passwd (on Debian/Ubuntu) package, used to create new user accounts on a Linux system. It’s written in C and lives at /usr/sbin/useradd. Because it’s a system binary rather than a shell script, it directly edits the core account databases: /etc/passwd, /etc/shadow, /etc/group, and /etc/gshadow.

It’s worth contrasting useradd with adduser. On Debian-based systems, adduser is a friendlier Perl wrapper around useradd that asks interactive questions and sets sane defaults automatically. useradd, on the other hand, is deliberately minimal — by default, if you just type useradd john, you’ll get an account, but without a password set and, depending on the distro, without a home directory. This is intentional: useradd is meant to be scriptable and predictable, not chatty.

Basic Syntax

useradd [options] LOGIN
useradd -D
useradd -D [options]

The first form creates a new account named LOGIN. The -D form is special — it shows or changes the default values used when new users are created (I’ll cover that separately below).

You need root privileges (or sudo) to run useradd, since it writes to protected files like /etc/shadow.

A Minimal Example

sudo useradd john

Run this and check what happened:

grep john /etc/passwd
john:x:1001:1001::/home/john:/bin/sh

Notice a few things already:

This is the classic “gotcha” for people coming from adduser: useradd alone gives you a bare-bones entry, not a fully working login.

Full Option Reference

Here is the actual set of options as reported by useradd --help on a modern shadow-utils build:

Usage: useradd [options] LOGIN
       useradd -D
       useradd -D [options]

Options:
      --badname                 do not check for bad names
  -b, --base-dir BASE_DIR       base directory for the home directory of the
                                 new account
      --btrfs-subvolume-home    use BTRFS subvolume for home directory
  -c, --comment COMMENT         GECOS field of the new account
  -d, --home-dir HOME_DIR       home directory of the new account
  -D, --defaults                print or change default useradd configuration
  -e, --expiredate EXPIRE_DATE  expiration date of the new account
  -f, --inactive INACTIVE       password inactivity period of the new account
  -F, --add-subids-for-system   add entries to sub[ud]id even when adding a system user
  -g, --gid GROUP               name or ID of the primary group of the new
                                 account
  -G, --groups GROUPS           list of supplementary groups of the new
                                 account
  -h, --help                    display this help message and exit
  -k, --skel SKEL_DIR           use this alternative skeleton directory
  -K, --key KEY=VALUE           override /etc/login.defs defaults
  -l, --no-log-init             do not add the user to the lastlog and
                                 faillog databases
  -m, --create-home             create the user's home directory
  -M, --no-create-home          do not create the user's home directory
  -N, --no-user-group           do not create a group with the same name as
                                 the user
  -o, --non-unique              allow to create users with duplicate
                                 (non-unique) UID
  -p, --password PASSWORD       encrypted password of the new account
  -r, --system                  create a system account
  -R, --root CHROOT_DIR         directory to chroot into
  -P, --prefix PREFIX_DIR       prefix directory where are located the /etc/* files
  -s, --shell SHELL             login shell of the new account
  -u, --uid UID                 user ID of the new account
  -U, --user-group              create a group with the same name as the user
  -Z, --selinux-user SEUSER     use a specific SEUSER for the SELinux user mapping
      --extrausers              Use the extra users database

Let’s break down the ones you’ll actually use day to day.

-m / --create-home

This is the option I almost always add. Without it, no home directory is created — the user has a home path listed in /etc/passwd, but the directory doesn’t physically exist, so on first login they’ll have nowhere to put files (and some shells will complain).

sudo useradd -m john

When -m is used, useradd copies the contents of the skeleton directory (/etc/skel by default) into the new home directory — that’s where .bashrc, .bash_profile, .profile, etc. come from.

-d / --home-dir

Overrides the default home directory path (normally /home/<username>):

sudo useradd -m -d /srv/apps/john john

-s / --shell

Sets the login shell. Default varies by distro — Debian/Ubuntu often defaults to /bin/sh, RHEL-based systems often default to /bin/bash. Always check /etc/default/useradd or /etc/login.defs.

sudo useradd -m -s /bin/bash john

To create an account that can’t log in interactively (common for service accounts):

sudo useradd -m -s /usr/sbin/nologin appuser

-u / --uid

Manually assigns a UID instead of letting the system pick the next free one:

sudo useradd -u 1500 -m john

Useful when syncing UIDs across NFS-mounted systems, containers, or when matching a UID from another server.

-g / --gid and -G / --groups

-g sets the primary group (by name or GID). -G sets supplementary groups (comma-separated, no spaces):

sudo useradd -m -g developers -G sudo,docker,www-data john

If -g is omitted, most modern distros create a private group matching the username (User Private Group scheme) — this is controlled by USERGROUPS_ENAB in /etc/login.defs.

-c / --comment

Sets the GECOS field — traditionally used for the user’s full name, and shown by tools like finger:

sudo useradd -m -c "John Carter, DevOps" john

-e / --expiredate and -f / --inactive

-e sets an account expiration date (YYYY-MM-DD) — after this date the account is disabled entirely. -f sets how many days after a password expires the account gets locked:

sudo useradd -m -e 2026-12-31 -f 7 contractor

This is standard practice for contractor or temporary accounts — set it once at creation and never worry about manually disabling it later.

-r / --system

Creates a system account: UID is picked from the system range (below UID_MIN, typically under 1000), no expiration by default, and often no home directory unless -m is explicit. Used for daemons and services.

sudo useradd -r -s /usr/sbin/nologin -m -d /var/lib/myapp myappsvc

-p / --password

Sets an already-encrypted password hash — not a plaintext one. If you pass plaintext here, it gets stored as-is (broken and insecure), so this is almost always misused by beginners. The safe pattern:

sudo useradd -m john
sudo passwd john

Or, for scripting, generate a hash properly and pass it:

HASH=$(openssl passwd -6 'StrongPassword123!')
sudo useradd -m -p "$HASH" john

-N / --no-user-group and -U / --user-group

Controls whether a private group matching the username is created. -U forces it, -N suppresses it (falling back to a shared default group, usually users).

-o / --non-unique

Allows creating a user with a duplicate UID — two usernames sharing one UID. Rare, but used for aliasing accounts.

-k / --skel

Points to an alternate skeleton directory instead of /etc/skel, useful when different account types need different default dotfiles.

sudo useradd -m -k /etc/skel-devs -s /bin/bash devuser

Checking and Changing Defaults with -D

Running useradd -D alone prints current defaults:

useradd -D
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/sh
SKEL=/etc/skel
CREATE_MAIL_SPOOL=no

These values live in /etc/default/useradd. You can change them permanently:

sudo useradd -D -s /bin/bash

This rewrites SHELL=/bin/bash in /etc/default/useradd, so every future useradd call without -s gets bash by default.

What Files Change When You Run useradd

This is the part that separates people who “use the command” from people who actually understand Linux account internals.

/etc/passwd — one line per user: username:x:UID:GID:GECOS:home:shell. The x means the real password hash lives elsewhere (in shadow), not here — this file is world-readable.

/etc/shadow — password hashes and aging data, readable only by root: username:hash:lastchange:min:max:warn:inactive:expire:.

/etc/group and /etc/gshadow — group membership and group password data.

/etc/login.defs — system-wide defaults for UID/GID ranges (UID_MIN, UID_MAX, SYS_UID_MIN, etc.), password aging policy, and whether useradd creates a mail spool.

/etc/default/useradd — defaults consumed specifically by useradd (home base dir, default shell, skeleton dir).

Internally, useradd doesn’t edit these files directly with a text editor — it uses locking (/etc/passwd.lock, etc.) to avoid corrupting the database if two processes try to modify it simultaneously, then writes atomically and releases the lock. This is why you should never hand-edit /etc/passwd while useradd/usermod/userdel might be running concurrently — use vipw and vigr if you must edit by hand.

Real-World System Administration Examples

Creating a standard developer account:

sudo useradd -m -s /bin/bash -c "Jane Doe" -G sudo,docker jane
sudo passwd jane

Creating a service account for an application with no login shell and a custom home:

sudo useradd -r -m -d /opt/myapp -s /usr/sbin/nologin -c "MyApp service account" myapp

Bulk-creating users from a list (shell scripting example):

#!/bin/bash
# create_users.sh - bulk create users from a text file (one username per line)
while IFS= read -r username; do
    if id "$username" &>/dev/null; then
        echo "User $username already exists, skipping."
    else
        useradd -m -s /bin/bash "$username"
        echo "${username}:ChangeMe123!" | chpasswd
        passwd -e "$username"   # force password change on first login
        echo "Created user: $username"
    fi
done < users.txt

Creating a user with a matching UID/GID for NFS consistency:

sudo groupadd -g 2001 sharedgroup
sudo useradd -m -u 2001 -g 2001 nfsuser

How This Compares to Related Tools

useradd is consistent across RHEL, CentOS, Fedora, Debian, Ubuntu, SUSE, and Arch — it’s part of the POSIX-adjacent shadow-utils toolset, so scripts written against it are highly portable. The main differences you’ll hit across distros are the defaults (default shell, whether a UID-matching private group is created, whether home directories are auto-created), not the command’s behavior itself.

Troubleshooting Common Issues

“useradd: user ‘john’ already exists” — check with id john or getent passwd john; if you need to change something, use usermod, not useradd.

Home directory not created — you forgot -m. Check with ls -ld /home/john.

User can log in via SSH key but shell immediately exits — shell is set to /usr/sbin/nologin or /bin/false; check with getent passwd john and fix with usermod -s /bin/bash john.

“useradd: UID 1000 is not unique” — you tried to reuse a UID without -o. Either pick a different UID or add --non-unique if that’s genuinely intended.

Account created but can’t log in with a password — the account is locked (password field starts with ! in /etc/shadow) until you run passwd username.

Security Considerations

Summary

useradd is deliberately minimal, predictable, and script-friendly — it does exactly what you tell it and nothing more, which is precisely why it’s the right tool for automation and configuration management (Ansible, Puppet, Bash provisioning scripts all lean on it directly or indirectly). Once you understand that it’s really just a careful, locking-aware editor for /etc/passwd, /etc/shadow, /etc/group, and /etc/gshadow, the whole Linux user-management model stops being mysterious.

References

Exit mobile version