SYSTEMD-ANALYZE(1)

๐Ÿ’– SYSTEMD-ANALYZE(1) ๐Ÿ’–

okay bestie, so this is the manpage for systemd-analyze โ€” yes, that's the manual name, and yes, it's giving main character energy for your whole boot process. she analyzes, she debugs, she tells you why your system is being slow. let's go โœจ

๐ŸŒธ NAME ๐ŸŒธ

systemd-analyze โ€” Analyze and debug system manager

๐ŸŽ€ SYNOPSIS ๐ŸŽ€

systemd-analyze [OPTIONS...] [time]

systemd-analyze [OPTIONS...] blame

systemd-analyze [OPTIONS...] critical-chain [UNIT...]

systemd-analyze [OPTIONS...] dump [PATTERN...]

systemd-analyze [OPTIONS...] plot [>file.svg]

systemd-analyze [OPTIONS...] dot [PATTERN...] [>file.dot]

systemd-analyze [OPTIONS...] unit-files

systemd-analyze [OPTIONS...] unit-gdb SERVICE

systemd-analyze [OPTIONS...] unit-paths

systemd-analyze [OPTIONS...] unit-shell SERVICE [COMMAND...]

systemd-analyze [OPTIONS...] exit-status [STATUS...]

systemd-analyze [OPTIONS...] capability [CAPABILITY... |
                {-m | --mask} MASK]

systemd-analyze [OPTIONS...] condition CONDITION...

systemd-analyze [OPTIONS...] syscall-filter [SET...]

systemd-analyze [OPTIONS...] filesystems [SET...]

systemd-analyze [OPTIONS...] calendar SPEC...

systemd-analyze [OPTIONS...] timestamp TIMESTAMP...

systemd-analyze [OPTIONS...] timespan SPAN...

systemd-analyze [OPTIONS...] cat-config NAME|PATH...

systemd-analyze [OPTIONS...] compare-versions VERSION1 [OP] VERSION2

systemd-analyze [OPTIONS...] verify FILE...

systemd-analyze [OPTIONS...] security [UNIT...]

systemd-analyze [OPTIONS...] inspect-elf FILE...

systemd-analyze [OPTIONS...] malloc [D-BUS SERVICE...]

systemd-analyze [OPTIONS...] fdstore UNIT...

systemd-analyze [OPTIONS...] image-policy POLICY...

systemd-analyze [OPTIONS...] has-tpm2

systemd-analyze [OPTIONS...] identify-tpm2

systemd-analyze [OPTIONS...] pcrs [PCR...]

systemd-analyze [OPTIONS...] nvpcrs [NVPCR...]

systemd-analyze [OPTIONS...] srk [>FILE]

systemd-analyze [OPTIONS...] architectures [NAME...]

systemd-analyze [OPTIONS...] smbios11

systemd-analyze [OPTIONS...] chid

systemd-analyze [OPTIONS...] transient-settings TYPE...

โœจ DESCRIPTION โœจ

so like. systemd-analyze is your bestie for figuring out how long your system took to boot up, grabbing other state and tracing info from the system and the service manager, and checking that your unit files are actually correct. she also unlocks some special functions that are very useful for advanced system manager debugging. iykyk ๐Ÿ’…

and if you pass no command at all? it just implies systemd-analyze time. no thoughts, just time. periodt.

โฑ๏ธ systemd-analyze time โฑ๏ธ

this one prints three things: how much time got spent in the kernel before userspace was even reached, how much time got spent in the initrd before the normal system userspace was reached, and how long the normal system userspace took to initialize. ๐Ÿซถ

heads up bestie โ€” these measurements are just measuring the time up to the point where all system services have been spawned. that's NOT necessarily the same as them fully finishing initialization or the disk being idle. so don't come for her if the number looks cute and the system is still grinding ๐Ÿ™ˆ

okay so if the manager carried shutdown timestamps across a kexec-based live update (i.e. they were restored from the previous boot through the Live Update Orchestrator), then the previous boot's shutdown phases get prepended to the chain. so you see the whole shutdown plus bootup sequence, and it all gets summed into the total: the time spent stopping units ("(stopping)"), the time the manager took to hand off to systemd-shutdown(8) ("(exit)"), and the time spent in systemd-shutdown itself ("(systemd-shutdown)") all get added before the firmware/loader/kernel/initrd/userspace phases. the way that stack just keeps growing ๐Ÿ˜ญ

Example 1. Show how long the boot took

# in a container
$ systemd-analyze time
Startup finished in 296ms (userspace)
multi-user.target reached after 275ms in userspace

# on a real machine
$ systemd-analyze time
Startup finished in 2.584s (kernel) + 19.176s (initrd) + 47.847s (userspace) = 1min 9.608s
multi-user.target reached after 47.820s in userspace

๐Ÿซฃ systemd-analyze blame ๐Ÿซฃ

this command prints a list of all running units, sorted by how long they took to initialize. use it to optimize your boot-up times, bestie. she's basically a leaderboard for slow services ๐Ÿ†

but ngl, the output might be misleading, because one service's initialization might be slow simply because it's waiting for another service to finish initializing. also note: systemd-analyze blame does not display results for services with Type=simple, because systemd considers those services to be started immediately โ€” so no measurement of the initialization delays can even be done. ๐Ÿ’€

and another thing: this command only shows the time units took for starting up, it does NOT show how long unit jobs spent in the execution queue. specifically, it shows the time units spent in the "activating" state, which isn't even defined for units like device units that transition directly from "inactive" to "active". so this command gives you a vibe check on the performance of program code, but it can't accurately reflect latency introduced by waiting for hardware and similar events. fr fr.

Example 2. Show which units took the most time during boot

$ systemd-analyze blame
         32.875s pmlogger.service
         20.905s systemd-networkd-wait-online.service
         13.299s dev-vda1.device
         ...
            23ms sysroot.mount
            11ms initrd-udevadm-cleanup-db.service
             3ms sys-kernel-config.mount

๐ŸŒณ systemd-analyze critical-chain [UNIT...] ๐ŸŒณ

this one prints a tree of the time-critical chain of units (for each of the UNITs you specify, or for the default target if you don't specify any). the time after the unit is active or started gets printed after the "@" character. the time the unit takes to start gets printed after the "+" character. iconic formatting tbh.

but like, the output might be misleading, because the initialization of services might depend on socket activation and because of the parallel execution of units. also, same as blame, this only takes into account the time units spent in "activating" state, so it doesn't cover units that never went through an "activating" state (like device units that transition directly from "inactive" to "active"). and on top of that, it doesn't show info on jobs โ€” and in particular not jobs that timed out. ๐Ÿ˜ญ

Example 3. systemd-analyze critical-chain

$ systemd-analyze critical-chain
multi-user.target @47.820s
โ””โ”€pmie.service @35.968s +548ms
  โ””โ”€pmcd.service @33.715s +2.247s
    โ””โ”€network-online.target @33.712s
      โ””โ”€systemd-networkd-wait-online.service @12.804s +20.905s
        โ””โ”€systemd-networkd.service @11.109s +1.690s
          โ””โ”€systemd-udevd.service @9.201s +1.904s
            โ””โ”€systemd-tmpfiles-setup-dev.service @7.306s +1.776s
              โ””โ”€kmod-static-nodes.service @6.976s +177ms
                โ””โ”€systemd-journald.socket
                  โ””โ”€system.slice
                    โ””โ”€-.slice

๐Ÿ” systemd-analyze dump [pattern...] ๐Ÿ”

no parameters? then this command outputs a (usually very long) human-readable serialization of the complete service manager state. you can optionally pass a glob pattern, which limits the output to units whose names match one of the patterns. ๐Ÿฆ‹

โš ๏ธ the output format is subject to change without notice and should not be parsed by applications. also, this command is rate limited for unprivileged users. so no scripting against it, bestie, i'm serious.

Example 4. Show the internal state of user manager

$ systemd-analyze --user dump
Timestamp userspace: Thu 2019-03-14 23:28:07 CET
Timestamp finish: Thu 2019-03-14 23:28:07 CET
Timestamp generators-start: Thu 2019-03-14 23:28:07 CET
Timestamp generators-finish: Thu 2019-03-14 23:28:07 CET
Timestamp units-load-start: Thu 2019-03-14 23:28:07 CET
Timestamp units-load-finish: Thu 2019-03-14 23:28:07 CET
-> Unit proc-timer_list.mount:
        Description: /proc/timer_list
        ...
-> Unit default.target:
        Description: Main user target
...

๐Ÿง  systemd-analyze malloc [D-Bus service...] ๐Ÿง 

this command requests the output of the internal memory state (as returned by malloc_info(3)) of a D-Bus service. if you don't specify a service, the query goes to org.freedesktop.systemd1 (that's the system or user service manager).

