Bali, Indonesia
Insights

Why an LXC Container on Proxmox Shows the Host's Full RAM in free and top

October 25, 2026
0:00
You give an LXC container 2 GB of RAM in Proxmox, log in, run free -h, and it reports the host's full memory, maybe 64 GB or more. top and htop agree with it, and Proxmox's own summary page shows something different. So which number is real, and how close is the container to running out? The short answer is that the limit you set is almost certainly being enforced. The tools inside the container just aren't seeing it. This walkthrough starts from that symptom and works down to the cause, then shows where to read the numbers that actually matter.
An LXC container is not a virtual machine. It has no kernel of its own. Every process in the container runs on the host kernel, and isolation comes from kernel features: namespaces control what a process can see, and cgroups (control groups) control how much CPU, memory, and I/O it can use. When you set memory in Proxmox, it writes a limit into the container's cgroup. The kernel enforces that limit. If the container goes over it, the kernel reclaims memory inside that cgroup, and if that fails, the OOM killer ends a process in the container. The host's other memory doesn't come into it. Tools like free and top don't ask the cgroup anything, though. They read /proc/meminfo, a file the kernel generates. /proc/meminfo was never namespaced, so the kernel always fills it with host-wide figures. A container that reads the raw file sees the host's RAM. Proxmox works around this with lxcfs. lxcfs is a small FUSE filesystem (a filesystem implemented by a userspace program) that runs on the host. When a container starts, lxcfs files are bind-mounted over /proc/meminfo and a few related files inside it. When something reads them, lxcfs looks up the reader's cgroup and returns figures based on that container's limit and usage. So seeing the host's full RAM usually means one of two things. Either the lxcfs overlay isn't in place for that container, or the program you're looking at doesn't read /proc/meminfo at all.
Start inside the container. Two quick reads tell you most of what you need: is /proc/meminfo an lxcfs mount, and what does the cgroup say?
BASH
# Inside the container: lxcfs-backed files show up as fuse.lxcfs mounts
grep lxcfs /proc/mounts

# Compare the total that free reports with the cgroup limit (cgroup v2)
free -h
cat /sys/fs/cgroup/memory.max
If the first command prints lines for /proc/meminfo, /proc/cpuinfo, /proc/stat, and similar paths, lxcfs is mounted. In that case free should report something close to your configured limit. If it prints nothing, the overlay is missing and every procps tool will show host figures. That's the most common root cause. The usual reasons are that lxcfs wasn't running when the container started, or that lxcfs was restarted or upgraded while the container kept running and the old mounts went stale. Stale mounts often show up as errors like Transport endpoint is not connected when you read /proc/meminfo, rather than as wrong numbers. If you see cat: /sys/fs/cgroup/memory.max: No such file or directory, the host may be on cgroup v1. There, the limit lives in a file such as memory.limit_in_bytes under the memory controller directory. The exact path depends on the setup.
A few things look related but usually aren't. The limit not being applied. People often assume that a host-sized free output means the container can use all host RAM. Normally it can't. The cgroup enforces the limit whether or not lxcfs is mounted. lxcfs only changes what the container is told, not what it's allowed to use. That's what makes this confusing in support work: a customer sees plenty of free memory in top, then a process dies with an OOM kill and it looks impossible. Ballooning and KVM settings. Memory ballooning is a VM feature and doesn't apply to containers. If you're comparing the container with a KVM guest that reports its RAM correctly, the difference is that a VM has its own kernel generating its own /proc/meminfo. The application rather than the container. If free looks right but an application still sizes itself to the host, lxcfs is working fine. Some programs call the sysinfo(2) syscall to get total memory instead of reading /proc/meminfo. lxcfs only virtualizes files, so syscalls go straight to the host kernel. Runtimes that size a heap or a cache from total memory may then pick a value far above the container limit. The fix there is to set that application's memory size explicitly. Changing anything in Proxmox won't help. Privileged versus unprivileged. Both container types normally get lxcfs. Switching from one to the other is not a fix for this symptom.
Even with lxcfs working, cgroup files are the most direct source of truth. They are also what monitoring inside a container should read if it needs to know how close you are to the limit.
BASH
# Inside the container (cgroup v2)
cat /sys/fs/cgroup/memory.max      # limit in bytes, or 'max' if none at this level
cat /sys/fs/cgroup/memory.current  # current usage, including page cache
grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat  # anon = process memory, file = page cache
cat /sys/fs/cgroup/memory.events   # oom and oom_kill counters
A few notes on reading these. memory.current includes page cache, which is file data the kernel keeps in RAM. A container running a database or serving lots of files can sit near its limit and still be healthy, because the kernel reclaims cache before it starts killing anything. The anon line in memory.stat is closer to what people mean by memory used by processes. memory.events is the most useful file for the question "did we actually run out?" A rising oom_kill count means the kernel killed processes in this cgroup. You can't get that from free at all. If memory.max shows max inside the container even though you set a limit, that doesn't automatically mean there's no limit. Depending on how the cgroup namespace is arranged, the container's view of /sys/fs/cgroup may be a child of the cgroup that actually holds the limit. In that case, check from the host.
On the host you can confirm the configured limit, the enforced limit, and whether lxcfs is healthy. Replace 101 with your container ID. It's used here as an illustrative example.
BASH
# Is lxcfs running on the host?
systemctl status lxcfs

# Configured memory and swap for the container (values in MiB)
pct config 101 | grep -E '^(memory|swap):'

# What the kernel is enforcing and the current usage (cgroup v2 layout)
cat /sys/fs/cgroup/lxc/101/memory.max
cat /sys/fs/cgroup/lxc/101/memory.current
pct status 101 --verbose also reports memory figures for the container on recent versions, if you prefer one command. If memory.max on the host matches the configured value, the limit is in force and the problem is only visibility. If lxcfs is running but the container has no lxcfs mounts, the usual fix is to restart the container so it picks up fresh mounts:
TEXT
pct reboot 101
That interrupts whatever runs in the container, so schedule it like any other restart. Be careful about restarting the lxcfs service itself on a host with running containers. Depending on the lxcfs version, doing that can leave every running container with broken /proc files until each one is restarted. The safe order is to make sure lxcfs is running and healthy first, then restart the affected containers one at a time. Don't restart lxcfs to fix a single container. If the overlay keeps disappearing after host package upgrades, look at the host's journal for lxcfs messages around the upgrade time. Also check whether the containers were left running across the upgrade. The practical takeaway: inside an LXC container, treat free and top as a convenience view that is only right when lxcfs is mounted. When the numbers look wrong, check grep lxcfs /proc/mounts first. To know whether a container is really short on memory, read memory.current, memory.stat, and especially memory.events from the cgroup, and confirm the enforced limit from the host. If an application still behaves as if it has host-sized RAM while free looks correct, the application is reading memory through a path lxcfs doesn't cover, and it needs an explicit memory setting.