Your container thinks it has infinite memory? Blame cgroup v2.
By Saket Jain Published Linux/Unix
Your container thinks it has infinite memory? Blame cgroup v2.
Technical Briefing | 10/25/2026
You’ve spun up a new container, maybe for that shiny new microservice, or just another batch job. You run `top` or `free -h` inside, and everything looks great. “Plenty of memory!” you think. Then, out of nowhere, your container gets OOM-killed. No obvious spikes in your host-level monitoring, no logs indicating memory exhaustion from the application itself. What gives? This bit me hard in production once, sending me down a rabbit hole trying to understand why my container was lying to me about its memory limits.
The sweet lies `cgroup v1` told us
For years, we got used to `cgroup v1`’s way of doing things. You’d `cat /proc/meminfo` inside a container and, while it *did* show the host’s total memory, most container-aware tools or libraries would automagically peek into `/sys/fs/cgroup/memory/memory.limit_in_bytes` or `/sys/fs/cgroup/memory/memory.usage_in_bytes`. And that’s what you *really* cared about: your container’s boundary and current consumption. It wasn’t perfect, but it worked well enough that you could often trust `free` or `top` to at least point you in the right direction. Libraries like those used by Java’s `MaxRAMPercentage` or NodeJS applications could typically pick up the correct memory limits from these paths without a fuss.
`cgroup v2`: The honest (but confusing) truth
But with `cgroup v2`, things got a little… unified. And by unified, I mean the old, familiar `/sys/fs/cgroup/memory/…` paths often just aren’t there anymore, or they’re empty. Instead, you’ve got a single, unified hierarchy. This is actually a cleaner design under the hood, but it means many of those old tools and libraries? They’re now either looking at nothing, or worse, they’re falling back to `/proc/meminfo`, which shows the *host’s* memory. And suddenly, your container thinks it has 64GB of RAM when Docker or Kubernetes actually capped it at 2GB. You’re effectively operating blind, until the kernel decides your application needs to go.
# From inside a cgroup v2 container
cat /proc/self/cgroup
cat /proc/meminfo | grep MemTotal
cat /sys/fs/cgroup/memory.max
cat /sys/fs/cgroup/memory.current
That first command, `cat /proc/self/cgroup`, is your tell. If you see `0::/some/path` instead of a list of subsystems, you’re on `cgroup v2`. Then, compare `MemTotal` from `/proc/meminfo` to `memory.max`. Notice the difference? The `memory.max` file holds the true limit set by your container runtime. And `memory.current` is the current usage *relative to that limit*, not the host. This matters huge for any JVM app that calculates heap based on available RAM, or for scripts trying to determine if they’re about to run out of memory.
- Always check `/proc/self/cgroup` first to identify the cgroup version your container is running under.
- For `cgroup v2`, rely on `/sys/fs/cgroup/memory.max` for the allocated memory limit and `/sys/fs/cgroup/memory.current` for current usage.
- Ensure your container orchestrator (Kubernetes, Docker, Podman) is using `cgroup v2` appropriately and that its internal metrics account for it.
- If you’re writing custom monitoring scripts for inside containers, update them to read the `cgroup v2` paths.
This isn’t just an academic detail; it’s a fundamental shift in how the kernel reports resource isolation. It’s why your otherwise healthy-looking app silently died, even though it thought it had memory to spare. Get used to these new paths and don’t blindly trust old `top` output inside your containers anymore. Knowing this difference is what’ll save you an afternoon of head-scratching when the next OOMKill hits a `cgroup v2` system.
