Read /proc/cgroups Without Misreading a cgroup v2 Host
You will finish with a reliable read-only check of the Linux control-group setup: what /proc/cgroups reports, which cgroup filesystem is mounted, and where the current shell belongs. The key result is knowing when the file is describing legacy controller information rather than the layout you should manage.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a shell on a Linux host with /proc mounted. All commands in this guide only read kernel interfaces and do not need sudo. The examples use Linux man-pages 6.7, package version manpages 6.7-2, and were checked on Linux 6.8.0-139-generic. Output from another kernel, container or service manager may differ.
1. Read the installed manual page
The installed proc_cgroups(5) page has a deliberately small contract. It identifies /proc/cgroups as the control-groups file, available since Linux 2.6.24, and points to cgroups(7) for the interpretation. It does not promise that the file is a complete map of a modern cgroup hierarchy.
Confirm the local package and the file you are reading:
$ dpkg-query -W -f='${Package} ${Version}\n' manpages
manpages 6.7-2
$ man 5 proc_cgroups
Checkpoint: if the page is missing, stop here and use the documentation shipped by your distribution. Do not infer field meanings from a copied example found on another host.
2. Inspect the four columns
Read the file as an inventory of controller names and counters:
$ cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 0 503 1
cpu 0 503 1
memory 0 503 1
pids 0 503 1
The first column is the controller or subsystem name. The second is its legacy hierarchy identifier. The third is the number of cgroups counted for that subsystem. The fourth says whether the subsystem is enabled in the legacy interface. The exact rows and numbers are host-specific, so treat the block above as representative rather than a value to copy into a report.
A common distraction is seeing hierarchy equal to 0 and concluding that cgroups are disabled. That conclusion is unsafe. On a cgroup v2 system, the unified hierarchy is not represented by the old v1 hierarchy-number scheme in the way many older examples suggest. Cross-check the mounted filesystem and the current process before deciding what the file means.
3. Identify the mounted cgroup version
Ask findmnt which cgroup filesystem is mounted. This is still an ordinary, read-only command:
$ findmnt -t cgroup,cgroup2 -o TARGET,FSTYPE,OPTIONS
TARGET FSTYPE OPTIONS
/sys/fs/cgroup cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot
cgroup2 means that this host is using the unified cgroup v2 filesystem at that mount point. A cgroup row means a legacy v1 filesystem. It is possible for a system to expose both versions during a transition, so do not reduce a multi-row result to one label without checking the mount points.
Checkpoint: confirm the controller interface exposed by the v2 mount:
$ cat /sys/fs/cgroup/cgroup.controllers
cpuset cpu io memory hugetlb pids rdma misc
This list is the controllers available at the v2 root on the host. It is not a list of limits currently applied to every process, and it does not say which controllers a child cgroup has enabled in its subtree. Do not write to cgroup.subtree_control merely to make a controller appear. That changes resource-control policy and belongs in a planned, privileged configuration change.
4. Locate the shell in the hierarchy
Read the current process's membership record:
$ cat /proc/self/cgroup
0::/user.slice/user-1004.slice/[email protected]/session.scope
For v2, the line uses the format 0::PATH. The empty middle field identifies the unified hierarchy, and the final field is the cgroup path relative to its root. Your path will normally contain different systemd or container names. An empty path, shown as 0::/, means the process is in the v2 root cgroup.
For a quick comparison, inspect the mounted tree without changing it:
$ stat -fc '%T %m' /sys/fs/cgroup
cgroup2 /sys/fs/cgroup
$ test -r /sys/fs/cgroup/cgroup.procs && echo cgroup-v2-root-readable
cgroup-v2-root-readable
The stat filesystem type and the /proc/self/cgroup record should agree. If they do not, check whether you are inside a container or a different mount namespace. A container can show a restricted view of the host's hierarchy.
5. Use the result correctly
Use /proc/cgroups when you need a quick controller inventory or when diagnosing older tooling. Use findmnt, /sys/fs/cgroup and /proc/self/cgroup to answer the operational questions: which cgroup API is mounted, which v2 controllers are visible, and where is this process attached?
Do not use hierarchy numbers from /proc/cgroups as paths under /sys/fs/cgroup. Do not assume a controller listed as enabled is active for your current service. In v2, controllers are enabled for subtrees through the hierarchy, and limits are exposed by files in the relevant cgroup directory. A service manager such as systemd may also create and move cgroups on your behalf.
If a script needs to consume the result, retain the raw lines and record the mount type alongside them. This avoids reporting a v1-style hierarchy number as if it were a stable v2 identifier. Re-run the checks after a container, service manager or boot configuration change because the visible namespace and mounts can change.
Done means
- You checked the installed
proc_cgroups(5)version and its limited scope. - You read the four columns without treating example counts as universal values.
- You confirmed the mounted filesystem with
findmnt. - You checked available v2 controllers without writing to cgroup control files.
- You located the current shell with
/proc/self/cgroup. - You know when a container or mount namespace may be hiding the host hierarchy.