Home / Alt manpages / systemd-sysusers(8)

  • systemd-sysusers(8)
  • Admin command
  • linux

Declare a Locked-Down Service Account with systemd-sysusers

A hand-edited /etc/passwd line for a service account never survives an image rebuild, but a systemd-sysusers definition does. You will create a repeatable sysusers.d definition, preview the resulting user and group allocation, apply it, and verify the account without touching the account files by hand. The examples are for systemd 255, installed here as Debian package systemd 255.4-1ubuntu8.17.

Allow about fifteen minutes. You need a shell and a package that provides systemd-sysusers. Reading and previewing configuration is normally unprivileged. Writing a system configuration and allocating an account require elevated privileges. This guide creates a system account, not a normal human login, and deliberately gives it no usable login shell.

Checkpoint

If you already have a configuration file, start at step 3. If you are only checking a vendor file, step 2 is enough.

1. Confirm the installed implementation

Check the binary and package version before relying on any option. This matters because features such as --dry-run and --inline were added in different systemd releases:

$ command -v systemd-sysusers
/usr/bin/systemd-sysusers
$ systemd-sysusers --version
systemd 255 (255.4-1ubuntu8.17)

The command is also the implementation behind systemd-sysusers.service. It reads declarative files from /etc/sysusers.d, /run/sysusers.d and /usr/lib/sysusers.d. The local administrator's /etc files take priority over files with the same name elsewhere.

2. Inspect existing definitions before adding one

With no configuration-file argument, systemd-sysusers considers every discovered definition. Use --cat-config to see the files and their comments without applying them:

$ systemd-sysusers --cat-config --no-pager | sed -n '1,30p'
# /usr/lib/sysusers.d/basic.conf
# generated from /usr/share/base-passwd/{passwd,group}.master
g adm        4     -
g tty        5     -
g disk       6     -

Your list will differ. Use --tldr instead when comments and blank lines are distracting. Do not assume a name is unused just because its vendor file is hard to find: verify with getent passwd NAME and getent group NAME before choosing an account name.

Names may contain letters, digits, underscores and hyphens, but cannot start with a digit or hyphen and are limited to 31 characters. A leading underscore makes a collision with an administrator-created human account less likely, so this guide uses _reporter.

3. Write one small configuration file

Put local administrator configuration in /etc/sysusers.d. The file name should identify the package or service and end in .conf. The following definition requests an automatically allocated UID and GID, a descriptive GECOS field, and the default non-login shell:

$ sudo install -d -m 0755 /etc/sysusers.d
$ sudo sh -c 'printf "%s\n" "u _reporter - \"Report worker\" - /usr/sbin/nologin" > /etc/sysusers.d/report-worker.conf'
$ sudo chmod 0644 /etc/sysusers.d/report-worker.conf
$ sudo sed -n '1,5p' /etc/sysusers.d/report-worker.conf
u _reporter - "Report worker" - /usr/sbin/nologin

A u line creates the user and a matching primary group if they do not already exist. A hyphen in the ID field requests automatic allocation from systemd's pool. The explicit shell keeps the intent visible; if you omit the shell column, systemd normally uses /usr/sbin/nologin for a non-root account anyway. The home-directory hyphen means no useful home directory is requested.

Do not put a password in this file. A u account is created disabled, and service accounts should never be made interactive just to make a test convenient.

4. Preview the change

Run the exact file through --dry-run before applying it. The command processes the configuration but never writes account databases:

$ systemd-sysusers --dry-run /etc/sysusers.d/report-worker.conf
Creating group '_reporter' with GID 981.
Creating user '_reporter' (Report worker) with UID 981 and GID 981.
Would write /etc/group...
Would write /etc/gshadow...
Would write /etc/passwd...
Would write /etc/shadow...

The numeric ID is host-dependent and may differ from the example; the preview may also mention fewer changes if the account already exists. Treat any unexpected existing name, fixed ID, or configuration file as a stop-and-investigate result. A dry run is not a reservation: another account allocation can claim an ID before the real run happens.

For a throwaway syntax check, you can skip creating a file entirely. On systemd 255, --inline treats each positional argument as a configuration line:

$ systemd-sysusers --dry-run --inline 'u _preview - "Preview only"'
Creating group '_preview' with GID 981.
Creating user '_preview' (Preview only) with UID 981 and GID 981.
Would write /etc/group...

Output formatting and allocated numbers vary. What matters is the account and group names, the proposed allocation, and the Would write messages.

5. Apply the definition

Once the preview matches what you intend, apply only the file you reviewed:

$ sudo systemd-sysusers /etc/sysusers.d/report-worker.conf
$ getent passwd _reporter
_reporter:x:981:981:Report worker:/usr/sbin:/usr/sbin/nologin
$ getent group _reporter
_reporter:x:981:

The UID, GID and exact passwd-field rendering are host-specific. A successful command returns status 0, and both getent queries should return records. Running the command again is normally harmless: sysusers is designed to do nothing for users, groups and memberships that already exist.

Warning

This changes persistent account databases. There is no general inverse command that safely removes an account and every file owned by its UID. If the definition was wrong, first stop the service that might use it, preserve evidence about the allocated UID, remove or correct the configuration file, and let an administrator decide whether account removal is appropriate. Do not blindly run userdel or delete files by numeric ownership.

6. Add a supplementary group only when required

If the worker genuinely needs access to a device or another service-owned resource, use a separate m line to add it to an existing group:

u _reporter - "Report worker" - /usr/sbin/nologin
m _reporter input

An m line can implicitly create a missing user or group, so check both names before applying a change that grants access. Membership is an authorisation decision, not a harmless convenience. Prefer the narrowest existing group, and never add a service account to broad groups such as sudo, adm or docker without documenting the resulting privilege.

After changing membership, rerun the file and verify it with:

$ getent group input
input:x:999:_reporter

The group ID and other members vary. If the account is already in the group, sysusers normally leaves it alone.

7. Understand precedence and overrides

All files are sorted lexicographically across the three directories. An administrator file in /etc/sysusers.d overrides a vendor file with the same name, while a symlink from /etc/sysusers.d to /dev/null disables a vendor file with that name. Earlier entries for the same user or group win, and later conflicting entries are only logged as warnings.

Keep overrides deliberate and inspect them before applying the full set:

$ ls -l /etc/sysusers.d /run/sysusers.d /usr/lib/sysusers.d
$ systemd-sysusers --cat-config --no-pager | less

Do not edit a vendor file under /usr/lib/sysusers.d; package upgrades can replace it outright. If you must suppress one, record why the /dev/null symlink exists and test the package's service after the next upgrade.

8. Keep home directories and disk-image work separate

sysusers records a home directory in the user database but does not create that directory itself. If an application needs one, create it with a matching tmpfiles.d definition and set ownership there, rather than assuming the u line creates it for you.

For an offline root filesystem, --root=/path/to/root prefixes paths and configuration searches with that directory. --image=/path/to/image targets a disk image instead. These options can modify a different system from the one named in your prompt, so confirm the mount or image and take a backup before using them. They are not needed for the host-local example in this guide.

Done means

  • Version and syntax checked: the installed systemd version and command syntax were confirmed.
  • Name checked both ways: the account name was checked against both passwd and group databases.
  • Definition owned by admin: it lives in an administrator-owned /etc/sysusers.d/*.conf file.
  • Dry run reviewed: a preview was checked before any account database changed.
  • Accounts confirmed: getent passwd and getent group confirm the resulting records.
  • No shell, minimal groups: the account has no interactive shell and only explicitly justified group membership.
  • Safe correction plan: any fix preserves the account's UID and avoids blind deletion of owned files.