Repository navigation
fix(linux): report container memory limits instead of the host's - #1118
Merged
Merged
Conversation
karlitschek
force-pushed
the
fix/container-memory-limits
branch
from
September 9, 2026 13:13
9b40e2c to
8563b1c
Compare
Member
Author
/proc/meminfo is not namespaced, so inside Docker, Kubernetes or AIO the Monitoring page and the OCS API reported the host's RAM rather than the memory Nextcloud is actually allowed to use. On a 2 GiB container running on a 64 GiB host, that is off by a factor of 32. Where a cgroup memory limit applies, read the limit and usage from it instead, preferring the v2 unified hierarchy and falling back to v1. Reclaimable page cache is discounted from the usage so that MemAvailable keeps the meaning it has in /proc/meminfo, and swap is taken from memory.swap.* on v2 and derived from the memsw counters on v1. A cgroup that is not more restrictive than the host is ignored, which covers both "max" on v2 and the near-PHP_INT_MAX sentinel on v1, so hosts without a limit keep reporting exactly what they reported before. Signed-off-by: Frank Karlitschek <frank.karlitschek@nextcloud.com> Assisted-by: Claude Code:claude-opus-5 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
karlitschek
force-pushed
the
fix/container-memory-limits
branch
from
September 9, 2026 13:46
8563b1c to
7a3df4f
Compare
jancborchardt
approved these changes
Sep 9, 2026
jancborchardt
left a comment
Member
There was a problem hiding this comment.
Seems to make sense to use the container limit if it gets reached first
Member
Author
|
/backport to stable35 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
/proc/meminfois not namespaced. Inside Docker, Kubernetes or Nextcloud AIO the kernel happily reports the host's memory to a container, soLinux::getMemory()has been describing the machine rather than the memory Nextcloud is actually allowed to use.This surfaces in two places:
mem_total/mem_free/swap_total/swap_free), which is what external monitoring tools graph and alert onOn a 2 GiB container running on a 64 GiB host the reported figure is off by a factor of 32, and — worse than being merely wrong — it is wrong in the reassuring direction: a container about to hit its limit and get OOM-killed shows plenty of free memory right up until it dies.
Change
Where a cgroup memory limit applies, read the limit and usage from the cgroup instead of
/proc/meminfo:memory.max,memory.current,memory.stat,memory.swap.max,memory.swap.currentmemory.limit_in_bytes,memory.usage_in_bytes,memory.stat, and thememory.memsw.*counters, from which the swap share is derived by subtracting the memory limitTwo details worth calling out:
inactive_fileon v2,total_inactive_fileon v1), soMemAvailablekeeps the meaning it has in/proc/meminfowhileMemFreestays the stricter number. This is the same accountingdocker statsdoes.maxon v2, and the near-PHP_INT_MAXsentinel on v1 — and it also guards against a limit set above the host's physical memory.Anything unreadable falls back to the previous behaviour, so a host without cgroups, with
open_basedirin the way, or inrestricted_modereports exactly what it reported before.Tests
Six new cases in
LinuxTest, plus twomemory.statfixtures:memory.max = maxPHP_INT_MAXsentinelmemory.statThe existing memory tests needed one adjustment: they pinned
readContentto/proc/meminfowith->with(...), which constrains every invocation. They now go through a smallmockFiles()helper that serves a map of paths and throws for anything else — which is exactly whatreadContentdoes for a file that does not exist.Verified locally with PHPUnit 10 (29 tests, 54 assertions in
LinuxTest;FreeBSDTest,DummyTestandNetInterfaceTestalso still green), and cross-checked by disabling the new code path to confirm the added tests actually fail without it.php-cs-fixerreports no style violations.Deliberately not in scope
/proc/cpuinfohas the same problem, but the honest fix is not symmetrical:sys_getloadavg()is host-wide, so reporting a container's CPU quota as the core count while continuing to divide the host's load average by it would make the percentage worse, not better (host load 8 on a 16-core host, quota of 2 → "400 %"). Doing this properly means samplingcpu.stat'susage_usecbetween polls, which is a larger change and belongs in its own PR.MemoryMax=lives at/sys/fs/cgroup/<slice>/<unit>/, not at the root. Resolving that through/proc/self/cgroupwould extend the fix to those setups, but it must not be applied blindly: under cgroup v1 a container's/proc/self/cgroupnames a path (/docker/<id>) that is not readable from inside the container, where the root path is. Happy to follow up if you'd like it covered.