# /usr/lib/sysusers.d/demoapp.conf
u! _demoapp - "Demo app -weight: 500;">service user"
g demoapp-data -
m _demoapp demoapp-data
# /usr/lib/sysusers.d/demoapp.conf
u! _demoapp - "Demo app -weight: 500;">service user"
g demoapp-data -
m _demoapp demoapp-data
# /usr/lib/sysusers.d/demoapp.conf
u! _demoapp - "Demo app -weight: 500;">service user"
g demoapp-data -
m _demoapp demoapp-data
tmpdir=$(mktemp -d)
mkdir -p "$tmpdir"/{etc,usr/lib}/sysusers.d cat >"$tmpdir/usr/lib/sysusers.d/demoapp.conf" <<'EOF'
u! _demoapp - "Demo app -weight: 500;">service user"
g demoapp-data -
m _demoapp demoapp-data
EOF systemd-sysusers \ --root="$tmpdir" \ --dry-run \ "$tmpdir/usr/lib/sysusers.d/demoapp.conf"
tmpdir=$(mktemp -d)
mkdir -p "$tmpdir"/{etc,usr/lib}/sysusers.d cat >"$tmpdir/usr/lib/sysusers.d/demoapp.conf" <<'EOF'
u! _demoapp - "Demo app -weight: 500;">service user"
g demoapp-data -
m _demoapp demoapp-data
EOF systemd-sysusers \ --root="$tmpdir" \ --dry-run \ "$tmpdir/usr/lib/sysusers.d/demoapp.conf"
tmpdir=$(mktemp -d)
mkdir -p "$tmpdir"/{etc,usr/lib}/sysusers.d cat >"$tmpdir/usr/lib/sysusers.d/demoapp.conf" <<'EOF'
u! _demoapp - "Demo app -weight: 500;">service user"
g demoapp-data -
m _demoapp demoapp-data
EOF systemd-sysusers \ --root="$tmpdir" \ --dry-run \ "$tmpdir/usr/lib/sysusers.d/demoapp.conf"
Creating group 'demoapp-data' with GID 999.
Creating group '_demoapp' with GID 998.
Creating user '_demoapp' (Demo app -weight: 500;">service user) with UID 998 and GID 998.
Would write /etc/group…
Would write /etc/gshadow…
Would write /etc/passwd…
Would write /etc/shadow…
Creating group 'demoapp-data' with GID 999.
Creating group '_demoapp' with GID 998.
Creating user '_demoapp' (Demo app -weight: 500;">service user) with UID 998 and GID 998.
Would write /etc/group…
Would write /etc/gshadow…
Would write /etc/passwd…
Would write /etc/shadow…
Creating group 'demoapp-data' with GID 999.
Creating group '_demoapp' with GID 998.
Creating user '_demoapp' (Demo app -weight: 500;">service user) with UID 998 and GID 998.
Would write /etc/group…
Would write /etc/gshadow…
Would write /etc/passwd…
Would write /etc/shadow…
rm -rf "$tmpdir"
rm -rf "$tmpdir"
rm -rf "$tmpdir"
# /usr/lib/sysusers.d/demoapp.conf
u! _demoapp - "Demo app -weight: 500;">service user"
g demoapp-data -
m _demoapp demoapp-data
# /usr/lib/sysusers.d/demoapp.conf
u! _demoapp - "Demo app -weight: 500;">service user"
g demoapp-data -
m _demoapp demoapp-data
# /usr/lib/sysusers.d/demoapp.conf
u! _demoapp - "Demo app -weight: 500;">service user"
g demoapp-data -
m _demoapp demoapp-data
u! _demoapp 450 "Demo app -weight: 500;">service user"
g demoapp-data 451
m _demoapp demoapp-data
u! _demoapp 450 "Demo app -weight: 500;">service user"
g demoapp-data 451
m _demoapp demoapp-data
u! _demoapp 450 "Demo app -weight: 500;">service user"
g demoapp-data 451
m _demoapp demoapp-data
tmpdir=$(mktemp -d)
mkdir -p "$tmpdir"/{etc,usr/lib}/sysusers.d cat >"$tmpdir/usr/lib/sysusers.d/demoapp.conf" <<'EOF'
u! _demoapp - "Demo app -weight: 500;">service user"
g demoapp-data -
m _demoapp demoapp-data
EOF cat >"$tmpdir/etc/sysusers.d/demoapp.conf" <<'EOF'
u! _demoapp 450 "Demo app -weight: 500;">service user"
g demoapp-data 451
m _demoapp demoapp-data
EOF systemd-sysusers --root="$tmpdir" --dry-run demoapp.conf
tmpdir=$(mktemp -d)
mkdir -p "$tmpdir"/{etc,usr/lib}/sysusers.d cat >"$tmpdir/usr/lib/sysusers.d/demoapp.conf" <<'EOF'
u! _demoapp - "Demo app -weight: 500;">service user"
g demoapp-data -
m _demoapp demoapp-data
EOF cat >"$tmpdir/etc/sysusers.d/demoapp.conf" <<'EOF'
u! _demoapp 450 "Demo app -weight: 500;">service user"
g demoapp-data 451
m _demoapp demoapp-data
EOF systemd-sysusers --root="$tmpdir" --dry-run demoapp.conf
tmpdir=$(mktemp -d)
mkdir -p "$tmpdir"/{etc,usr/lib}/sysusers.d cat >"$tmpdir/usr/lib/sysusers.d/demoapp.conf" <<'EOF'
u! _demoapp - "Demo app -weight: 500;">service user"
g demoapp-data -
m _demoapp demoapp-data
EOF cat >"$tmpdir/etc/sysusers.d/demoapp.conf" <<'EOF'
u! _demoapp 450 "Demo app -weight: 500;">service user"
g demoapp-data 451
m _demoapp demoapp-data
EOF systemd-sysusers --root="$tmpdir" --dry-run demoapp.conf
tmpdir=$(mktemp -d)
mkdir -p "$tmpdir/etc/sysusers.d" systemd-sysusers \ --root="$tmpdir" \ --dry-run \ --inline \ 'u! _cachebot - "Cache Bot" /var/lib/cachebot /usr/sbin/nologin' \ 'g cachebot-data -' \ 'm _cachebot cachebot-data'
tmpdir=$(mktemp -d)
mkdir -p "$tmpdir/etc/sysusers.d" systemd-sysusers \ --root="$tmpdir" \ --dry-run \ --inline \ 'u! _cachebot - "Cache Bot" /var/lib/cachebot /usr/sbin/nologin' \ 'g cachebot-data -' \ 'm _cachebot cachebot-data'
tmpdir=$(mktemp -d)
mkdir -p "$tmpdir/etc/sysusers.d" systemd-sysusers \ --root="$tmpdir" \ --dry-run \ --inline \ 'u! _cachebot - "Cache Bot" /var/lib/cachebot /usr/sbin/nologin' \ 'g cachebot-data -' \ 'm _cachebot cachebot-data'
printf '%s\n' 'u! _radvd - "radvd daemon"' \ | systemd-sysusers --dry-run --replace=/usr/lib/sysusers.d/radvd.conf -
printf '%s\n' 'u! _radvd - "radvd daemon"' \ | systemd-sysusers --dry-run --replace=/usr/lib/sysusers.d/radvd.conf -
printf '%s\n' 'u! _radvd - "radvd daemon"' \ | systemd-sysusers --dry-run --replace=/usr/lib/sysusers.d/radvd.conf -
# /etc/systemd/system/demoapp.-weight: 500;">service
[Service]
User=_demoapp
Group=_demoapp
StateDirectory=demoapp
CacheDirectory=demoapp
LogsDirectory=demoapp
# /etc/systemd/system/demoapp.-weight: 500;">service
[Service]
User=_demoapp
Group=_demoapp
StateDirectory=demoapp
CacheDirectory=demoapp
LogsDirectory=demoapp
# /etc/systemd/system/demoapp.-weight: 500;">service
[Service]
User=_demoapp
Group=_demoapp
StateDirectory=demoapp
CacheDirectory=demoapp
LogsDirectory=demoapp
# /usr/lib/tmpfiles.d/demoapp.conf
d /var/lib/demoapp 0750 _demoapp _demoapp - -
d /var/log/demoapp 0750 _demoapp _demoapp - -
# /usr/lib/tmpfiles.d/demoapp.conf
d /var/lib/demoapp 0750 _demoapp _demoapp - -
d /var/log/demoapp 0750 _demoapp _demoapp - -
# /usr/lib/tmpfiles.d/demoapp.conf
d /var/lib/demoapp 0750 _demoapp _demoapp - -
d /var/log/demoapp 0750 _demoapp _demoapp - -
systemd-tmpfiles --create --prefix=/var/lib/demoapp --prefix=/var/log/demoapp
systemd-tmpfiles --create --prefix=/var/lib/demoapp --prefix=/var/log/demoapp
systemd-tmpfiles --create --prefix=/var/lib/demoapp --prefix=/var/log/demoapp - define -weight: 500;">service users and groups with sysusers.d
- validate changes safely with --dry-run
- understand override precedence between /usr/lib and /etc
- use --replace for packaging workflows
- pair account creation with the right directory-management approach - daemon accounts like _demoapp or postgres
- package -weight: 500;">install or image-build workflows
- reproducible -weight: 500;">service identities across machines
- first-boot or offline-root provisioning with --root or --image - declarative, the desired account state lives in a file
- auditable, you can see exactly which identities a package or -weight: 500;">service expects
- override-friendly, admins can replace vendor defaults in /etc/sysusers.d
- testable, systemd-sysusers --dry-run shows what would happen before anything is written - /etc/sysusers.d/*.conf
- /run/sysusers.d/*.conf
- /usr/local/lib/sysusers.d/*.conf
- /usr/lib/sysusers.d/*.conf - /etc/sysusers.d overrides /run/sysusers.d and /usr/lib/sysusers.d
- /run/sysusers.d overrides /usr/lib/sysusers.d
- vendor packages should -weight: 500;">install files in /usr/lib/sysusers.d
- local admin overrides belong in /etc/sysusers.d - u to create a system user, and implicitly a same-named group
- g to create a group
- m to add a user to a group
- r to define a UID/GID allocation range - creates a locked system user named _demoapp
- lets systemd allocate a UID automatically
- creates a supplemental group called demoapp-data
- adds _demoapp to that group - u! is preferable for most daemon accounts because it creates a fully locked account
- - in the ID field means automatic UID/GID allocation
- prefixing -weight: 500;">service accounts with _ is strongly recommended by the docs to avoid clashes with human users - experimenting during packaging work
- verifying syntax in CI
- teaching teammates what each field does - Use automatic UID/GID allocation unless you truly need fixed numbers. The docs strongly recommend - for most cases.
- Prefix daemon accounts with _. This reduces collision risk with human users.
- Prefer u! for -weight: 500;">service identities. Locked accounts are safer for daemons.
- Keep vendor config in /usr/lib/sysusers.d, local policy in /etc/sysusers.d. That is the intended split.
- Pair accounts with -weight: 500;">service-level directories or tmpfiles. Do not assume the home or state path will magically appear.
- Use --dry-run in CI and image builds. It is cheap and catches bad assumptions early. - systemd-sysusers is better for packages, shared file ownership, and predictable account names
- DynamicUser= is better for tighter isolation when persistence is unnecessary - systemd-sysusers(8) man page: https://man7.org/linux/man-pages/man8/systemd-sysusers.8.html
- sysusers.d(5) man page: https://man7.org/linux/man-pages/man5/sysusers.d.5.html
- systemd upstream notes on UID/GID ranges: https://systemd.io/UIDS-GIDS/
- systemd upstream notes on user/group naming: https://systemd.io/USER_NAMES/
- tmpfiles.d(5) man page: https://man7.org/linux/man-pages/man5/tmpfiles.d.5.html
- systemd.exec(5) for StateDirectory= and related directives: https://man7.org/linux/man-pages/man5/systemd.exec.5.html