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 -landpasswd -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.
getent passwd # every account known to the systemgetent group # every group known to the systemgetent passwd paul # the entry of one precise accountA 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.
awk -F: '$3 >= 1000 && $1 != "nobody" { print $1, $3 }' /etc/passwdTo 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.
id paulOutput 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:
sudo useradd -m paulCheck the result immediately, it is the reflex to take after every action:
id paulls -ld /home/paulThe 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 byGROUPin/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.
sudo passwd paulTyping is invisible on screen, that is normal. Then check the
password state, the second field must go from L to P:
sudo passwd -S paulCreate 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:
sudo groupadd developpeurssudo useradd -m -s /bin/bash -g developpeurs -u 1050 pierreid pierreThe 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.
sudo groupadd developpeursgetent group developpeursAdd 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:
sudo usermod -aG developpeurs paulid 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.
sudo gpasswd -d paul developpeursid paulChange 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.
sudo usermod -g developpeurs paulid paulBeware: 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
sudo groupdel developpeursThe 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.
| Goal | Command | Check |
|---|---|---|
| Rename the account | sudo usermod -l pierre paul | getent passwd pierre |
| Move the home directory | sudo usermod -d /home/pierre -m pierre | ls -ld /home/pierre |
| Change the shell | sudo usermod -s /bin/bash pierre | getent passwd pierre |
| Change the UID | sudo usermod -u 1234 pierre | id 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 UIDThe /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:
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.
id -u pierre # shows for instance 1050Archive the home directory
Archive before deleting: userdel -r erases the directory with no confirmation and no trash.
sudo tar czf /var/backups/pierre-home.tar.gz -C /home pierreDelete the account
userdel alone removes the account from the system databases but keeps
all its files, including its home directory:
sudo userdel pierreThe -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).
sudo userdel -r pierreEverything 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:
find / -user pierrefind: invalid user name or UID argument to -user: 'pierre'Two methods really work. The first uses the UID noted before deletion:
sudo find / -xdev -uid 1050 -lsThe second needs nothing: -nouser selects files whose UID exists in
no account database. It is the safety net when nobody noted the UID.
sudo find / -xdev -nouser -lsThe -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:
sudo chown -R marie:developpeurs /srv/projets/api-facturationWhat 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:
- Linux permissions: read and understand rights
to decode
ls -l, the meaning ofrwxon a file and on a folder, and theumask. - Change rights with chmod, chown and chgrp for the octal notation, recursive changes and sharing a folder with a group.
- Refine with ACLs when a single owning group is no longer enough to express the need.
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
sudo passwd -l paulThe 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:
sudo usermod --expiredate 1 paulThe 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:
sudo usermod --expiredate 1 paul # expire the accountsudo passwd -l paul # lock the passwordsudo pkill -KILL -u paul # close the sessions already openThe 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:
sudo usermod --expiredate "" paulsudo passwd -u paulCheck the state of an account
It takes two commands, because neither tells everything. chage -l shows
the account expiry, which passwd -S completely ignores:
sudo chage -l paulAccount expires : Jan 02, 1970The Account expires line must show a past date for a disabled
account, or never for an active account. For the password state:
sudo passwd -S paulThe 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:
| State | Field in /etc/shadow | Meaning |
|---|---|---|
P | a hash ($y$j9T$...) | Usable password |
L | ! in front of the hash | Locked, the hash is kept, passwd -u restores it |
L | * alone | Never a usable password: normal case of service accounts |
NP | empty field | No 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:
sudo useradd -r -s /usr/sbin/nologin -M exportateur-metriquesThe 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.
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.
| Symptom | Likely cause | Fix |
|---|---|---|
| The former employee still logs in over SSH | Password locked, but account not expired | sudo 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 name | sudo find / -xdev -nouser -ls |
| The user added to a group still lacks the rights | Their session predates the addition | Log out/in, then check with id |
ls -l shows a number instead of the owner | Orphan file after userdel or usermod -u | sudo chown <new_owner> <file> |
groupdel: cannot remove the primary group of user | The group is the primary group of an account | Change that primary group (usermod -g) before deleting |
useradd: UID 1050 is not unique | The UID is already taken by another account | Choose 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.
sudo chage -d 0 paulGenerate 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 -mcreates a same-name private group, not ausersgroup: it is the default behaviour on Debian, Ubuntu and RHEL, driven byUSERGROUPS_ENAB yesin/etc/login.defs.usermod -Gwithout the-aoverwrites all existing secondary memberships, includingsudo. And a new group is only active after logging in again.passwd -landusermod -Ldo 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 -lis authoritative, notpasswd -S: an expired account still shows aPpassword. TheAccount expiresline is the only proof.- In
/etc/shadow,!= locked,*= never a password (service accounts), empty field = no password, so to fix urgently. userdel -ronly deletes the home directory and the mailbox. Everything the account owned in/srv,/var/wwwor/optstays in place.- Note the UID with
id -ubefore deleting: afterwards,find -userfails and onlysudo find / -xdev -nouser -lsremains to find the orphan files. - A service account has no shell:
useradd -r -s /usr/sbin/nologin(/sbinbeing a link tousr/sbin, both paths are the same file), and humans go throughsudo, 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
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
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Authenticate Linux against an LDAP directory with SSSD
A new full quiz with random questions
Work again only on the questions you missed