Why FreeBSD Appears to Eat Your RAM (and Why It Doesn't Matter)
Demystifying FreeBSD's page queues, ZFS ARC, and why your monitoring tools are lying to you.
If you have recently migrated a build server, CI runner, or database node to FreeBSD, you might have experienced a brief moment of panic after running top or btop. The dashboard shows your memory usage flatlining near 90%, even when the system is idling.
This is not a memory leak, nor is it a sign of bloated daemon processes. Instead, it is the result of a fundamental architectural difference in how FreeBSD manages virtual memory, how modern filesystems like OpenZFS cache data, and how monitoring tools interpret those states.
Understanding these mechanisms is key to avoiding over-provisioning your infrastructure or chasing ghost resource crises.
The Heuristic Disconnect: Why Tools Lie
The confusion starts because "used memory" is not a standardized metric across Unix-like operating systems. Every system monitoring tool uses its own heuristic to decide what counts as active, cached, or free.
Consider two popular CLI tools: fastfetch and btop. If you run both on the same idle FreeBSD machine, you will get wildly different results. This is because of how they parse the kernel's memory statistics:
- Fastfetch assumes that inactive memory is practically free because the kernel can reclaim it instantly. Its formula is: $$\text{Free Memory} = \text{Free} + \text{Inactive} + \text{Cache}$$ $$\text{Used Memory} = \text{Total} - \text{Free Memory}$$
- Btop takes a much more conservative approach. It treats wired memory (which includes the kernel and filesystem caches) as fully used: $$\text{Available Memory} = \text{Total} - \text{Active} - \text{Wired}$$ $$\text{Free Memory} = \text{Free}$$ $$\text{Used Memory} = \text{Active} + \text{Wired}$$
If your system has a large disk cache, btop will report that you are running out of RAM, while fastfetch will show plenty of breathing room. Neither tool is wrong; they simply target different definitions of availability.
Under the Hood: FreeBSD's Page Queues
To understand why these tools disagree, we have to look at how the FreeBSD virtual memory (VM) subsystem manages physical memory. In the kernel source file sys/vm/vm_page.h, the system defines several page queues that dictate how physical memory pages (typically 4KiB) are juggled:
#define PQ_NONE 255
#define PQ_INACTIVE 0
#define PQ_ACTIVE 1
#define PQ_LAUNDRY 2
#define PQ_UNSWAPPABLE 3
These queues represent the lifecycle of a memory page:
- Active (
PQ_ACTIVE): Pages currently allocated to and actively accessed by userland processes or active kernel tasks. - Inactive (
PQ_INACTIVE): Pages that contain valid data but have not been accessed recently. The kernel keeps them in RAM in case a process requests them again, but they can be instantly reclaimed and reassigned if the system faces memory pressure. - Laundry (
PQ_LAUNDRY): Dirty pages (modified in memory but not yet written to disk) that are slated for swap. When memory gets tight, the kernel moves inactive pages here, flushes them to swap, and then marks them as free. - Wired (
PQ_UNSWAPPABLE/PQ_NONE): Pages locked in place. The kernel guarantees these pages will never be paged out to swap. This queue holds the kernel itself, network buffers, driver memory, and most importantly, the ZFS file cache. - Free: Purely empty, unallocated memory pages.
On a healthy FreeBSD system, the amount of purely "Free" memory will often trend toward zero over time. This is by design. Unused RAM is wasted RAM; the kernel uses every spare byte to cache files and metadata.
The ZFS ARC Tax
If you accepted the default installation options on a modern FreeBSD release, your system is running ZFS. Unlike traditional filesystems that rely on the operating system's standard page cache, ZFS uses its own caching mechanism called the Adaptive Replacement Cache (ARC).
ARC bypasses the standard VM page queues and manages its own memory footprint. Because ARC is managed directly by the kernel, its memory allocations are classified as wired.
This is why tools like btop report massive memory usage. They see gigabytes of wired memory and assume the system is heavily loaded, failing to realize that ARC is highly elastic. If a userland process demands more memory, the ZFS kernel module will shrink the ARC on the fly to release pages back to the system.
You can inspect the current size and limits of your ARC using sysctl:
sysctl kstat.zfs.misc.arcstats.size
sysctl kstat.zfs.misc.arcstats.c_min
sysctl kstat.zfs.misc.arcstats.c_max
To see the current ARC size in a human-readable format, pipe the output to gnumfmt:
sysctl -n kstat.zfs.misc.arcstats.size | gnumfmt --to=iec
On a dedicated database server, letting ZFS claim up to 90% of your physical RAM for ARC is ideal. However, on a CI/CD runner or a build machine where compilers like clang or rustc spawn massive, short-lived parallel processes, the default ARC behavior can cause issues. If the ARC does not shrink fast enough to accommodate a sudden spike in compiler memory demand, the system may trigger the Out-Of-Memory (OOM) killer or begin thrashing swap.
To prevent this, you can cap the maximum ARC size in /boot/loader.conf:
vfs.zfs.arc.max="4G"
Setting this limit ensures your build processes always have a guaranteed pool of un-wired memory to work with.
The Hypervisor Trap: Proxmox and Ballooning
If you run FreeBSD as a guest virtual machine on a hypervisor like Proxmox VE or KVM, the way FreeBSD handles memory can trigger false alerts on your host dashboard.
Proxmox relies on the VirtIO ballooning driver to communicate with guest operating systems and determine their actual memory usage. If the guest is not using much RAM, the host can reclaim those pages.
However, the FreeBSD VirtIO balloon driver does not always report detailed memory statistics back to the hypervisor. If you run info balloon inside the Proxmox monitor for a FreeBSD guest, you might see incomplete metrics:
balloon: actual=3072 max_mem=3072
Because the guest agent does not populate fields like free_mem or disk_caches, the hypervisor cannot tell the difference between active userland memory and volatile disk cache. Proxmox falls back to measuring the Resident Set Size (RSS) of the entire QEMU process on the host.
Since FreeBSD aggressively uses its idle memory for ARC and page caching, the VM's RSS quickly climbs to its maximum allocated limit. Your Proxmox dashboard will show the VM consuming 99% of its allocated RAM, even if the guest is completely idle.
If you are running FreeBSD in a virtualized environment, you should generally ignore the host-level memory graphs. Instead, rely on guest-level monitoring that understands the distinction between wired, active, and inactive queues.
A Developer's Monitoring Checklist
When managing FreeBSD nodes, you can get an accurate picture of your system's memory health by looking at the right metrics.
- Check Swap Usage First: If your system is actually running out of memory, it will begin paging to swap. Monitor swap activity using
swapinfo -h. If swap usage is low or zero, your system is not under memory pressure, regardless of whattopsays. - Inspect the Page Queues Directly: Use
sysctlto query the exact page counts:sysctl vm.stats.vm.v_active_count sysctl vm.stats.vm.v_inactive_count sysctl vm.stats.vm.v_wire_count sysctl vm.stats.vm.v_free_count - Account for ARC: Remember that
v_wire_countincludes the ZFS ARC. Subtract the ARC size from the wired count to find the memory actually locked by the kernel and drivers.
By treating inactive pages and the ZFS ARC as reclaimable space, you can avoid the trap of over-provisioning virtual machines and build servers. FreeBSD is not eating your RAM; it is simply putting it to work.
Sources & further reading
- FreeBSD ate my RAM — crocidb.com
- FreeBSD uses a big amount of RAM | The FreeBSD Forums — forums.freebsd.org
- FreeBSD Guest - wrong ram usage | Proxmox Support Forum — forum.proxmox.com
- freebsd memory usage - Support - Syncthing Community Forum — forum.syncthing.net
Lenn writes about cloud platforms, Kubernetes internals, and the infrastructure decisions that quietly make or break engineering organizations. Based in Berlin's vibrant tech scene, they have a talent for turning dense platform-engineering topics into prose that people actually finish reading.
Discussion 1
need to revisit my freebsd config, been meaning to dig into this