Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Administer users and groups

By updated

Improve this page

Create an account, attach it to the right groups, modify it without breaking the files it owns, then really disable it on the day the person leaves: that is the life cycle of a Linux account. This page gives you the exact commands of each step (useradd, usermod, gpasswd, userdel, chage), the check command that goes with each, and the two traps that cost the most in production: the "locked" account that keeps its SSH access, and the orphan files left behind a deleted account.

The basic notions (UID, GID, root, content of /etc/passwd and /etc/group) are detailed in Linux users and groups. Here, we move on to administration: you act on the system, with sudo.

What you will learn

  • List the accounts and groups really known to the system
  • Create a user with their home directory and password
  • Organise rights with primary and secondary groups
  • Modify and delete an account without leaving orphan files
  • Really disable an access, including by SSH key
  • Check the state of an account with id, chage -l and passwd -S

What users and groups are for

On Linux, identity is the first security mechanism: the kernel only allows a read, a write or an execution according to the UID (user identifier) and the GIDs (group identifiers) of the process asking for access.

A user represents a unique identity. It can match a human, or a service (nginx, postgres) that must run with as few rights as possible. Each account has a home directory, a login shell and a list of groups.

A group gathers users who share the same permissions on resources. It is what makes an access policy maintainable: you grant rights once to the group, then you only manage memberships.

The typical case: a development team. You create a developpeurs group, you give write access on the /srv/projets folder to this group, then you add each newcomer to it. No file permission is touched when one more person arrives.

List the accounts and groups present

Before any manipulation, look at what already exists: an account you believe absent may exist locally, or come from a directory (LDAP, Active Directory via SSSD). Creating a duplicate UID is a classic source of files reachable by the wrong person.

The reference command is getent: it queries all the sources declared in /etc/nsswitch.conf (the file that decides where the system goes to look up identities), not only the local files.

Terminal window
getent passwd # every account known to the system
getent group # every group known to the system
getent passwd paul # the entry of one precise account

A getent passwd entry reads as follows: name, password (the x points to /etc/shadow), UID, primary GID, comment, home directory, shell.

To limit yourself to human accounts, filter on the UID: distributions reserve UIDs below 1000 for system accounts.

Terminal window
awk -F: '$3 >= 1000 && $1 != "nobody" { print $1, $3 }' /etc/passwd

To know a person's groups, id is more complete than groups: it shows the numeric UIDs/GIDs on top of the names, which lets you spot an identifier mismatch right away.

Terminal window
id paul

Output on a freshly created account:

uid=1001(paul) gid=1001(paul) groups=1001(paul)

Create a user

The universal command is useradd, present on all distributions. Always write -m to create the home directory:

Terminal window
sudo useradd -m paul

Check the result immediately, it is the reflex to take after every action:

Terminal window
id paul
ls -ld /home/paul

The same-name private group

Look at the output of id paul: the primary group is called paul, not users. It is the default behaviour of modern Debian, Ubuntu and RHEL, driven by the USERGROUPS_ENAB yes directive of /etc/login.defs: useradd creates a private group with the same name as the account, with the same identifier.

The benefit is isolation by default: since nobody else is a member of the paul group, files created with generous group rights do not leak to the other accounts of the server. It is also what makes umask 002 safe on these distributions.

Two options force this behaviour one way or the other:

  • -U, --user-group: create the same-name group (default behaviour);
  • -N, --no-user-group: do not create it, the account then gets the primary group set by GROUP in /etc/default/useradd.

Set the password

A freshly created account has no usable password: the matching field in /etc/shadow contains !. Password login is therefore impossible until you set one.

Terminal window
sudo passwd paul

Typing is invisible on screen, that is normal. Then check the password state, the second field must go from L to P:

Terminal window
sudo passwd -S paul

Create an account with precise options

In production, you often set the shell, the UID and sometimes the primary group. The example below creates an account whose primary group is developpeurs, which replaces the same-name private group:

Terminal window
sudo groupadd developpeurs
sudo useradd -m -s /bin/bash -g developpeurs -u 1050 pierre
id pierre

The output confirms the three choices, shell aside:

uid=1050(pierre) gid=1002(developpeurs) groups=1002(developpeurs)

Choosing -g developpeurs means that all files created by pierre will belong by default to the developpeurs group. It is handy for a shared folder, but it weakens the isolation described above: keep this setup for project accounts, not for a person's administration account.

