FreeBSD Jail Memory Metrics

Date: 2026-02-11
Host: FreeBSD jail host
Component: RACCT/RCTL + node_exporter textfile collector
Outcome: Per-jail RAM/VMEM/SWAP metrics in Prometheus/Grafana

grafana FreeBSD jail metrics.

per-jail RAM, VMEM, and swap metrics in Prometheus/Grafana.

One of the most satisfying improvements I’ve made lately was getting per-jail memory visibility working via jail_mem_prom.sh.

Memory usage on FreeBSD felt like a black box at the jail level you could see host pressure, but not how individual services were actually behaving. Exporting per-jail memory metrics so that I can tell which jail is quietly hoarding RAM, which ones spike under load, and which are behaving exactly as expected.

It turns vague suspicion into concrete signals, and that alone makes capacity planning and debugging dramatically less painful.


FreeBSD Per-Jail Memory Metrics (RACCT/RCTL via Prometheus via node_exporter)

This guide enables per-jail memory accounting on FreeBSD using RACCT/RCTL, and exports the metrics to Prometheus/Grafana via node_exporter’s textfile collector.

This is real per-jail accounting (not “sum RSS and hope”), and it’s the missing piece when you’re running a bunch of services inside jails.


What You’ll Get

Exported Prometheus metrics (per jail):

  • jail_memoryuse_bytes{jail="<name>"}
  • jail_vmemoryuse_bytes{jail="<name>"}
  • jail_swapuse_bytes{jail="<name>"}

Mapped from:

rctl -u jail:<name>

Fields used:

Per-jail memory metrics (RACCT / rctl)
├─ memoryuse  --> resident memory used (RSS)
├─ vmemoryuse --> virtual memory footprint (VM size)
└─ swapuse    --> swap consumption

1) Verify RACCT support exists

sysctl kern.racct.enable

If the OID exists, your kernel includes RACCT/RCTL; if it doesn’t, you’re running a kernel without RACCT/RCTL.

2) Enable RACCT at boot

Edit:

/boot/loader.conf

Add:

kern.racct.enable="1"

Reboot the host.

Verify after reboot:

sysctl kern.racct.enable

Expected:

kern.racct.enable: 1

3) Verify rctl works (root)

doas su
rctl -u jail:skynet

Example fields you should see:

memoryuse=...
vmemoryuse=...
swapuse=...

4) Create the exporter script (node_exporter textfile collector)

Save as:

/usr/local/bin/jail_mem_prom.sh
#!/bin/sh

OUT="/var/db/node_exporter/textfile_collector/jail_mem.prom.$$"
FINAL="/var/db/node_exporter/textfile_collector/jail_mem.prom"

{
  echo "# HELP jail_memoryuse_bytes Per-jail resident memory usage (rctl memoryuse) in bytes"
  echo "# TYPE jail_memoryuse_bytes gauge"
  echo "# HELP jail_vmemoryuse_bytes Per-jail virtual memory usage (rctl vmemoryuse) in bytes"
  echo "# TYPE jail_vmemoryuse_bytes gauge"
  echo "# HELP jail_swapuse_bytes Per-jail swap usage (rctl swapuse) in bytes"
  echo "# TYPE jail_swapuse_bytes gauge"

  jls -q name | while read -r jail; do
    [ -z "$jail" ] && continue
    rctl -u "jail:${jail}" 2>/dev/null | awk -F= -v j="$jail" '
      $1=="memoryuse"   { printf "jail_memoryuse_bytes{jail=\"%s\"} %s\n", j, $2 }
      $1=="vmemoryuse"  { printf "jail_vmemoryuse_bytes{jail=\"%s\"} %s\n", j, $2 }
      $1=="swapuse"     { printf "jail_swapuse_bytes{jail=\"%s\"} %s\n", j, $2 }
    '
  done
} > "$OUT" && mv "$OUT" "$FINAL"

Make it executable and test:

chmod +x /usr/local/bin/jail_mem_prom.sh
doas /usr/local/bin/jail_mem_prom.sh
tail -n 50 /var/db/node_exporter/textfile_collector/jail_mem.prom

5) Ensure node_exporter reads the textfile directory

In /etc/rc.conf:

node_exporter_enable="YES"
node_exporter_args="--collector.textfile.directory=/var/db/node_exporter/textfile_collector"

Restart:

service node_exporter restart

Verify metrics are exposed:

curl -s http://127.0.0.1:9100/metrics | egrep '^jail_(memoryuse|vmemoryuse|swapuse)_bytes'

6) Schedule the exporter (root cron)

doas crontab -e

Add:

* * * * * /usr/local/bin/jail_mem_prom.sh >/dev/null 2>&1

7) PromQL / Grafana examples

Per-jail resident memory:

jail_memoryuse_bytes

Top 5 RAM users:

topk(5, jail_memoryuse_bytes)

Virtual memory:

jail_vmemoryuse_bytes

Swap usage:

jail_swapuse_bytes

If you want a percent-of-host panel:

100 * jail_memoryuse_bytes / node_memory_MemTotal_bytes

Notes

  • This is accurate per-jail accounting via RACCT/RCTL (unlike RSS summation).
  • The exporter must run as root (cron as root) to read rctl usage.

Conclusion

You now have per-jail RAM, VMEM, and swap metrics in Prometheus/Grafana — and memory metrics go beyond host-level becoming something you can more easily debug.