Articles
Linux cgroups Interview Questions: CPU Quotas, Memory Pressure, and Container Limits
Practice Linux cgroups interview questions with cgroup v2 CPU quotas, throttling counters, memory.high, memory.max, PSI, and OOM evidence.
The host dashboard says CPU is 40% idle, yet a container's request latency doubles. At the same time its memory graph rises, and a restart is blamed on “the OOM killer.” The best interview answer does not guess from host averages. It reads the container's cgroup limit and event files, takes counter deltas, and separates CPU throttling, memory reclaim pressure, and an actual OOM kill. These mechanisms can coexist, but they are not synonyms.
This guide uses the Linux kernel's cgroup v2 documentation for file meanings. The metrics and interview prompts below are original worked exercises, not candidate reports or claims about a named employer's question bank. For adjacent practice, Diagnose CPU Performance Problems on a Linux Server asks you to connect host and container observations.
Question 1: How can a container throttle when the host has idle CPUs?
In cgroup v2, cpu.max expresses a quota and period for the group's CPU use. A value such as 50000 100000 permits roughly 50 milliseconds of CPU time in each 100-millisecond period: effectively half a CPU's quota over that interval. The host can have spare cores while this group exhausts its own budget and waits for the next period. “Host idle” therefore does not falsify CPU throttling for a limited container.
Read cpu.max for the target cgroup, then sample cpu.stat twice across the latency window. With the CPU controller enabled, cpu.stat includes nr_periods, nr_throttled, and throttled_usec, alongside usage counters. Look at deltas over time rather than a lifetime total. If throttled periods and time rise when request latency rises, a quota limit is a plausible contributor. It is still not proof that raising the quota is the right fix; the application may have a single hot thread, lock contention, or excessive CPU work. Correlate cgroup data with per-process and per-thread profiles and with throughput.
If cpu.max reads max 100000, that cgroup has no local CPU quota at that level. An ancestor cgroup can still constrain it, and other CPU controls or scheduler effects can matter. The process may also be inside a different cgroup than the path you first inspected. A defensible answer traces the cgroup hierarchy and confirms the runtime's actual placement before announcing “no limit.”
Question 2: What is the difference between memory.high and memory.max?
The kernel documents memory.high as a throttling and reclaim boundary. When a cgroup exceeds it, processes are put under reclaim pressure and throttled; crossing it does not itself invoke the OOM killer. memory.max is a hard limit: if memory use reaches it and reclaim cannot reduce usage, the kernel can invoke an OOM kill within that cgroup. This distinction is central to interviews because a service may slow badly under memory.high without any killed process.
Read memory.current for current memory use, but remember it covers the cgroup and descendants. The number is not interchangeable with the heap size reported by a language runtime. File cache, kernel memory accounting, child processes, and other categories can contribute. Use memory.stat to understand composition, and observe the workload rather than inferring a leak from a single rising line. If memory.current falls after reclaim while latency spikes, pressure may be causing stalls rather than a persistent leak.
memory.events includes counters such as high, max, oom, and oom_kill. The kernel distinguishes an allocation hitting an OOM condition from a process actually being killed. A rise in high suggests crossing the reclaim boundary; a rise in oom_kill is direct evidence of kills attributed to that cgroup's event accounting. Because memory.events is hierarchical, events in descendants can be included. Use memory.events.local if you need events local to that cgroup, and inspect child groups where relevant. Do not call every oom event a killed container.
A cgroup v2 diagnosis matrix
This matrix is an original interpretation aid built from kernel-documented files. The proposed next moves are hypotheses to test in the live environment, not a universal remedy list.
| Observation over the incident window | Stronger interpretation | Next check |
|---|---|---|
Host idle; cpu.max is 50000 100000; nr_throttled rises | The group's half-CPU quota may limit work | Compare request latency, CPU profiles, and ancestor quota |
memory.events high rises; oom_kill flat | Reclaim/throttling pressure without observed kill | Compare memory.current, PSI, allocations and latency |
memory.events oom_kill rises | At least one attributed OOM kill occurred | Identify victim process, cgroup path, restart event |
memory.current high; no event delta | High use alone does not prove pressure or kill | Read memory.stat, PSI, workload baseline |
| Local files appear unlimited but ancestor is limited | Parent boundary may still constrain descendants | Walk cgroup ancestry and container runtime placement |
For the CPU row, calculate the quota explicitly: 50000 / 100000 = 0.5. That is a budget, not a promise the application will use exactly 50% of one core in every graph interval. Sampling windows, bursts, multiple threads, and parent limits affect observed usage. For the memory rows, record the counter values before and after the problem; an old nonzero oom_kill counter does not prove the current restart was caused by OOM.
Question 3: How would you use pressure stall information?
The cgroup v2 interface exposes memory.pressure for pressure stall information (PSI), describing time in which tasks are stalled because of memory pressure. CPU and I/O pressure interfaces also exist when available. PSI complements utilization and event counters. A service can show moderate memory use but severe stalls during reclaim; a flat high-water mark cannot reveal that time cost. Conversely, a container may have high cache use with little pressure.
Ask what the service-level symptom is. If p99 latency rises while memory.events high and memory PSI rise, reclaim pressure is a stronger hypothesis than “a leak” based solely on memory.current. Check allocation rate, garbage collection, page faults, and the file cache mix before changing a limit. A higher memory.high may reduce reclaim stalls but move the group closer to a hard limit or create host pressure. The safe decision depends on node headroom and the cost of other workloads.
PSI and cgroup counters are still diagnostic signals, not a direct cause trace. Correlation can be misleading if an upstream request surge simultaneously increases CPU, allocations, and latency. Use a controlled load test, capacity change, or profile to narrow the cause. In an interview, say what observation would make you abandon your first hypothesis.
Question 4: Which process did the OOM killer actually kill?
First establish whether this was a cgroup OOM event, a host-wide OOM event, an orchestrator eviction, or an application exit. A container restart reason can be suggestive, but the kernel event, runtime event, exit status, and process identity should agree. The cgroup's oom_kill counter and kernel logs can help identify a kill. Then ask which process was selected: a container with multiple workers may lose a child while PID 1 survives, so the “container restarted” dashboard may not tell the whole story.
Do not assume the application heap equals the cgroup limit. Native allocations, child processes, page cache, and allocator fragmentation can consume memory outside a managed runtime's heap accounting. A Java heap set below memory.max can still be accompanied by memory pressure or an OOM kill. The correct response is to compare the workload's complete memory footprint with the limit and the kernel's accounted categories.
If an incident has only a high memory.current sample and no event delta, say that OOM is unproven. If oom_kill rose in a descendant and memory.events at the parent rose as well, avoid counting two distinct kills without checking memory.events.local or process records. If host logs show a global OOM, the group's local hard-limit story may be wrong even if the service happened to use a lot of memory.
Question 5: How do container settings map to cgroup files?
Container runtimes and orchestrators translate CPU and memory settings into Linux cgroup controls, but the exact path and file values depend on runtime, cgroup version, node policy, and parent hierarchy. For a direct Docker container, CPU quota options can map to cpu.max under cgroup v2; memory limits can map to memory.max. Kubernetes resource requests and limits add scheduler and QoS behavior beyond the kernel files. A CPU request is not identical to a hard CPU quota, and a memory limit is not a reservation of physical memory.
The interview-friendly diagnostic is to verify, not assume, the mapping. Find the process's cgroup membership through /proc/<pid>/cgroup, locate the corresponding cgroup v2 directory on the host, and read the files there and at relevant ancestors. In a container, mount namespaces and read-only cgroup views may obscure the host path. Use the node's runtime or orchestrator metadata to avoid reading a neighboring workload's group by mistake.
Do not mix cgroup v1 and v2 file names in one command recipe. This article's cpu.max, memory.high, memory.max, and memory.events are cgroup v2 interfaces. A cgroup v1 node exposes different controller paths and files. Ask which mode the interview scenario uses; if unspecified, state the assumption before quoting a path or counter.
Question 6: What would a strong answer sound like?
“I would find the process's actual cgroup and read its v2 limits and counter deltas. A cpu.max of 50000 100000 can throttle a container even while the host has idle CPU, so I would compare cpu.stat changes with latency and profiles. For memory, memory.high indicates reclaim pressure, whereas memory.max can lead to a cgroup OOM kill if reclaim fails. I would inspect memory.events and memory.pressure, distinguish oom from oom_kill, check descendants and ancestors, and identify the victim process before calling the restart an OOM.”
The answer is useful because each claim has an observable file or event that can contradict it. It also avoids the common shortcut of increasing every limit. More quota may help a truly throttled service; it may merely hide inefficient code or shift pressure to other containers. Recheck user-facing latency and node health after any change.
| Practice question | Connection to this scenario |
|---|---|
| Diagnose CPU Performance Problems on a Linux Server | Move from host CPU to process and container quota. |
| Diagnose I/O, Memory, and CPU Constraints | Separate three kinds of resource pressure. |
| Investigate High Memory Usage | Test memory-growth and reclaim hypotheses. |
| Explain CPU-Bound vs I/O-Bound Work | Relate CPU demand to waits and bottlenecks. |
| Debug and mitigate a CPU spike incident | Connect counter deltas to an incident decision. |
Continue with Linux file descriptor interview questions and build a small incident table with cpu.max, two cpu.stat samples, memory.events, PSI, and p99 latency.
Sources and further reading
- Linux kernel: Control Group v2 — CPU, memory, events, and pressure files.
- Docker: Runtime options with Memory, CPUs, and GPUs — container runtime resource options.
- Kubernetes: Resource management for Pods and containers — request and limit semantics in Kubernetes.
- Linux kernel: Pressure stall information — meaning of pressure metrics.