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.
What the symptom actually tells you
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.
Check whether lxcfs is in the picture at all
/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/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.
Red herrings worth ruling out
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.
Reading the real limit and usage from cgroups
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 countersmemory.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.
Checking and fixing it from the Proxmox host
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.currentpct 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/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.