Audit Linux Accounts with lslogins
You will finish with a repeatable account audit: a list of accounts, separate views for human and system UIDs, and a focused record for one login. The examples use the installed lslogins command from util-linux 2.41.3_1. The canonical manpage available on this host describes util-linux 2.39.3, so the command's own version and help output are the final check when they differ.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need a shell and read access to the account databases. Most checks are ordinary commands and do not need sudo. Some password-expiration details require root, and the command may read login databases and wtmp or btmp logs. This guide only reads them.
1. Confirm the command you will run
Check the executable, version and option names before building a script around them:
$ command -v lslogins
/home/linuxbrew/.linuxbrew/bin/lslogins
$ lslogins --version
lslogins from util-linux 2.41.3 (features: lastlog2)
$ lslogins --help
The version line is not decorative. Distribution packages can ship a different util-linux release, and newer builds can expose columns or log formats that an older manpage does not mention. The command exits with status 0 for a successful request, 1 for incorrect arguments and 2 for a serious error such as a corrupt log.
Checkpoint
If command -v points somewhere unexpected, stop and decide whether that is the binary you intend to audit. Do not silently mix its help output with another installation's manpage.
2. Take a read-only account inventory
With no filters, lslogins lists known accounts. Limit the columns so the result is easier to scan:
$ lslogins --noheadings --output=USER,UID,GROUP,PROC
root 0 422
daemon 1 1
bin 2 0
sys 3 0
The exact names and process counts are host-specific. USER and UID identify the account, GROUP is its primary group, and PROC is the number of processes attributed to it. --noheadings is useful for a pipeline, but headings are safer while exploring because they keep the column meaning visible.
Do not interpret an account with zero processes as broken. Service accounts commonly have no running process, while a process count is only a snapshot. If the output is too wide for your terminal, use --notruncate when the complete value matters, or choose fewer columns.
3. Separate human and system account views
Use the account filters when the full inventory is distracting:
$ lslogins --user-accs --noheadings --output=USER,UID,PROC
root 0 422
[email protected] 1000 3
ftptest 1001 6
andy 1004 151
$ lslogins --system-accs --noheadings --output=USER,UID,PROC
messagebus 101 17
pollinate 102 0
syslog 103 1
sshd 109 0
These categories are UID ranges, not a promise about how an account is used. The manpage gives user accounts as UID 1000 and above by default, and system accounts as UID 101 through 999, excluding the special nobody or nfsnobody UID. The default thresholds can be replaced by UID_MIN, UID_MAX, SYS_UID_MIN and SYS_UID_MAX in /etc/login.defs. Check the local policy before treating the two lists as a security classification:
$ grep -E '^(UID_MIN|UID_MAX|SYS_UID_MIN|SYS_UID_MAX)[[:space:]]' /etc/login.defs
UID_MIN 1000
UID_MAX 60000
A UID below the user threshold is not automatically unsafe, and a UID above it is not automatically a person. Investigate the shell, home directory, ownership and service configuration before disabling anything.
4. Inspect one login precisely
Once you have an account name, use --logins with a comma-separated list. Names and numeric user IDs are accepted:
$ lslogins --logins=root,andy --noheadings --output=USER,UID,GROUP,PROC
root 0 422
andy 1004 150
Unknown login names are ignored by the filter, so an unexpectedly short result deserves a spelling check. For an individual user, passing the username as the positional argument asks for all available details and uses a different output shape. That is useful for an interactive inspection, but selected columns are less fragile for reports.
Groups need separate attention. Use --groups with a comma-separated list:
$ lslogins --groups=sudo --noheadings --output=USER,UID,GROUP
andy 1004
Unknown groups are ignored. A primary-group relationship can be absent from this result when the user is not explicitly listed in /etc/group; the group scan uses the groups database rather than inferring membership from the user's primary GID. Use --supp-groups when the supplementary group information itself is what you need.
5. Choose output for a script or report
Start with a small, explicit column list. Ask the installed help for the complete list when you need another field:
$ lslogins --help | sed -n '/Available output columns:/,$p'
$ lslogins --logins=root --noheadings \
--output=USER,UID,GROUP,SUPP-GROUPS,PROC
root 0 ollama,kvm 422
For shell-friendly key-value output, combine --export and --shell:
$ lslogins --logins=root --noheadings --export --shell \
--output=USER,UID,GROUP,PROC
USER="root" UID="0" GROUP="" PROC="422"
The --shell option changes column names to characters allowed in shell variable identifiers. Do not parse the default aligned table with a fixed number of spaces. It can be truncated for display, and empty fields are meaningful. For machine-to-machine records where newlines are unsafe, --print0 uses a NUL delimiter; make the receiving program explicitly support that format.
Safety boundary
Exported values are data, not trusted shell code. Do not turn an entire line into a command with eval. Parse the fields with a tool that treats values as values.
6. Add login and password details only when needed
Use --last for the last login session and --failed for failed attempts. These depend on available log files, so "never" or an empty field can be a real account state rather than a command failure:
$ lslogins --logins=andy --last --noheadings \
--output=USER,UID,LAST-LOGIN,LAST-TTY,LAST-HOSTNAME
andy 1004 Jul24/21:21 pts/2 example-host
Use --time-format=iso when a consistent date representation matters. The local log's retention and timezone affect what can be reported. An old last-login date does not prove that the account is unused.
Password status and account expiration are more sensitive. --pwd shows password-related fields. --acc-expiration includes the last password change and account expiration data and requires root privileges:
$ sudo lslogins --logins=andy --acc-expiration --pwd \
--noheadings --output=USER,UID,PWD-LOCK,PWD-EMPTY,PWD-DENY,PWD-CHANGE,PWD-EXPIR
andy 1004 0 0 0 2026-01-12 -
Review the output before sharing it. Password status is security-sensitive account data, and the exact dates and markers vary with the local shadow database. Do not paste a full report into a ticket or chat unless its recipients are authorised to see it.
7. Diagnose a failed or confusing result
An "unknown column" error usually means the column name is wrong for this installed build. Re-run lslogins --help and copy an exact name. For example, SUPP-GROUPS is a column name, while GROUPS is not listed by the installed command.
If the command returns status 2, inspect the referenced log and permissions without modifying it. Alternate paths can be supplied with --wtmp-file, --btmp-file and --lastlog when you have a known compatible copy. Do not create or truncate a log to make an audit pass. That would destroy evidence and change future login accounting.
If account categories look wrong, compare the UID thresholds with /etc/login.defs and check whether name-service configuration supplies users from LDAP, SSSD or another source. lslogins reports what the system's account databases expose; it is not a replacement for reviewing the service or identity provider configuration.
Done means
- You confirmed the executable, util-linux version and supported columns.
- You captured a read-only inventory with explicit fields.
- You checked local UID thresholds before labelling accounts as human or system accounts.
- You inspected a selected login and, where relevant, group membership and login history.
- You used export or NUL-delimited output deliberately rather than scraping aligned whitespace.
- You treated password details as sensitive and made no account, group or log changes.