Read this first if you are running 2.0.2
2.0.2 can shrink a container until the kernel kills a process inside it. If any of your containers use tmpfs or shared memory, upgrade. That includes more workloads than it sounds like: /run is tmpfs in every systemd container, PostgreSQL's shared buffers are shared memory, and Redis, Chrome and most build systems use /dev/shm.
The page-cache exclusion added in 2.0.2 subtracted the file key of memory.stat. On cgroup v2 that key includes shmem, and shared memory is swap-backed: on a container with no swap the kernel cannot reclaim it at all. Discounting it reports an almost-full container as empty.
Measured on Proxmox VE 9.1, a 512 MB container with swap disabled holding 200 MB in /dev/shm:
| Actually in use | 205 MB of 512, 39.9% |
| Reported by 2.0.2 | 0.41% |
| Result | shrunk to the 256 MB floor, then oom_kill 1 on the next ordinary allocation, still reporting 0.79%
|
| Reported by 2.0.3 | 39.48%, container left alone |
It could not recover on its own, because by its own measurement there was nothing to relieve.
If you are on 2.0.2 and cannot upgrade now, set memory_exclude_cache: false. That restores the pre-2.0.2 accounting, in which containers doing file I/O never scale down, which is the opposite failure and the safe one.
Note that the reported figure now differs from the Proxmox UI for containers holding shared memory, because the UI subtracts the file total whole. That difference is deliberate: the UI describes the machine, this number decides whether to take memory away from it.
Also in this release
CPU core pinning never worked, on any hardware. p-cores and e-cores were resolved from /sys/devices/system/cpu/cpu*/topology/core_type, an attribute that has never existed in mainline Linux: it is absent from drivers/base/topology.c at every tag checked from v3.16 to v6.17 and from all three ABI documents. It was not an AMD gap, as originally reported; a hybrid Intel i9-12900 failed identically. p-cores pinned containers to every core and e-cores was skipped in silence. P and E cores are now read from the hybrid perf PMU the kernel does create, /sys/devices/cpu_core/cpus and /sys/devices/cpu_atom/cpus, whose presence is itself the hybrid signal. Reported and largely fixed by @MatrixMagician, who also caught and corrected their own framing before it merged.
Two vendor-neutral pinning groups, l3:N and numa:N, derived from generic sysfs and available on any CPU. On a single-socket host with one L3 domain both resolve to every CPU, which is no restriction; check the startup line before relying on them.
Pinning writes now reach the container. The write path resolved /etc/pve/lxc/<ctid>.conf and then refused the result as a symlink attack, which it always is: /etc/pve/lxc is a pmxcfs symlink to nodes/<hostname>/lxc/. Under use_remote_proxmox the config was read over SSH and written back with a local tee, so the remote node never received it.
Explicit cpu_pinning ranges are validated. 31-0, 0-999 and 99 were written verbatim into the container config, and a cpuset the kernel refuses stops the container from starting.
Horizontal scaling: every clone received the same static IP, and min_instances was documented but silently ignored. Both fixed. The feature remains experimental and has known defects beyond these: see the guide.
CI now runs the test suite. It never did. The only Python workflow ran pylint, so a change that broke a test reached main with every check green, and pull requests from forks ran nothing at all. The matrix is 3.10 through 3.14; the daemon does not run on 3.9 and the documentation claiming otherwise is being corrected.
Upgrading
Re-run the installer, or pull main. Existing configuration files are compatible. min_containers still works as a deprecated alias for min_instances.