The CGROUPS page is a live /sys/fs/cgroup browser that also knows where each cgroup comes from: its three subviews bridge the runtime cgroup hierarchy, the raw controller files, and the static systemd unit definitions that shape those limits – all by reading the filesystem directly, with no libcgroup or systemd API in the data path.
CGROUPS makes cgroup state and configuration visible without leaving the TUI. The interesting design choice is that it reaches the same subject – resource control – from two completely different angles: the live cgroup filesystem on one side, and the systemd unit files that produce those cgroups on the other.
Like the systemd, udev, and tune pages, it runs on the client/daemon split: a worker-thread backend in the frontend talks to lulod over a socket, and the frontend only ever consumes cached snapshots (see Process Model & IPC).
Each subview reads a distinct part of the system, which is what makes the page more than a directory listing.
The Tree view is a real directory browser over /sys/fs/cgroup, walked with opendir / readdir / lstat – there is no libcgroup dependency. For the current browse path it lists child cgroups and reads each one’s pseudo-files: cgroup.type, cgroup.controllers, and line counts of cgroup.procs and cgroup.threads for the process and thread tallies. The child-directory count becomes the sub column.
| Column | Meaning |
|---|---|
typ |
cgroup.type (domain / threaded / …) |
p |
Process count (lines in cgroup.procs) |
t |
Thread count (lines in cgroup.threads) |
sub |
Number of child cgroups |
name |
Cgroup directory name |
When you are not at the root, a synthetic .. row is prepended so you can navigate up; parent rows sort first and render in a distinct color. Opening a directory descends into it and triggers a full re-gather for the new path. The browse path is sanitized with realpath and rejected if it would escape /sys/fs/cgroup, so the browser can never wander outside the hierarchy.
The Tree preview pane is the richest part: it shows the path, type, controllers, subtree_control, and counts, then cgroup.events, then up to a dozen each of the member processes and threads – with their PIDs/TIDs resolved to command names via the shared proc-metadata reader.
The Files view drops from “which cgroup” to “what is this cgroup configured to do.” It lists the regular files in the selected cgroup directory – the controller interface files – reading each file’s first line as its current value and probing access(W_OK) to mark whether it is writable.
| Column | Meaning |
|---|---|
rw |
Writable (green) vs read-only (dim) |
name |
Control file name (e.g. cpu.max, memory.high) |
value/path |
Current value, or path |
Selecting a file previews its access mode, current value, and a raw dump of the file (capped to keep the snapshot bounded). This is where you confirm the live, effective value of a controller – as opposed to the Config view, which shows what should set it.
The Config view does not read cgroupfs at all. It scans the systemd unit directories (eight roots under /etc/systemd/* and /usr/lib/systemd/* plus /lib), recursively, and includes a file only if it is a .slice, or a .service / .scope / drop-in .conf that actually contains a cgroup resource directive.
That filter is the clever bit: rather than dumping every unit, it matches file contents against a 38-entry table of resource-control directives – CPUQuota=, MemoryMax=, IOWeight=, and the rest – so the Config list is exactly the set of units that impose a cgroup limit. Sources are ranked and colored (/etc overrides green, vendor /usr/lib cyan), and the kind column distinguishes slice / service / scope / dropin / conf. Selecting one previews the unit file.
So the page answers two different questions with two different data paths: Tree/Files = “what is the kernel doing right now,” Config = “which unit declared that limit, and where do I edit it.”
There is no inline editing on this page. The Files and Config rows expose a path through the shared external-editor handoff: pressing i collects the active edit path, suspends the TUI, runs $VISUAL / $EDITOR on it, and resumes. The Tree view is not editable (it is a navigator). Writes that target privileged paths go through lulod-system’s atomic, allowlist-scoped edit protocol; cgroup pseudo-files are written in place rather than via rename, because atomic rename is not meaningful for kernel interface files.
The snapshot the daemon caches holds the tree rows, file rows, configs, and the active preview lines, plus the current browse path. The config scan runs once and is cached (configs_loaded); the daemon re-gathers the tree when the TTL expires or the browse path changes, and re-renders just the preview when the selection moves.
CGROUPS is the map that the scheduler reads against. Scheduler rules can match on cgroup path, systemd slice, and unit – so understanding the shape of the cgroup tree here tells you what those matchers will see. The Tree view shows where a workload actually lives; the Config view shows the slices and services that define it; and the scheduler uses exactly those concepts to decide how a process should be prioritized.
/sys/fs/cgroup controllers as bundles