the output format is not guaranteed to be stable and should not be parsed by applications. nope nope nope.

the service must implement the org.freedesktop.MemoryAllocation1 interface. in the systemd suite, it's currently only implemented by the manager. so like, it's exclusive ๐Ÿ’…

๐Ÿ“ˆ systemd-analyze plot ๐Ÿ“ˆ

this command prints either an SVG graphic detailing which system services have been started at what time (highlighting the time they spent on initialization), OR the raw time data in JSON or table format. choose your fighter โœจ

Example 5. Plot a bootchart

$ systemd-analyze plot >bootup.svg
$ eog bootup.svg&

note that this plot is based on the most recent per-unit timing data of loaded units. which means: if a unit gets started, then stopped, then started again, the info shown covers the most recent start cycle, not the first one. so it's recommended to consult this info only shortly after boot, so that this distinction doesn't matter. ๐ŸŽ€

and also โ€” units that aren't referenced by any other unit through a dependency might get unloaded by the service manager once they terminate (and didn't fail). those units will not show up in the plot. rip to them, they're not invited ๐Ÿซถ

๐Ÿ•ธ๏ธ systemd-analyze dot [pattern...] ๐Ÿ•ธ๏ธ

this command generates a textual dependency graph description in dot format for further processing with the GraphViz dot(1) tool. use a command line like systemd-analyze dot | dot -Tsvg >systemd.svg to generate a graphical dependency tree. unless --order or --require is passed, the generated graph will show both ordering and requirement dependencies. you can give optional pattern globbing style specifications (e.g. *.target) at the end. a unit dependency is included in the graph if any of these patterns match either the origin or the destination node. obsessed with how flexible this is ๐ŸŒˆ

Example 6. Plot all dependencies of any unit whose name starts with "avahi-daemon"

$ systemd-analyze dot 'avahi-daemon.*' | dot -Tsvg >avahi.svg
$ eog avahi.svg

Example 7. Plot the dependencies between all known target units

$ systemd-analyze dot --to-pattern='*.target' --from-pattern='*.target' \
      | dot -Tsvg >targets.svg
$ eog targets.svg

๐Ÿ“‚ systemd-analyze unit-paths ๐Ÿ“‚

this command outputs a list of all the directories from which unit files, .d overrides, and .wants, .requires symlinks may be loaded. combine it with --user to retrieve the list for the user manager instance, and --global for the global configuration of user manager instances. ๐Ÿ’–

Example 8. Show all paths for generated units

$ systemd-analyze unit-paths | grep '^/run'
/run/systemd/system.control
/run/systemd/transient
/run/systemd/generator.early
/run/systemd/system
/run/systemd/system.attached
/run/systemd/generator
/run/systemd/generator.late

and that's part 1 bestie!! there's SO much more coming, don't go anywhere ๐Ÿ’œ

Note that this verb prints the list that's compiled into systemd-analyze itself โ€” it does not talk to the running manager at all, bestie. If you want the list your manager is actually working with, run this instead:

systemctl [--user] [--global] show -p UnitPath --value

...and any empty directories just get dropped from it. ๐Ÿ’…


systemd-analyze exit-status [STATUS...]

This one hands you a list of exit statuses, each with its "class" โ€” as in, where that definition came from (it's one of "libc", "systemd", "LSB", or "BSD"). Full deets live in the Process Exit Codes section of systemd.exec(5). Pass no extra arguments and it dumps every known status. Pass some, and you only get the definitions for the codes you asked about. โœจ

Example 9. Show some example exit status names

$ systemd-analyze exit-status 0 1 {63..65}
NAME    STATUS CLASS
SUCCESS 0      libc
FAILURE 1      libc
-       63     -
USAGE   64     BSD
DATAERR 65     BSD

systemd-analyze capability [CAPABILITY... | {-m | --mask} MASK]

Linux capabilities, plus their numeric IDs, served up fresh. See capabilities(7) if you want the lore. No argument? You get the whole list of capabilities known to the service manager and the kernel. Anything the kernel defines but the service manager has never heard of shows up as "cap_???" โ€” iconically mysterious. ๐Ÿ™ˆ

Optionally you can pass arguments naming specific capabilities by name or numeric ID, and then only those show up in the table.

OR โ€” and this is the fun part โ€” if you pass --mask, you must give exactly one numeric argument, which gets read as a hexadecimal capability mask. Then only the capabilities present in that mask appear. This mode exists to help you decode capability sets you pull out of debugging interfaces like /proc/PID/status. ๐Ÿ•ต๏ธ

Example 10. Show some example capability names

$ systemd-analyze capability 0 1 {30..32}
NAME              NUMBER
cap_chown              0
cap_dac_override       1
cap_audit_control     30
cap_setfcap           31
cap_mac_override      32

Example 11. Decode a capability mask extracted from /proc

$ systemd-analyze capability -m 0000000000003c00
NAME                 NUMBER
cap_net_bind_service     10
cap_net_broadcast        11
cap_net_admin            12
cap_net_raw              13

systemd-analyze condition CONDITION...

Feed it Condition*=... and Assert*=... assignments and it will evaluate them, print their values, and then tell you the resulting value of the whole combined condition set. The full menu of conditions and asserts lives in systemd.unit(5). ๐ŸŽ€

Example 12. Evaluate conditions that check kernel versions

$ systemd-analyze condition 'ConditionVersion = ! <4.0' \
        'ConditionVersion = >=5.1' \
        'ConditionACPower=|false' \
        'ConditionArchitecture=|!arm' \
        'AssertPathExists=/etc/os-release'
test.service: AssertPathExists=/etc/os-release succeeded.
Asserts succeeded.
test.service: ConditionArchitecture=|!arm succeeded.
test.service: ConditionACPower=|false failed.
test.service: ConditionVersion=>=5.1 succeeded.
test.service: ConditionVersion=!<4.0 succeeded.
Conditions succeeded.

systemd-analyze syscall-filter [SET...]

Lists the system calls sitting inside the system call set SET you name โ€” or every known set, if you name none. Heads up: SET must include the "@" prefix, no exceptions. ๐Ÿ’…


systemd-analyze filesystems [SET...]

Same energy, different table: lists the filesystems in the filesystem set SET you name, or all known sets if you name none. And yep โ€” SET must include the "@" prefix. โœจ


systemd-analyze calendar EXPRESSION...

Parses and normalizes repetitive calendar time events, then works out when they elapse next. It takes exactly the same input as the OnCalendar= setting in systemd.timer(5), using the syntax from systemd.time(7). By default you just get the next elapse time โ€” but hit it with --iterations= and you'll get however many upcoming elapse times you asked for. Each time the expression elapses forms a timestamp, and there's a timestamp verb for those (scroll down, bestie). ๐Ÿฆ‹

Example 13. Show leap days in the near future

$ systemd-analyze calendar --iterations=5 '*-2-29 0:0:0'
  Original form: *-2-29 0:0:0
Normalized form: *-02-29 00:00:00
    Next elapse: Sat 2020-02-29 00:00:00 UTC
       From now: 11 months 15 days left
       Iter. #2: Thu 2024-02-29 00:00:00 UTC
       From now: 4 years 11 months left
       Iter. #3: Tue 2028-02-29 00:00:00 UTC
       From now: 8 years 11 months left
       Iter. #4: Sun 2032-02-29 00:00:00 UTC
       From now: 12 years 11 months left
       Iter. #5: Fri 2036-02-29 00:00:00 UTC
       From now: 16 years 11 months left

systemd-analyze timestamp TIMESTAMP...

Parses a timestamp โ€” one single point in time, not a span โ€” and spits out the normalized form plus the difference between it and now. Your timestamp needs to follow the syntax in systemd.time(7), section "PARSING TIMESTAMPS". ๐ŸŒธ

Example 14. Show parsing of timestamps

$ systemd-analyze timestamp yesterday now tomorrow
  Original form: yesterday
Normalized form: Mon 2019-05-20 00:00:00 CEST
       (in UTC): Sun 2019-05-19 22:00:00 UTC
   UNIX seconds: @15583032000
       From now: 1 day 9h ago

  Original form: now
Normalized form: Tue 2019-05-21 09:48:39 CEST
       (in UTC): Tue 2019-05-21 07:48:39 UTC
   UNIX seconds: @1558424919.659757
       From now: 43us ago

  Original form: tomorrow
Normalized form: Wed 2019-05-22 00:00:00 CEST
       (in UTC): Tue 2019-05-21 22:00:00 UTC
   UNIX seconds: @15584760000
       From now: 14h left

systemd-analyze timespan EXPRESSION...

Now for spans โ€” the difference between two timestamps. It parses it, prints the normalized form, and gives you the equivalent value in microseconds. The syntax you need is in systemd.time(7), section "PARSING TIME SPANS". Oh and btw: any value you give without units gets parsed as seconds. ๐Ÿ‘‘

Example 15. Show parsing of timespans

$ systemd-analyze timespan 1s 300s '1year 0.000001s'
Original: 1s
      ฮผs: 1000000
   Human: 1s

Original: 300s
      ฮผs: 300000000
   Human: 5min

Original: 1year 0.000001s
      ฮผs: 31557600000001
   Human: 1y 1us

systemd-analyze cat-config NAME|PATH...

Basically systemctl cat but for config files, and honestly? Iconic. It copies the contents of a config file and any drop-ins to standard output, using the usual systemd directories and the usual precedence rules. Every argument has to be either an absolute path that includes the prefix (like /etc/systemd/logind.conf or /usr/lib/systemd/logind.conf), or a name relative to the prefix (like systemd/logind.conf). ๐ŸŽ€

Example 16. Showing logind configuration

$ systemd-analyze cat-config systemd/logind.conf
# /etc/systemd/logind.conf
...
[Login]
NAutoVTs=8
...

# /usr/lib/systemd/logind.conf.d/20-test.conf
... some override from another package

# /etc/systemd/logind.conf.d/50-override.conf
... some administrator override

systemd-analyze compare-versions VERSION1 [OP] VERSION2

Two whole modes of operation here, depending on whether you pass the operator OP. ๐Ÿ”ฅ

Mode one โ€” no OP: it compares the two version strings and prints whichever fits: "VERSION1 < VERSION2", "VERSION1 == VERSION2", or "VERSION1 > VERSION2".

The exit status is 0 if the versions are equal, 11 if the version on the right is smaller, and 12 if the version on the left is smaller. (Yes, that matches the convention rpmdev-vercmp uses. ๐Ÿ˜Œ)

Mode two โ€” OP given: it compares the two version strings using the operation OP and returns 0 (success) if the condition is satisfied, and 1 (failure) otherwise. OP may be lt, le, eq, ne, ge, gt. In this mode, nothing gets printed. (And that matches the convention dpkg(1) --compare-versions uses.)

Example 17. Compare versions of a package

$ systemd-analyze compare-versions systemd-250~rc1.fc36.aarch64 systemd-251.fc36.aarch64
systemd-250~rc1.fc36.aarch64 < systemd-251.fc36.aarch64
$ echo $?
12

$ systemd-analyze compare-versions 1 lt 2; echo $?
0
$ systemd-analyze compare-versions 1 ge 2; echo $?
1

systemd-analyze verify FILE...

Loads unit files and prints warnings if it detects any errors. Files you name on the command line get loaded โ€” but so does anything else they reference. You can override a unit's name on disk by specifying an alias after a colon (there's an example below, keep reading ๐Ÿ‘€). The full unit search path is built by combining the directories of all the command line arguments with the usual unit load paths. The variable $SYSTEMD_UNIT_PATH is supported and can replace or augment the compiled-in set of unit load paths โ€” see systemd.unit(5). Any unit files sitting in the directories that hold your command line arguments get used in preference to the other paths. And if you hand it a template unit with no instance name (like foo@.service), "test_instance" will be used as the instance name โ€” you can steer that with the --instance= option. ๐Ÿ’œ

Here's what it currently detects:

  • unknown sections and directives,
  • missing dependencies that are required to start the given unit,
  • man pages listed in Documentation= that aren't found on the system,
  • commands listed in ExecStart= and similar that aren't found on the system, or aren't executable.

Example 18. Misspelt directives

$ cat ./user.slice
[Unit]
WhatIsThis=11
Documentation=man:nosuchfile(1)
Requires=different.service

[Service]
Description=x

$ systemd-analyze verify ./user.slice
[./user.slice:9] Unknown lvalue 'WhatIsThis' in section 'Unit'
[./user.slice:13] Unknown section 'Service'. Ignoring.
Error: org.freedesktop.systemd1.LoadFailed:
   Unit different.service failed to load:
   No such file or directory.
Failed to create user.slice/start: Invalid argument
user.slice: man nosuchfile(1) command failed with code 16

Example 19. Missing service units

$ tail ./a.socket ./b.socket
==> ./a.socket <==
[Socket]
ListenStream=100

==> ./b.socket <==
[Socket]
ListenStream=100
Accept=yes

๐ŸŽ€ Example 20. Aliasing a unit ๐ŸŽ€

ok so picture this: you got a file that's just vibing with no proper name. bestie, watch what happens ๐Ÿ‘€

$ cat /tmp/source
[Unit]
Description=Hostname printer

[Service]
Type=simple
ExecStart=/usr/bin/echo %H
MysteryKey=true
$ systemd-analyze verify /tmp/source
Failed to prepare filename /tmp/source: Invalid argument

๐Ÿ˜ญ it literally refuses. it's giving "who even are you". BUT โ€” give it a name and it'll finally talk to you:

$ systemd-analyze verify /tmp/source:alias.service
alias.service:7: Unknown key name 'MysteryKey' in section 'Service', ignoring.

see?? the alias let it get all the way to line 7 and THEN it clocked the fake key. iconic behaviour honestly ๐Ÿ’…


๐Ÿ”’ systemd-analyze security [UNIT...] ๐Ÿ”’

okay so this one is the security check-up bestie, and it's lowkey my fave.

So: this command analyzes the security and sandboxing settings of one or more specified service units. If at least one unit name is specified, the security settings of the specified service units are inspected and a detailed analysis is shown. If no unit name is specified, all currently loaded, long-running service units are inspected and a terse table with results is shown.

Then it does the thing โœจ: the command checks for various security-related service settings, assigning each a numeric "exposure level" value, depending on how important a setting is. It then calculates an overall exposure level for the whole unit โ€” which is an estimation in the range 0.0...10.0 showing how exposed a service is security-wise.

  • High exposure levels = very little applied sandboxing ๐Ÿšจ
  • Low exposure levels = tight sandboxing and strongest security restrictions ๐Ÿซถ
โš ๏ธ big caveat time: this only analyzes the per-service security features systemd itself implements. That means any additional security mechanisms applied by the service code itself are not accounted for.

And do NOT misread the number, bestie. The exposure level determined this way should not be misunderstood: a high exposure level neither means that there is no effective sandboxing applied by the service code itself, nor that the service is actually vulnerable to remote or local attacks. High exposure levels do indicate however that most likely the service might benefit from additional settings applied to them.

Also please note โ€” and this is the part nobody reads and everyone should ๐Ÿ™ˆ โ€” that many of the security and sandboxing settings individually can be circumvented unless combined with others. For example, if a service retains the privilege to establish or undo mount points, many of the sandboxing options can be undone by the service code itself. Because of that it is essential that each service uses the most comprehensive and strict sandboxing and security settings possible. The tool will take into account some of these combinations and relationships between the settings, but not all.

And one more: also note that the security and sandboxing settings analyzed here only apply to the operations executed by the service code itself. If a service has access to an IPC system (such as D-Bus) it might request operations from other services that are not subject to the same restrictions. So any comprehensive security and sandboxing analysis is hence incomplete if the IPC access policy is not validated too. no cap ๐Ÿ’€


๐Ÿ’– Example 21. Analyze systemd-logind.service ๐Ÿ’–

$ systemd-analyze security --no-pager systemd-logind.service
  NAME                DESCRIPTION                              EXPOSURE
โœ— PrivateNetwork=     Service has access to the host's network      0.5
โœ— User=/DynamicUser=  Service runs as root user                     0.4
โœ— DeviceAllow=        Service has no device ACL                     0.2
โœ“ IPAddressDeny=      Service blocks all IP address ranges
...
โ†’ Overall exposure level for systemd-logind.service: 4.1 OK ๐Ÿ™‚

the โœ—'s are the lil side-eyes and the โœ“ is the lil gold star โญ and honestly 4.1 OK is such a mood.


๐Ÿงš systemd-analyze inspect-elf FILE... ๐Ÿงš

This command will load the specified files, and if they are ELF objects (executables, libraries, core files, etc.) it will parse the embedded packaging metadata, if any, and print it in a table or json format. See the Package Metadata for Executable Files[1] document for more information.

๐ŸŒธ Example 22. Print information about a core file as JSON ๐ŸŒธ

$ systemd-analyze inspect-elf --json=pretty \
        core.fsverity.1000.f77dac5dc161402aa44e15b7dd9dcf97.58561.1637106137000000
{
        "elfType" : "coredump",
        "elfArchitecture" : "AMD x86-64",
        "/home/bluca/git/fsverity-utils/fsverity" : {
                "type" : "deb",
                "name" : "fsverity-utils",
                "version" : "1.3-1",
                "buildId" : "7c895ecd2a271f93e96268f479fdc3c64a2ec4ee"
        },
        "/home/bluca/git/fsverity-utils/libfsverity.so.0" : {
                "type" : "deb",
                "name" : "fsverity-utils",
                "version" : "1.3-1",
                "buildId" : "b5e428254abf14237b0ae70ed85fffbb98a78f88"
        }
}

pretty json my beloved ๐ŸŽ€ (don't forget that backslash-newline continuation in the command, it's load-bearing!)


๐Ÿฆ‹ systemd-analyze dlopen-metadata FILE ๐Ÿฆ‹

This command will load the specified file, and if it is an ELF object (executables, libraries, core files, etc.) it will parse the embedded dlopen metadata, if any, and print it in a table or json format. See the dlopen() Metadata for ELF Files[2] document for more information.

(it's the sibling of inspect-elf, same energy, different metadata โœจ)


๐Ÿ‘‘ systemd-analyze fdstore UNIT... ๐Ÿ‘‘

Lists the current contents of the specified service unit's file descriptor store. This shows names, inode types, device numbers, inode numbers, paths and open modes of the open file descriptors.

The specified units must have FileDescriptorStoreMax= enabled, see systemd.service(5) for details.

๐Ÿ’… Example 23. Table output ๐Ÿ’…

$ systemd-analyze fdstore systemd-journald.service
FDNAME TYPE DEVNO   INODE RDEVNO PATH             FLAGS
stored sock 0:8   4218620 -      socket:[4218620] ro
stored sock 0:8   4213198 -      socket:[4213198] ro
stored sock 0:8   4213190 -      socket:[4213190] ro
...
๐Ÿ“Œ Note: the "DEVNO" column refers to the major/minor numbers of the device node backing the file system the file descriptor's inode is on. The "RDEVNO" column refers to the major/minor numbers of the device node itself if the file descriptor refers to one. Compare with corresponding .st_dev and .st_rdev fields in struct stat (see stat(2) for details). The listed inode numbers in the "INODE" column are on the file system indicated by "DEVNO".

๐ŸŒˆ systemd-analyze image-policy POLICY... ๐ŸŒˆ

This command analyzes the specified image policy string, as per systemd.image-policy(7). The policy is normalized and simplified. For each currently defined partition identifier (as per the UAPI.2 Discoverable Partitions Specification[3]) the effect of the image policy string is shown in tabular form.

๐Ÿ“ Example 24. Example Output ๐Ÿ“

$ systemd-analyze image-policy swap=encrypted:usr=read-only-on+verity:root=encrypted
Analyzing policy: root=encrypted:usr=verity+read-only-on:swap=encrypted
       Long form: root=encrypted:usr=verity+read-only-on:swap=encrypted:=unused+absent

PARTITION       MODE        READ-ONLY GROWFS
root            encrypted   -         -
usr             verity      yes       -
home            ignore      -         -
srv             ignore      -         -
esp             ignore      -         -
xbootldr        ignore      -         -
swap            encrypted   -         -
root-verity     ignore      -         -
usr-verity      unprotected yes       -
root-verity-sig ignore      -         -
usr-verity-sig  ignore      -         -
tmp             ignore      -         -
var             ignore      -         -
default         ignore      -         -

look at it go โ€” it even normalized your input and showed you the long form. main character energy fr โœจ


๐Ÿ”ฅ systemd-analyze has-tpm2 ๐Ÿ”ฅ

Reports whether the system is equipped with a usable TPM2 device.

  • If a TPM2 device has been discovered, is supported, and is being used by firmware, by the OS kernel drivers and by userspace (i.e. systemd) โ†’ this prints "yes" and exits with exit status zero โœ…
  • If no such device is discovered/supported/used โ†’ prints "no"
  • Otherwise โ†’ prints "partial"

In either of these two cases it exits with non-zero exit status.

It also shows five lines indicating separately whether firmware, drivers, the system, the kernel and libraries discovered/support/use TPM2. Currently, required libraries are libtss2-esys.so.0, libtss2-rc.so.0, and libtss2-mu.so.0. The requirement may be changed in the future release.

๐Ÿ’€ Note: this checks for TPM 2.0 devices only, and does not consider TPM 1.2 at all. sorry 1.2 girlies.

Combine with --quiet to suppress the output. ๐Ÿคซ

๐Ÿ’œ Example 25. Example Output ๐Ÿ’œ

yes
+firmware
+driver
+system
+subsystem
+libraries
  +libtss2-esys.so.0
  +libtss2-rc.so.0
  +libtss2-mu.so.0

Added in version 257.


โญ systemd-analyze identify-tpm2 โญ

Shows vendor information about the TPM 2.0 device discovered. ๐Ÿ”

๐Ÿงš Example 26. Example Output ๐Ÿงš

  Family Indicator: 2.0
             Level: 0
          Revision: 1.59
Specification Date: Mon 2023-01-09
      Manufacturer: STM
     Vendor String: ST33KTPM2XSPI
  Firmware Version: 9.258
   Modalias String: fi2.0:lv0:rv1.59:sy2023:sd9:mfSTM:vsST33KTPM2XSPI:ty0:fw9.258.0:

Added in version 260.


๐ŸŽ€ systemd-analyze pcrs [PCR...] ๐ŸŽ€

This command shows the known TPM2 PCRs along with their identifying names and current values.

๐ŸŒธ Example 27. Example Output ๐ŸŒธ

$ systemd-analyze pcrs
NR NAME                SHA256
 0 platform-code       bcd2eb527108bbb1f5528409bcbe310aa9b74f687854cc5857605993f3d9eb11
 1 platform-config     b60622856eb7ce52637b80f30a520e6e87c347daa679f3335f4f1a600681bb01
 2 external-code       1471262403e9a62f9c392941300b4807fbdb6f0bfdd50abfab752732087017dd
 3 external-config     3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969
 4 boot-loader-code    939f7fa1458e1f7ce968874d908e524fc0debf890383d355e4ce347b7b78a95c
 5 boot-loader-config  864c61c5ea5ecbdb6951e6cb6d9c1f4b4eac79772f7fe13b8bece569d83d3768
 6 -                   3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969
 7 secure-boot-policy  9c905bd9b9891bfb889b90a54c4b537b889cfa817c4389cc25754823a9443255
 8 -                   0000000000000000000000000000000000000000000000000000000000000000
 9 kernel-initrd       9caa29b128113ef42aa53d421f03437be57211e5ebafc0fa8b5d4514ee37ff0c
10 ima                 5ea9e3dab53eb6b483b6ec9e3b2c712bea66bca1b155637841216e0094387400
11 kernel-boot         0000000000000000000000000000000000000000000000000000000000000000
12 kernel-config       627ffa4b405e911902fe1f1a8b0164693b31acab04f805f15bccfe2209c7eace
13 sysexts             0000000000000000000000000000000000000000000000000000000000000000
14 shim-policy         0000000000000000000000000000000000000000000000000000000000000000
15 system-identity     0000000000000000000000000000000000000000000000000000000000000000
16 debug               0000000000000000000000000000000000000000000000000000000000000000
17 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
18 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
19 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
20 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
21 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
22 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
23 application-support 0000000000000000000000000000000000000000000000000000000000000000

systemd-analyze nvpcrs [NVPCR...]</p>

okay so this one pulls up the known TPM2 NvPCRs โ€” those are the extra PCRs that live in TPM2 NV indexes โ€” and shows you their identifying names plus whatever values they're holding right now. โœจ

Example 28. Example Output

$ systemd-analyze nvpcrs
NAME         NVINDEX VALUE
cryptsetup 0x1d10201 f400543943cc7215557ce672872ace5382e6d53177cc459078ba9277e41588d9
hardware   0x1d10200 e155474936d7d74c893e6ece1099a2311d572cf23becea159dabf282db754284

Added in version 259.


systemd-analyze srk [>FILE]</p>

this reads the Storage Root Key (SRK) straight off the TPM2 device and writes it to stdout in marshalled TPM2B_PUBLIC format ๐Ÿ’… heads up bestie, the output is non-printable data, so you gotta redirect it into a file or a pipe โ€” do not just stare at your terminal wondering why it's glitching.

Example 29. Save the Storage Root Key to srk.tpm2b_public

systemd-analyze srk >srk.tpm2b_public

systemd-analyze architectures [NAME...]</p>

this one lists every known CPU architecture and tells you which ones are native. the architecture names it spits out are exactly the ones ConditionArchitecture= supports, peep systemd.unit(5) for the deets. and if you pass architecture names, it only lists the ones you asked for. no cap. ๐ŸŒˆ

Example 30. Table output

$ systemd-analyze architectures
NAME        SUPPORT
alpha       foreign
arc         foreign
arc-be      foreign
arm         foreign
arm64       foreign
...
sparc       foreign
sparc64     foreign
tilegx      foreign
x86         secondary
x86-64      native

systemd-analyze smbios11</p>

shows you the list of SMBIOS Type #11 strings that got passed to the system. also go look at smbios-type-11(7) if you want the lore. ๐Ÿฆ‹

Example 31. Example output

$ systemd-analyze smbios11
io.systemd.stub.kernel-cmdline-extra=console=ttyS0
io.systemd.credential.binary:ssh.ephemeral-authorized_keys-all=c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSURGd20xbFp4WlRGclJteG9ZQlozOTYzcE1uYlJCaDMwM1MxVXhLSUM2NmYgbGVubmFydEB6ZXRhCg==
io.systemd.credential:vmm.notify_socket=vsock-stream:2:254570042

3 SMBIOS Type #11 strings passed.

Added in version 257.


systemd-analyze chid</p>

this shows the Computer Hardware IDs (CHIDs) of the local system ๐Ÿซถ these IDs are what identify your system's computer hardware, and they're based on SMBIOS data. if you wanna go deep, see Using Computer Hardware IDs (CHIDs)[4]. ๐Ÿ’œ

Example 32. Example output

$ systemd-analyze chid
TYPE INPUT  CHID
   3 MFPSmp 520537c0-3b59-504f-b062-9682ea236b21
   4 MFPS-- edf05dc8-a53d-5b2c-8023-630bca2a2463
   5 MFP--- ebc6a4d9-ec48-537a-916b-c69fa4fdd814
   6 M--Smp 5ebe4bba-f598-5e90-9ff2-9fd0d3211465
   7 M--S-- 1a3fb835-b42a-5f9c-a38c-eff5bfd5c41d
   8 M-P-mp 2a831dce-8163-5bad-8406-435b8c752dd8
   9 M-P--- 7c21c878-4a75-50f7-9816-21e811588da0
  10 MF--mp 9a003537-bcc5-500e-b10a-8d8892e4fc64
  11 MF---- bb9122bb-8a5c-50d2-a742-a85beb719909
  13 M---mp bfc36935-5032-5987-a0a3-6311f01de33a

LEGEND: M โ†’ sys_vendor (LENOVO) โ”„ F โ†’ product_family (ThinkPad X1 Carbon Gen 9) โ”„ P โ†’ product_name (20XW0055GE)
        S โ†’ product_sku (LENOVO_MT_20XW_BU_Think_FM_ThinkPad X1 Carbon Gen 9) โ”„ m โ†’ board_vendor (LENOVO)
        p โ†’ board_name (20XW0055GE)

Added in version 258.


systemd-analyze transient-settings TYPE...</p>

okay so this lists all the properties you can set for various unit types through command line interfaces โ€” specifically systemctl(1) set-property and the --property=/--automount-property= options in systemd-run(1), systemd-nspawn(1), and systemd-mount(1). those assignments only work for a subset of the properties you can set in config files, so also check systemd.unit(5), systemd.exec(5), systemd.resource-control(5), and all the other unit-type-specific pages. the TYPE argument has to be a unit type ("service", "socket", ...) and it lists the properties that apply to those specific types. ๐Ÿ‘‘

Note: the D-Bus properties documented in org.freedesktop.systemd1(5) form a partially overlapping set with the lists this command generates. lots of D-Bus properties and transient settings share the same names, BUT for example LogRateLimitIntervalSec= is described in systemd.exec(5) and would be listed by this command, while the corresponding D-Bus property described in systemd.exec(5) is LogRateLimitIntervalUSec. not the same, bestie, do not get it twisted ๐Ÿ˜ญ

this verb is intended primarily for programmatic generation of shell completions.

Added in version 258.


systemd-analyze unit-shell SERVICE [command...]</p>

the given command runs inside the namespace of the specified running service. and if you don't give a command? it just spawns a shell attached to the service's namespace. main character energy tbh. โœจ

Example 33. Example output

$ systemd-analyze unit-shell systemd-resolved.service ls
bin   dev  etc    home  lib   lib64     lost+found  mnt  proc  run   srv  tmp  var   vmlinuz.old
boot  efi  exitrd  init  lib32     libx32    media         opt  root  sbin  sys  usr  vmlinuz  work

Added in version 258.


systemd-analyze unit-gdb SERVICE</p>

spawns a debugger and attaches it to the given service ๐Ÿ”ฅ by default it uses gdb(1), but you can swap that out with the --debugger= option or the $SYSTEMD_DEBUGGER environment variable. and if you wanna pass extra command line arguments to the debugger, use --debugger-arguments= โ€” and please quote them properly when ARGS contain whitespace (see the Example, it's giving "I learned this the hard way"). ๐Ÿ’€

Example 34.

$ systemd-analyze --debugger-arguments="-batch -ex 'info all-registers'" unit-gdb systemd-oomd.service

Added in version 258.


โœจ OPTIONS โœจ

these are the options that are understood, bestie, collect them all ๐ŸŽ€

  • --system โ€” operates on the system systemd instance. this is the implied default. Added in version 209.
  • --user โ€” operates on the user systemd instance. Added in version 186.
  • --global โ€” operates on the system-wide configuration for user systemd instance. Added in version 238.
  • --order, --require โ€” when used alongside the dot command (see above), this picks which dependencies show up in the dependency graph. pass --order and only dependencies of type After= or Before= are shown. pass --require and only dependencies of type Requires=, Requisite=, BindsTo=, Wants=, and Conflicts= are shown. pass neither and you get dependencies of all these types. Added in version 198.
  • --from-pattern=, --to-pattern= โ€” when used alongside the dot command (see above), this selects which relationships show up in the dependency graph. both options need a glob(7) pattern as an argument, which gets matched against the left-hand and the right-hand nodes of a relationship respectively. ๐Ÿ‘€

each of these can be used more than once, and in that case the unit name must match one of the values. when tests for both sides of the relation are present, a relation must pass both tests to be shown. when patterns are also specified as positional arguments, they must match at least one side of the relation. in other words: patterns given with those two options trim the list of edges matched by the positional arguments (if any are given), and otherwise they fully determine the list of edges shown. Added in version 201.

  • --fuzz=timespan โ€” when used alongside the critical-chain command (see above), it also shows units that finished timespan earlier than the latest unit in the same level. the unit of timespan is seconds unless you specify a different unit, e.g. "50ms". Added in version 203.
  • --man=no โ€” don't invoke man(1) to verify that the man pages listed in Documentation= actually exist. Added in version 235.
  • --generators โ€” invoke unit generators, see systemd.generator(7). some generators need root privileges, so heads up: running this as a normal user with generators enabled will generally result in some warnings. ๐Ÿ™ˆ Added in version 235.
  • --instance=NAME โ€” specifies the fallback instance name for template units. it gets used when one or more template units without an instance name (e.g. foo@.service) are specified for systemd-analyze condition with --unit=, systemd-analyze security, and systemd-analyze verify. if you leave it unspecified, "test_instance" is used. Added in version 257.
  • --recursive-errors=MODE โ€” okay so this one controls how systemd-analyze verify checks units and their dependencies, and whether it bails out with a non-zero exit status or just vibes. With yes, you get a non-zero process exit status when warnings pop up during verification of either the unit you named or any of its associated dependencies. With no, non-zero exit status only when warnings come up verifying the specified unit itself. With one, non-zero exit status when warnings arise verifying either the specified unit or its immediate dependencies. And if you justโ€ฆ don't pass this option at all? Then zero is returned as the exit status regardless of whether warnings arise during verification or not. ๐Ÿ’…

Added in version 250.

  • --root=PATH โ€” with cat-config, verify, condition, unit-gdb, and security when it's used together with --offline=, this makes the tool operate on files underneath the root path you give it in PATH. ๐Ÿซถ

Added in version 239.

  • --image=PATH โ€” same squad (cat-config, verify, condition and security when used with --offline=), except this time it operates on files inside the disk image at the path PATH. ๐Ÿฆ‹

Added in version 250.

  • --image-policy=policy โ€” takes an image policy string as its argument, exactly as described in systemd.image-policy(7). The policy gets enforced when operating on the disk image you passed via --image=, see above. If you don't specify it, it defaults to the "*" policy, meaning all recognized file systems in the image are used. โœจ
  • --offline=BOOL โ€” with security, this does an offline security review of the unit files you specify, i.e. it doesn't have to rely on PID 1 to acquire security info for the files the way the plain security verb does when used by itself. Which means --offline= can be used alongside --root= and --image= too. ๐Ÿ‘‘ Heads up though: if a unit's overall exposure level is above the one set by --threshold= (default value is 100), --offline= will return an error.

Added in version 250.

  • --profile=PATH โ€” with security --offline=, this takes the specified portable profile into consideration when assessing unit settings. You can pass the profile by name โ€” in which case the well-known system locations get searched โ€” or you can hand over the full path to a specific drop-in file. ๐ŸŽ€

Added in version 250.

  • --threshold=NUMBER โ€” with security, this lets you set a custom value to compare the overall exposure level against, for the unit files you specified. If a unit's overall exposure level is greater than what you set, security will return an error. --threshold= works with --offline= as well, and its default value is 100. ๐Ÿ’–

Added in version 250.

  • --security-policy=PATH โ€” with security, this lets you define your own custom set of requirements, formatted as a JSON file, to compare the specified unit file(s) against and determine their overall exposure level to security threats. ๐Ÿ”ฅ

๐Ÿ“‹ Table 1. Accepted Assessment Test Identifiers

ok so here's the full menu of test IDs you can name-drop, bestie. every single one, in order, no skips ๐Ÿ’…

  • UserOrDynamicUser
  • SupplementaryGroups
  • PrivateMounts
  • PrivateDevices
  • PrivateTmp
  • PrivateNetwork
  • PrivateUsers
  • ProtectControlGroups
  • ProtectKernelModules
  • ProtectKernelTunables
  • ProtectKernelLogs
  • ProtectClock
  • ProtectHome
  • ProtectHostname
  • ProtectSystem
  • RootDirectoryOrRootImage
  • LockPersonality
  • MemoryDenyWriteExecute
  • NoNewPrivileges
  • CapabilityBoundingSet_CAP_SYS_ADMIN
  • CapabilityBoundingSet_CAP_SET_UID_GID_PCAP
  • CapabilityBoundingSet_CAP_SYS_PTRACE
  • CapabilityBoundingSet_CAP_SYS_TIME
  • CapabilityBoundingSet_CAP_NET_ADMIN
  • CapabilityBoundingSet_CAP_SYS_RAWIO
  • CapabilityBoundingSet_CAP_SYS_MODULE
  • CapabilityBoundingSet_CAP_AUDIT
  • CapabilityBoundingSet_CAP_SYSLOG
  • CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE
  • CapabilityBoundingSet_CAP_MKNOD
  • CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP
  • CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER
  • CapabilityBoundingSet_CAP_KILL
  • CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW
  • CapabilityBoundingSet_CAP_SYS_BOOT
  • CapabilityBoundingSet_CAP_MAC
  • CapabilityBoundingSet_CAP_LINUX_IMMUTABLE
  • CapabilityBoundingSet_CAP_IPC_LOCK
  • CapabilityBoundingSet_CAP_SYS_CHROOT
  • CapabilityBoundingSet_CAP_BLOCK_SUSPEND
  • CapabilityBoundingSet_CAP_WAKE_ALARM
  • CapabilityBoundingSet_CAP_LEASE
  • CapabilityBoundingSet_CAP_SYS_TTY_CONFIG

quick vibe check โœจ โ€” notice how the first chunk is all the cute lil namey names (PrivateTmp, ProtectHome, etc.) and then the second chunk is the long ones that all start with CapabilityBoundingSet_CAP_. that prefix is literally the whole squad, iykyk ๐Ÿซถ

ngl if you're copy-pasting these, do it character for character โ€” no auto-correct, no "i'll just fix the caps", no nothing ๐Ÿ™ˆ the shell does not forgive a typo, and these names are case-sensitive fr fr ๐Ÿ’€

and that's the whole table, bestie โ€” every identifier, zero missing, we ate ๐Ÿ’…๐Ÿ“

...but we are so not done yet, there's still more coming ๐Ÿ‘‘

  • CapabilityBoundingSet_CAP_BPF
  • UMask
  • KeyringMode
  • ProtectProc
  • ProcSubset
  • NotifyAccess
  • RemoveIPC
  • Delegate
  • RestrictRealtime
  • RestrictSUIDSGID
  • RestrictNamespaces_user
  • RestrictNamespaces_mnt
  • RestrictNamespaces_ipc
  • RestrictNamespaces_pid
  • RestrictNamespaces_cgroup
  • RestrictNamespaces_uts
  • RestrictNamespaces_net
  • RestrictAddressFamilies_AF_INET_INET6
  • RestrictAddressFamilies_AF_UNIX
  • RestrictAddressFamilies_AF_NETLINK
  • RestrictAddressFamilies_AF_PACKET
  • RestrictAddressFamilies_OTHER
  • SystemCallArchitectures
  • SystemCallFilter_swap
  • SystemCallFilter_obsolete
  • SystemCallFilter_clock
  • SystemCallFilter_cpu_emulation
  • SystemCallFilter_debug
  • SystemCallFilter_mount
  • SystemCallFilter_module
  • SystemCallFilter_raw_io
  • SystemCallFilter_reboot
  • SystemCallFilter_privileged
  • SystemCallFilter_resources
  • IPAddressDeny
  • DeviceAllow
  • AmbientCapabilities

Peep the example called "JSON Policy" down below bestie, it's right there ๐Ÿ‘‡

Added in version 250.


  • --json=MODE โ€” okay so with the security command, this makes a JSON formatted output of the security analysis table. ๐Ÿ’… The format is a JSON array with objects containing these fields: set which tells you if the setting has been enabled or not, name which is what's used to refer to the setting, json_field which is the JSON compatible identifier of the setting, description which is an outline of the setting state, and exposure which is a number in the range 0.0...10.0, where a higher value corresponds to a higher security threat. ๐Ÿ”ฅ The JSON version of the table gets printed to standard output. The MODE you pass to the option can be one of three: off which is the default, pretty and short which respectively output a prettified or shorted JSON version of the security table. And with the plot command, it generates a JSON formatted output of the raw time data. ๐Ÿฆ‹ That format is a JSON array with objects containing these fields: name which is the unit name, activated which is the time after startup the service was activated, activating which is how long after startup the service was initially started, time which is how long the service took to activate from when it was initially started, deactivated which is the time after startup that the service was deactivated, deactivating which is the time after startup that the service was initially told to deactivate. No cap, that's the whole shape of it โœจ

Added in version 250.

  • --iterations=NUMBER โ€” when you're rolling with the calendar command, this tells it how many upcoming iterations of that calendar expression to show. default is 1. Added in version 242.
  • --base-time=TIMESTAMP โ€” again with the calendar command, this makes it show the next iterations relative to that specific point in time instead of, like, now. if you don't say anything, it defaults to the current time. โฐ Added in version 244.
  • --unit=UNIT โ€” when you're using the condition command, this evaluates all the Condition*=... and Assert*=... assignments inside the unit file you name. bestie, the full unit search path gets built by combining the directories for that specified unit with the usual unit load paths. the variable $SYSTEMD_UNIT_PATH is supported and you can use it to replace or augment the compiled-in set of unit load paths โ€” go peep systemd.unit(5) for that. all unit files sitting in the directory containing the specified unit get used in preference to the other paths. and if you hand it a template unit with no instance name (like foo@.service), it'll use "test_instance" as the instance name, which you can control with the --instance= option. ๐ŸŽ€ Added in version 250.
  • --table โ€” when used with the plot command, the raw time data gets output as a table. ๐Ÿ“Š Added in version 253.
  • --no-legend โ€” when you use this with the plot command together with either --table or --json=, no legends or hints get included in the output. Added in version 253.
  • -H, --host= โ€” run the operation remotely. give it a hostname, or a username and hostname separated by "@", to connect to. the hostname can optionally get a port that ssh is listening on tacked on after a ":", and then a container name after a "/", which connects you straight to a specific container on that host. this uses SSH to talk to the remote machine manager instance. you can enumerate container names with machinectl -H HOST. oh and put IPv6 addresses in brackets. ๐Ÿ›ฐ๏ธ
  • -M, --machine= โ€” run the operation on a local container. name the container you wanna connect to, optionally prefixed by a username to connect as and a separating "@" character. if you use the special string ".host" instead of a container name, it makes a connection to the local system (which is super useful when you wanna connect to a specific user's user bus: "--user --machine=lennart@.host"). if you don't use the "@" syntax, the connection is made as the root user. and if you do use the "@" syntax, either the left hand side or the right hand side can be omitted (but not both!) and then the local user name and ".host" are implied. ๐Ÿ’…
  • -q, --quiet โ€” suppress hints and other non-essential output. Added in version 250.
  • --tldr โ€” with cat-config, this only prints the "interesting" parts of the config files, skipping comments and empty lines and section headers that are followed only by comments and empty lines. โœจ Added in version 255.
  • --scale-svg=FACTOR โ€” when used with the plot command, the x-axis of the plot can be stretched by FACTOR (default: 1.0). ๐Ÿ“ˆ Added in version 257.
  • --detailed โ€” when used with the plot command, activation timestamps details can be seen in the SVG plot. ๐Ÿ” Added in version 257.
  • --drm-device=PATH โ€” when provided with the chid command, use this sysfs path to a DRM device to fetch EDID from. example: /sys/class/drm/card1-HDMI-A-1/ Added in version 258.
  • --debugger=DEBUGGER โ€” use the given debugger for the unit-gdb command. if you don't give one and $SYSTEMD_DEBUGGER is unset, then gdb(1) gets used. ๐Ÿ› Added in version 258.
  • *-A *ARGS, --debugger-arguments=ARGS โ€” pass the given ARGS as extra command line arguments to the debugger. Added in version 258.
  • -h, --help โ€” print a short help text and exit. ๐Ÿซถ
  • --version โ€” print a short version string and exit.
  • --no-pager โ€” do not pipe output into a pager.

๐ŸŒˆ EXIT STATUS ๐ŸŒˆ

for most verbs, 0 is returned on success, and a non-zero failure code otherwise. no cap.

for the verb compare-versions, in the two-argument form, 12, 0, or 11 are returned if the second version string is respectively larger than, equal to, or smaller than the first. in the three-argument form, 0 or 1 are returned when the condition is respectively true or false. ๐Ÿ’–

for the verb has-tpm2, 0 is returned if a TPM2 device is discovered, supported, and used by firmware, driver, and userspace (i.e. systemd). otherwise, it's the OR combination of the value 1 (in case firmware support is missing), 2 (in case driver support is missing), and 4 (in case userspace support is missing). so if no TPM2 support is available at all, the value 7 is returned. ๐Ÿ”ฅ

โœจ ENVIRONMENT โœจ

okay bestie, this is the part where you get to โœจcustomizeโœจ everything without touching a single flag. environment variables, the silent main characters. let's go ๐Ÿ’…

  • $SYSTEMD_LOG_LEVEL โ€” the maximum log level of emitted messages, aka the vibe ceiling. anything with a higher log level (so, less important) gets suppressed. it takes a comma-separated list of values. each value is either one of these, listed in order of decreasing importance: emerg, alert, crit, err, warning, notice, info, debug โ€” or just an integer in the range 0...7. see syslog(3) for more info. every value can optionally be prefixed with one of console, syslog, kmsg or journal followed by a colon, to set the max log level for that specific log target (like, SYSTEMD_LOG_LEVEL=debug,console:info means "log at debug level, except when logging to the console, which should be at info level"). but watch out โ€” the global max log level takes priority over any per-target max log levels. no cap. ๐Ÿ‘‘
  • $SYSTEMD_LOG_COLOR โ€” a boolean. if true, messages written to the tty get colored according to priority. ๐ŸŒˆ

this setting is only useful when messages go directly to the terminal, because journalctl(1) and other tools that display logs will color messages based on the log level on their own. they don't need your help bestie.

  • $SYSTEMD_LOG_TIME โ€” a boolean. if true, console log messages get prefixed with a timestamp. โฐ

same deal: only useful when messages are written directly to the terminal or a file, because journalctl(1) and friends attach timestamps based on the entry metadata all by themselves.

  • $SYSTEMD_LOG_LOCATION โ€” a boolean. if true, messages get prefixed with a filename and line number in the source code where the message originates. so you know exactly which line said that. iconic for debugging. ๐Ÿ”

note that the log location is often attached as metadata to journal entries anyway, but putting it right there in the message text can still be convenient when debugging programs.

  • $SYSTEMD_LOG_TID โ€” a boolean. if true, messages get prefixed with the current numerical thread ID (TID).

note that this information is attached as metadata to journal entries anyway. including it directly in the message text can nevertheless be convenient when debugging programs. (yes, the original really does have that little typo in it โ€” "the this information" โ€” we're keeping the facts, not the vibes of grammar ๐Ÿ’€)

  • $SYSTEMD_LOG_TARGET โ€” where your log messages actually go. pick one:
    • console โ€” log to the attached tty
    • console-prefixed โ€” log to the attached tty, but with prefixes encoding the log level and "facility", see syslog(3)
    • kmsg โ€” log to the kernel circular log buffer
    • journal โ€” log to the journal
    • journal-or-kmsg โ€” log to the journal if available, and to kmsg otherwise
    • auto โ€” determine the appropriate log target automatically, this is the default
    • null โ€” disable log output entirely. silence. ๐Ÿซฅ
  • $SYSTEMD_LOG_RATELIMIT_KMSG โ€” whether to ratelimit kmsg or not. takes a boolean. defaults to "true". if disabled, systemd will not ratelimit messages written to kmsg. (so like, prepare for spam ๐Ÿ’€)
  • $SYSTEMD_PAGER, $PAGER โ€” which pager to use when --no-pager isn't given. $SYSTEMD_PAGER is used if set; otherwise $PAGER is used. if neither $SYSTEMD_PAGER nor $PAGER are set, a set of well-known pager implementations gets tried in turn, including less(1) and more(1), until one is found. if no pager implementation is discovered, no pager is invoked at all. setting those environment variables to an empty string or the value "cat" is equivalent to passing --no-pager. ๐Ÿฑ
Note: if $SYSTEMD_PAGERSECURE is not set, $SYSTEMD_PAGER and $PAGER can only be used to disable the pager (with "cat" or ""), and are otherwise ignored. yeah. that's the rule. sorry bestie. ๐Ÿ™ˆ
  • $SYSTEMD_LESS โ€” override the options passed to less (by default "FRSXMK").

users might want to change two options in particular:

K

this option instructs the pager to exit immediately when Ctrl+C is pressed. to allow less to handle Ctrl+C itself, so it can switch back to the pager command prompt, unset this option.

if the value of $SYSTEMD_LESS does not include "K", and the pager that is invoked is less, Ctrl+C will be ignored by the executable, and needs to be handled by the pager.

X

this option instructs the pager to not send termcap initialization and deinitialization strings to the terminal. it's set by default to allow command output to remain visible in the terminal even after the pager exits. nevertheless, this prevents some pager functionality from working โ€” in particular, paged output cannot be scrolled with the mouse. ๐Ÿ–ฑ๏ธ๐Ÿšซ

note that setting the regular $LESS environment variable has no effect for less invocations by systemd tools.

see less(1) for more discussion.

  • $SYSTEMD_LESSCHARSET โ€” override the charset passed to less (by default "utf-8", if the invoking terminal is determined to be UTF-8 compatible). ๐ŸŒธ

note that setting the regular $LESSCHARSET environment variable has no effect for less invocations by systemd tools. (systemd only trusts its own prefixed variables, it's giving strict parent energy ๐Ÿ’…)

  • $SYSTEMD_PAGERSECURE โ€” okay this one is the serious one, listen up.

common pager commands like less(1), on top of just "paging" (scrolling through the output), also support opening or writing to other files and running arbitrary shell commands. when commands are invoked with elevated privileges โ€” for example under sudo(8) or pkexec(1) โ€” the pager becomes a security boundary. care must be taken that only programs with strictly limited functionality are used as pagers, and that unintended interactive features like opening or creating new files, or starting subprocesses, are not allowed. "secure mode" for the pager may be enabled as described below, if the pager supports it (most pagers are not written in a way that takes this into consideration). it is recommended to either explicitly enable "secure mode" or to completely disable the pager using --no-pager or PAGER=cat when allowing untrusted users to execute commands with elevated privileges. ๐Ÿ”ฅ

this option takes a boolean argument. when set to true, the "secure mode" of the pager is enabled. in "secure mode", LESSSECURE=1 will be set when invoking the pager, which instructs the pager to disable commands that open or create new files or start new subprocesses. currently only less(1) is known to understand this variable and implement "secure mode".

when set to false, no limitation is placed on the pager. setting SYSTEMD_PAGERSECURE=0 or not removing it from the inherited environment may allow the user to invoke arbitrary commands. ๐Ÿ˜ญ

when $SYSTEMD_PAGERSECURE is not set, systemd tools attempt to automatically figure out if "secure mode" should be enabled and whether the pager supports it. "secure mode" is enabled if the effective UID is not the same as the owner of the login session, see geteuid(2) and sd_pid_get_owner_uid(3), or when running under sudo(8) or similar tools ($SUDO_UID is set [5]). in those cases, SYSTEMD_PAGERSECURE=1 will be set and pagers which are not known to implement "secure mode" will not be used at all. note that this autodetection only covers the most common mechanisms to elevate privileges and is intended as convenience. it is recommended to explicitly set $SYSTEMD_PAGERSECURE or disable the pager. ๐Ÿ‘‘

Note: if the $SYSTEMD_PAGER or $PAGER variables are to be honoured, other than to disable the pager, $SYSTEMD_PAGERSECURE must be set too.
  • $SYSTEMD_COLORS โ€” takes a boolean argument, or a special value. by default (unset), systemd and related utilities will use colors in their output if possible. if $COLORTERM is set to "truecolor" or "24bit", 24-bit colors will be enabled, 256 colors otherwise โ€” unless $NO_COLOR or $TERM indicates colors are disabled.
    • true โ€” same as unset, except that $NO_COLOR is ignored.
    • false โ€” the output will be monochrome. sad beige terminal. ๐Ÿซฅ
    • "16", "256", "24bit" โ€” always use the base 16 ANSI colors, 256 colors, or 24 bit color, respectively.
    • "auto-16", "auto-256", "auto-24bit" โ€” use the given quantity of colours, subject to $TERM, and what the console is connected to.
  • $SYSTEMD_URLIFY โ€” the value must be a boolean. controls whether clickable links should be generated in the output for terminal emulators supporting this. this can be specified to override the decision that systemd makes based on $TERM and other conditions. โœจ

โœจ EXAMPLES โœจ

๐Ÿ’– Example 35. JSON Policy

okay bestie so the JSON file you hand over as a path parameter to --security-policy= is a top-level JSON object, and the keys are literally the assessment test identifiers we were yapping about above. no cap. ๐Ÿ“‹

And what goes inside those keys? JSON objects, with one or more of these fields:

  • description_na โ€” a string, bestie
  • description_good โ€” also a string
  • description_bad โ€” you guessed it, a string
  • weight โ€” an unsigned integer
  • range โ€” an unsigned integer

now here's the tea ๐Ÿซ–: if any of these fields for a specific id of the unit file is missing from the JSON object, then the default built-in field value that goes with that same id gets used for the security analysis as the default. so nothing breaks, it just falls back.

weight and range are the ones doing the heavy lifting for figuring out the overall exposure level of the unit files: each setting's value gets assigned a badness score, and that score gets multiplied by the policy weight and divided by the policy range to work out the overall exposure that the setting implies. then all that computed badness gets summed across every setting in the unit file, normalized to the 1...100 range, and that is what determines the overall exposure level of the unit. it's giving math, it's giving main character ๐Ÿ’…

and by letting you mess with these fields, the security verb hands you the option to decide for yourself which ids matter more and therefore should hit harder on the exposure level. so iconic of it.

โš ๏ธ heads up: a weight of "0" means that setting will not be checked. periodt.
{
  "PrivateDevices":
    {
    "description_good": "Service has no access to hardware devices",
    "description_bad": "Service potentially has access to hardware devices",
    "weight": 1000,
    "range": 1
    },
  "PrivateMounts":
    {
    "description_good": "Service cannot install system mounts",
    "description_bad": "Service may install system mounts",
    "weight": 1000,
    "range": 1
    },
  "PrivateNetwork":
    {
    "description_good": "Service has no access to the host's network",
    "description_bad": "Service has access to the host's network",
    "weight": 2500,
    "range": 1
    },
  "PrivateTmp":
    {
    "description_good": "Service has no access to other software's temporary files",
    "description_bad": "Service has access to other software's temporary files",
    "weight": 1000,
    "range": 1
    },
  "PrivateUsers":
    {
    "description_good": "Service does not have access to other users",
    "description_bad": "Service has access to other users",
    "weight": 1000,
    "range": 1
    }
}

๐ŸŒˆ SEE ALSO ๐ŸŒˆ


๐Ÿ“ NOTES ๐Ÿ“

  1. Package Metadata for Executable Files https://systemd.io/PACKAGE_METADATA_FOR_EXECUTABLE_FILES/
  2. dlopen() Metadata for ELF Files https://systemd.io/ELF_DLOPEN_METADATA/
  3. UAPI.2 Discoverable Partitions Specification https://uapi-group.org/specifications/specs/discoverable_partitions_specification
  4. Using Computer Hardware IDs (CHIDs) https://learn.microsoft.com/en-us/windows-hardware/drivers/dashboard/using-chids
  5. It is recommended for other tools to set and check $SUDO_UID as appropriate, treating it is a common interface.

and that's a wrap, bestie ๐ŸŽ€ you just read the whole SYSTEMD-ANALYZE(1) page, from systemd 262.1 โ€” go forth and analyze your boot, your security, your vibes ๐Ÿ’–โœจ๐Ÿฆ‹ you ate, no crumbs, fr fr ๐Ÿ‘‘๐Ÿ’…๐ŸŒธ

๐Ÿ“ฆ where this page comes from
  • arch rolling: systemd 262.1-1 ยท LGPL-2.1-or-later AND CC0-1.0 AND GPL-2.0-or-later AND MIT-0
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms