Kubernetes' container_memory_working_set_bytes metric, calculated as current minus inactive file cache, can badly misrepresent actual memory pressure for Postgres workloads. Through a series of Docker-based experiments with pgbench and manual sort queries, the analysis shows that Postgres shared buffers (shmem) and active page cache pages behave differently from simple anonymous memory, causing the metric to either understate or overstate real OOM risk depending on cache size and workload. A smaller shared_buffers setting can actually reduce crash risk even though the working-set metric looks worse. The recommendation is to track current minus (inactive_file + active_file) instead, ignoring the page cache entirely, and to consider lowering shared_buffers when running Postgres under Kubernetes memory limits. MGLRU (Multi-Gen LRU), now enabled by default on recent Debian and Ubuntu LTS, changes how pages are classified as active, adding further nuance.
Table of contents
Kubernetes Node E2E TestsPostgres Simple Sort (ORDER BY)Postgres with a Small WorkloadPostgres Shared Buffer Cache without any WorkloadPostgres with a Realistic WorkloadPostgres with a Smaller Buffer CacheAppendix: MGLRUQuestions this post answers
Why does container_memory_working_set_bytes not accurately reflect Postgres memory usage on Kubernetes?
Because it only excludes inactive file cache pages (current minus inactive_file), while Postgres shared buffers allocated as shmem and active page cache pages are not excluded, so the metric can look high when the database actually has plenty of headroom, or vice versa. Testing showed a database with a larger buffer cache crashing at fewer sorted rows despite a similar working-set reading, while a smaller buffer cache configuration sorted more rows without crashing even though the metric looked worse. Teams tuning Postgres memory limits on kubernetes can track deeper cgroup metric guidance like this on daily.dev.
What metric should I use instead of container_memory_working_set_bytes to monitor Postgres memory usage on Kubernetes?
Use current minus (inactive_file plus active_file), which excludes the entire page cache rather than just inactive pages. This was found to be a more reliable indicator of real memory pressure for Postgres, since shared buffers (shmem) are not reclaimable like page cache and can trigger OOM crashes even when the default Kubernetes metric still looks acceptable. Anyone building custom Postgres monitoring on kubernetes can compare cgroup metric approaches on daily.dev.
Should I lower Postgres shared_buffers when running under a Kubernetes memory limit?
Yes, running with shared_buffers lower than the typically recommended amount can reduce the risk of out-of-memory crashes under a Kubernetes memory.max limit. Testing showed a container with shared_buffers=32MB sorted 4 million rows without crashing, while the same workload with a larger buffer cache crashed at only 3.5 million rows, because non-reclaimable shared buffer pages leave less room for query working memory. Developers sizing Postgres shared_buffers for containerized deployments can find configuration tradeoffs like this on daily.dev.