Organise rights with groups

Groups avoid managing file by file. You create a functional group, give it the rights on a resource, then manage the memberships. It is the basis of an auditable access policy.

Terminal window
sudo groupadd developpeurs
getent group developpeurs

Add a user to a secondary group

An account has a single primary group (the one in /etc/passwd) and as many secondary groups as needed. For an addition, the -a option is mandatory:

Terminal window
sudo usermod -aG developpeurs paul
id paul
  • -a, --append: adds without touching existing memberships;
  • -G, --groups: the list of secondary groups.

A point that often confuses: an already open session keeps the groups it had at login time. The user must log in again for the new group to be taken into account. The check is done with id in the new session, not in the old one.

Remove a user from a group

gpasswd -d removes a secondary membership. The primary group is not affected.

Terminal window
sudo gpasswd -d paul developpeurs
id paul

Change the primary group

The primary group is changed with usermod -g. Useful when the same-name private group no longer makes sense, typically for a service account that must write into a shared tree.

Terminal window
sudo usermod -g developpeurs paul
id paul

Beware: files already created keep their old group. A chgrp -R is often needed afterwards, otherwise the user can no longer write in their own old folders through their new group.

Delete a group

Terminal window
sudo groupdel developpeurs

The command refuses to delete a group that serves as the primary group of an account, which is a useful protection:

groupdel: cannot remove the primary group of user 'paul'

First remove those accounts or change their primary group. Deleting a group deletes no user, but the files that carried this GID end up with no named group: ls -l will show a raw number instead of the name.

Modify an existing account

usermod covers renaming, moving the home directory, changing the shell and the UID. These are operations to do outside a session: ask the person to log out, otherwise running processes keep the old identity and write files with the old UID.

GoalCommandCheck
Rename the accountsudo usermod -l pierre paulgetent passwd pierre
Move the home directorysudo usermod -d /home/pierre -m pierrels -ld /home/pierre
Change the shellsudo usermod -s /bin/bash pierregetent passwd pierre
Change the UIDsudo usermod -u 1234 pierreid pierre

Renaming with -l does not move the home directory: after usermod -l pierre paul, the pierre account still points to /home/paul. It is the -d option combined with -m that really moves the content.

The UID change trap

usermod -u automatically reassigns the files located in the home directory, but not those located elsewhere. After a sudo usermod -u 1234 paul on an account that owned a file in /srv/app, here is the real state:

-rw-r--r-- 1 1234 0 0 /home/paul/notes.txt <- reassigned
-rw-r--r-- 1 1001 0 0 /srv/app/data.txt <- stayed on the old UID

The /srv/app file shows an orphan numeric UID. Worse: if another account one day gets UID 1001, it will inherit this file without anybody deciding it. So note the old UID before the operation and fix what remains:

Terminal window
sudo find / -xdev -uid 1001 -exec chown 1234 {} +

Delete an account cleanly

Deletion is final and is prepared in three stages: note the UID, archive what must be, then delete and clean up.

Note the UID first

It is the step everybody skips, and it drives what follows: once the account is deleted, the name no longer exists and you can no longer do a search by name.

Terminal window
id -u pierre # shows for instance 1050

Archive the home directory

Archive before deleting: userdel -r erases the directory with no confirmation and no trash.

Terminal window
sudo tar czf /var/backups/pierre-home.tar.gz -C /home pierre

Delete the account

userdel alone removes the account from the system databases but keeps all its files, including its home directory:

Terminal window
sudo userdel pierre

The -r option goes further. Its exact scope, as described in its manual (remove home directory and mail spool), is limited to two places:

  • the home directory (/home/pierre) and all its content;
  • the local mailbox (/var/mail/pierre).
Terminal window
sudo userdel -r pierre

Everything the user owned elsewhere on the disk (/srv, /var/www, /opt, /tmp) stays in place, with a UID that no longer matches anyone.

Find orphan files

Here hides the most frequent mistake. A search by name fails once the account is deleted, since find must resolve the name into a UID:

Terminal window
find / -user pierre
find: invalid user name or UID argument to -user: 'pierre'

Two methods really work. The first uses the UID noted before deletion:

Terminal window
sudo find / -xdev -uid 1050 -ls

