# Reading cgroup v2 memory stats in Go

> A tiny example of poking at the kernel's cgroup v2 interface from Go, and what the numbers actually mean.

Author: Hugo Sulaiman Setiawan · Published: 2026-10-02
Canonical: https://hugosetiawan.com/blog/reading-cgroup-memory-in-go

> Sample post — replace it with your own. It's here to exercise code highlighting.

On a cgroup v2 system every cgroup is a directory under `/sys/fs/cgroup`, and its
memory accounting lives in plain text files. No library required.

![Tree of /sys/fs/cgroup with system.slice containing memory.current](https://hugosetiawan.com/_app/immutable/assets/cgroup-tree.18Zmz2cO.svg 'Each cgroup is a directory; its accounting lives in plain files')

```go
package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"
)

// memoryCurrent returns the current memory usage of a cgroup in bytes.
func memoryCurrent(cgroup string) (uint64, error) {
	raw, err := os.ReadFile("/sys/fs/cgroup/" + cgroup + "/memory.current")
	if err != nil {
		return 0, fmt.Errorf("read memory.current: %w", err)
	}
	return strconv.ParseUint(strings.TrimSpace(string(raw)), 10, 64)
}

func main() {
	n, err := memoryCurrent("system.slice")
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	fmt.Printf("system.slice uses %d MiB\n", n>>20)
}
```

You can sanity-check the result from a shell:

```sh
cat /sys/fs/cgroup/system.slice/memory.current
systemd-cgtop -m --depth=1
```

## What the number includes

`memory.current` counts anonymous memory **and** page cache charged to the cgroup, so a
service that reads a lot of files will look "bigger" than its RSS. For the breakdown, read
`memory.stat`:

| field  | meaning                         |
| ------ | ------------------------------- |
| `anon` | anonymous memory (heap, stacks) |
| `file` | page cache                      |
| `slab` | kernel slab allocations         |
