Read Control-Group Resource Usage with systemd-cgtop

top tells you a process is eating CPU, not which systemd slice is behind it. systemd-cgtop breaks resource use down by control group instead, live on screen or as a one-shot report you can pipe into a script. The examples below target systemd 255.4-1ubuntu8.17, reported by the installed command as systemd 255.

Allow about ten minutes. You need a shell and the systemd-cgtop command. Reading the hierarchy normally needs no elevated privileges; use sudo only if your host's permissions require it. This guide does not change unit files, accounting settings or running services.

1. Check the installed command

Start with the local version and help text. This is read-only and does not need root:

$ systemd-cgtop --version
systemd 255 (255.4-1ubuntu8.17)
$ systemd-cgtop --help
systemd-cgtop [OPTIONS...] [CGROUP]

The display covers the local control-group hierarchy. A control group is normally a systemd slice, scope or service, such as system.slice/sshd.service. The command does not list individual processes in the way top does.

Checkpoint: Confirm that the version is the one you expect before copying option details into a long-lived script. The output and available switches can differ between systemd releases.

2. Take a bounded snapshot

An interactive terminal refreshes by default every second. For a report that returns, select batch mode and one iteration:

$ systemd-cgtop --batch --iterations=1
system.slice/...service                         12      3.2%   128.4M   1.5M   0B
user.slice/...service                           4      0.1%    42.7M     0B   0B

The service names, numbers and columns depend on the host. With no terminal, the program also defaults to one iteration and omits column headers, which is useful for scripts but easy to miss when reading redirected output. --batch makes the no-input behaviour explicit. Use --iterations=0 only when you intentionally want an indefinite monitor.

The exact columns depend on the installed build and terminal width. Treat the output as host data, not a stable table for parsing by column position. For a human-readable snapshot ordered by memory, use:

$ systemd-cgtop --batch --iterations=1 --order=memory

Its exit status is the first quick check:

$ systemd-cgtop --batch --iterations=1 --order=memory > cgtop-memory.txt
$ printf 'exit status: %s\n' "$?"
exit status: 0

The redirection creates or replaces cgtop-memory.txt. Choose a disposable filename, or use --output through a separate logging tool if you need an append-only workflow; systemd-cgtop itself has no output-file option. If the report matters, copy it to a new filename rather than overwriting an existing report.

3. Choose the measurement that answers the question

Sorting and measurement are separate choices. The command defaults to CPU ordering. Select a different order with one of these switches:

For CPU, the default display is a percentage. It can reach 100 times the processor count, so 250% can be legitimate on a machine with several processors. If elapsed CPU time is more useful, request it explicitly:

$ systemd-cgtop --batch --iterations=1 --order=cpu --cpu=time

Use --raw when another program needs numeric byte counts and CPU times instead of values such as 128.4M. Raw output is less convenient for a person to scan:

$ systemd-cgtop --batch --iterations=1 --raw --order=memory

4. Limit the hierarchy before it becomes noise

Large hosts can have many containers and transient scopes. The default traversal depth is 3. Limit it with --depth; zero shows only the root group:

$ systemd-cgtop --batch --iterations=1 --depth=1 --order=path
/                                      2917      -     9.7G        -        -
system.slice                          2538      -    20.7G        -        -
user.slice                             207      -     7.0G        -        -

The counts and resource values above are examples of the shape seen on this machine, not expected numbers for yours. Increase the depth when a service is nested below a slice and the shallow report hides it.

You can also pass a control-group path as the final argument to focus the report. Use an exact path visible in your own output:

$ systemd-cgtop --batch --iterations=1 system.slice

Do not guess a path from a unit name. A unit can be nested, escaped or placed in a different slice. If you need a container-specific view, use --machine=MACHINE instead of combining it with a group path.

5. Understand missing or misleading values

A dash does not prove that a service is idle. Resource usage is accounted for only where the relevant cgroup controller is enabled. CPU needs the cpu controller, memory needs memory, and I/O needs io. System services can therefore show incomplete data when accounting is not enabled in their unit configuration.

For a unit you administer, inspect its resource-control settings before changing anything:

$ systemctl show UNIT_NAME.service \\
    -p CPUAccounting -p MemoryAccounting -p IOAccounting
CPUAccounting=...
MemoryAccounting=...
IOAccounting=...

Replace UNIT_NAME.service with a real unit. systemctl show is normally read-only and unprivileged. Enabling accounting is a persistent service configuration change and can add overhead, so do not use sudo systemctl edit or restart a service merely to make one report look complete. If you do make that change later, keep a copy of the drop-in and remove that exact drop-in with systemctl revert UNIT_NAME.service only after checking that no other administrator-owned overrides would be removed.

6. Use the interactive view when a snapshot is not enough

Run the command without batch mode for a live display:

$ systemd-cgtop --order=cpu --delay=2

Press q to quit, space to refresh immediately, c, m, or i to change sorting, and + or - to adjust the delay. Press h for the installed command's short key reference. If you need a report that cannot accept keyboard input, use batch mode and an iteration limit instead.

The task count is another common distraction. By default it counts every task, including each kernel thread and each userspace thread. -P counts userspace processes only. -k counts userspace processes and kernel threads. These two switches cannot be combined. The default recursive process setting matters only when using -P or -k; with all tasks counted, child groups are always included.

Done means