The second needs nothing: -nouser selects files whose UID exists in no account database. It is the safety net when nobody noted the UID.

Terminal window
sudo find / -xdev -nouser -ls

The -xdev option limits the search to the root file system and avoids sweeping /proc, /sys and network mounts, which produce noise and lengthen the search. Repeat the command on each mounted file system you want to cover (sudo find /srv -xdev -nouser -ls).

Each file found must then be reassigned to its new owner, or deleted if the data is dead:

Terminal window
sudo chown -R marie:developpeurs /srv/projets/api-facturation

What about file permissions?

Creating accounts and groups is useless until the files carry the right rights. The trio to know is chmod (rwx permissions), chown (the owner) and chgrp (the group), with its octal notation (750, 640) and its symbolic notation (u+x, g-w).

These commands have their dedicated page in the course, and there is no reason to learn them again here:

Lock or disable an account

Deleting is not always the right answer. A long leave, a suspension, an internal investigation: you want to block access without losing files or settings. That is where the distinction between locking a password and disabling an account becomes critical.

Lock the password

Terminal window
sudo passwd -l paul

The command puts a ! in front of the password hash in /etc/shadow: no password typed can match any more. The account still exists, the home directory is kept, and the original hash is preserved, which allows an immediate rollback with passwd -u.

sudo usermod -L paul does exactly the same thing: both commands set the same ! in /etc/shadow. There is no "global lock" stronger on one side than on the other.

Really disable an account

To close an account whatever the authentication method, you must expire it:

Terminal window
sudo usermod --expiredate 1 paul

The expiry date is set to 2 January 1970, so in the past: the account is expired, and all logins are refused, including by SSH key. sudo chage -E 0 paul has the same effect.

A full disabling combines the three gestures, in this order:

Terminal window
sudo usermod --expiredate 1 paul # expire the account
sudo passwd -l paul # lock the password
sudo pkill -KILL -u paul # close the sessions already open

The pkill is not decorative: expiry blocks new logins, it does not kill an SSH session in progress. Without it, the person stays on the server until they log out by themselves.

To reactivate the account, lift the expiry then unlock the password:

Terminal window
sudo usermod --expiredate "" paul
sudo passwd -u paul

Check the state of an account

It takes two commands, because neither tells everything. chage -l shows the account expiry, which passwd -S completely ignores:

Terminal window
sudo chage -l paul
Account expires : Jan 02, 1970

The Account expires line must show a past date for a disabled account, or never for an active account. For the password state:

Terminal window
sudo passwd -S paul

The second field is L, P or NP (measured identical on AlmaLinux 10, Debian 13 and Ubuntu 24.04). Beware of their exact meaning, because it covers how the password field is written in /etc/shadow:

StateField in /etc/shadowMeaning
Pa hash ($y$j9T$...)Usable password
L! in front of the hashLocked, the hash is kept, passwd -u restores it
L* aloneNever a usable password: normal case of service accounts
NPempty fieldNo password: login possible without typing one, to fix urgently

The * is therefore not the mark of a lock: it is the original state of a system account that never had a password, and there is nothing to "unlock" behind it. Only the ! signals a locking action.

An expired account keeps a perfectly valid password: passwd -S still shows P. It is indeed chage -l that is authoritative to know whether an access is really closed.

Security and least privilege

Account management is the first attack surface of a server: a forgotten account is an open door that triggers no alert. Four rules cover most of the risk.

One account per person, never a shared account. An admin account used by five people makes traceability impossible: the logs will never say who ran the destructive command, and the departure of a single person forces changing the secret for everyone.

Service accounts have no shell. A service has no reason to be able to open an interactive session. Create it with -r (system account, UID below 1000) and a shell that refuses login:

Terminal window
sudo useradd -r -s /usr/sbin/nologin -M exportateur-metriques

The two paths /usr/sbin/nologin and /sbin/nologin actually designate the same executable: since the usr-merge, /sbin is a symbolic link to usr/sbin, which a stat -c %i on both paths confirms by returning the same inode number. Write /usr/sbin/nologin, which works everywhere. The -M option avoids creating a useless home directory.

Go through sudo, not root. Give the necessary rights via sudo and limit the allowed commands, instead of handing out full root access. A shared root access has the same traceability flaw as a shared account.

Audit regularly. Two commands are enough to spot drift: the first lists the password state of all accounts, the second shows who never logged in.

Terminal window
sudo passwd -Sa | awk '$2 == "NP" { print "SANS MOT DE PASSE:", $1 }'
lastlog | grep -i "never logged in"

lastlog no longer exists on Debian 13: the command was removed, and its replacement lastlog2 (package of the same name) only sees logins recorded by the libpam-lastlog2 PAM module. It is still present on AlmaLinux 10 and Ubuntu 24.04.

Any human account that comes out as NP, or that has not logged in for months, must be expired then deleted after archiving. The rollback is simple as long as you have not deleted: usermod --expiredate "" <account> puts the access back in service.

Troubleshooting

Account management mistakes all look alike: the action seems to have succeeded, but access or rights do not follow. Here are the most frequent symptoms and their real cause.

SymptomLikely causeFix
The former employee still logs in over SSHPassword locked, but account not expiredsudo usermod --expiredate 1 <account> then sudo pkill -KILL -u <account>
find: invalid user name or UID argument to -user: '<name>'The account was deleted, find -user can no longer resolve the namesudo find / -xdev -nouser -ls
The user added to a group still lacks the rightsTheir session predates the additionLog out/in, then check with id
ls -l shows a number instead of the ownerOrphan file after userdel or usermod -usudo chown <new_owner> <file>
groupdel: cannot remove the primary group of userThe group is the primary group of an accountChange that primary group (usermod -g) before deleting
useradd: UID 1050 is not uniqueThe UID is already taken by another accountChoose another UID, or let the system assign it

Good practices

These habits cost a few seconds when creating an account and avoid hours of archaeology six months later.

Adopt a naming convention. Lowercase, no accent or space, consistent format (firstname.lastname). For groups, name the activity (dev, infra, compta), not the person nor the server: a group outlives those who make it up.

Never edit /etc/passwd, /etc/group or /etc/shadow by hand. One wrong character in these files can prevent any login, including root's. Use usermod, groupmod, gpasswd, or the vipw and vigr editors which set a lock and check the syntax before writing.

Force a password change at first login. The initial password necessarily travels through an imperfect channel: it must not survive the first session.

Terminal window
sudo chage -d 0 paul

Generate this initial password with a tool, not with your imagination: openssl rand -base64 18, or pwgen -s 16 1 if you install the pwgen package.

Manage groups, not individual permissions. A right set directly on an account is invisible in an access review. A right set on a group reads at a glance with getent group, and is removed with a single gpasswd -d.

Document the movements. Who was created, in which groups, by whom, and why. On audit day, or the day an unexplained access appears, this trail is worth every tool.

Put it into practice

The life cycle of an account is learned hands on the keyboard. Two labs extend this guide: the first has you create an account with an imposed UID, shell and groups, checking each choice with id; the second has you set a password expiry and complexity policy, then prove that it really applies.

The two labs above take time to explain. The drill removes the safety net: timed, with no hint, and graded on the system state. It is the format of the RHCSA and LFCS exams.

Key points

  • useradd -m creates a same-name private group, not a users group: it is the default behaviour on Debian, Ubuntu and RHEL, driven by USERGROUPS_ENAB yes in /etc/login.defs.
  • usermod -G without the -a overwrites all existing secondary memberships, including sudo. And a new group is only active after logging in again.
  • passwd -l and usermod -L do the same thing: they put a ! in front of the hash and do not block SSH keys. Only expiry (usermod --expiredate 1) really closes an account.
  • chage -l is authoritative, not passwd -S: an expired account still shows a P password. The Account expires line is the only proof.
  • In /etc/shadow, ! = locked, * = never a password (service accounts), empty field = no password, so to fix urgently.
  • userdel -r only deletes the home directory and the mailbox. Everything the account owned in /srv, /var/www or /opt stays in place.
  • Note the UID with id -u before deleting: afterwards, find -user fails and only sudo find / -xdev -nouser -ls remains to find the orphan files.
  • A service account has no shell: useradd -r -s /usr/sbin/nologin (/sbin being a link to usr/sbin, both paths are the same file), and humans go through sudo, never through root.

Knowledge check

Check that the essentials of this guide are acquired. The questions only cover what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

6 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

FAQ: frequently asked questions about accounts and groups

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn