Skip to content

[Bug]: meshtasticd (portduino/Linux) VSZ + swap grow without bound under sustained load — VmPeak ~915 GB, VmSwap ~1.78 GB after 10 days #10468

Description

@Nursedude

Summary

meshtasticd (Linux/portduino build) grows its virtual address space without bound on long-uptime hosts. On two boxes in a 5-box fleet I observed VmPeak ~915 GB and VmSwap ~1.78 GB after 9–10 days of uptime, while VmRSS stayed at ~45 MB. The 1.78 GB of touched-then-swapped pages pegged one Pi 5's swap at 100% and starved the rest of the stack. A clean restart drops VmPeak back to ~280 MB and VmSwap to 0.

I'm filing this from the perspective of an operator running a small permanent NOC fleet — happy to instrument further if it helps narrow it down.

Environment

  • Version: meshtasticd 2.7.15 (all five fleet boxes are on the same build)
  • OS: Debian / Raspberry Pi OS bookworm (trixie on one)
  • Arch: aarch64
  • Hardware: mix of Pi 4B (Cortex-A72), Pi 5 (Cortex-A76), and a Pi Zero 2 W (Cortex-A53)
  • Service: meshtasticd.service running long-lived as a systemd unit
  • HATs: Various Meshtastic SX1262 HATs via SPI

Fleet snapshot at the time of investigation

Box Hardware Preset User Uptime VmPeak VmSwap RSS
A Pi 5 LongFast root 9d 7h 927 GB 1.42 GB 437 MB
B Pi 5 LongFast root 10d 11h 915 GB 1.78 GB 45 MB
C Pi 4B LongFast root 4d 11h 714 MB 1.7 MB 10 MB
D Pi 4B SHORT_TURBO meshtasticd 21d 5h 714 MB 0 18 MB
E Pi Zero 2 W SHORT_TURBO root 9d 10h 280 MB 1.7 MB 6 MB

The pattern hits boxes A and B; doesn't hit box D despite the longest uptime (21 days). Same firmware version on every box.

Two variables correlate with the leak in this small sample:

  1. LongFast preset (higher packet rate, broader gossip) — both leaky boxes are on LongFast; both heavily-trafficked boxes leak; the two SHORT_TURBO boxes don't.
  2. Heavy local API polling — boxes A and B both have a local NOC stack hitting /api/v1/fromradio?all=true and :4403 TCP on a continuous cycle. Boxes C/D/E don't have that polling pattern.

Box C is on LongFast but only 4 days uptime, so it's not yet to the threshold where A and B exhibited the leak. Worth retesting after another week.

Reproduction observations

On the leaky boxes, VmData was ~908 GB while VmRSS was 45 MB:

Name:    meshtasticd
VmPeak:  915812448 kB
VmSize:  915804192 kB
VmRSS:      45232 kB
VmData:  908017456 kB
VmSwap:   1785328 kB
Threads:        6

This suggests sparse anonymous mappings — large committed-but-untouched virtual regions that don't add up to RSS, plus a smaller (but absolute-terms still significant) ~1.78 GB of pages that DID get touched and then got pushed to swap.

Threads stayed at 6 — this isn't a thread leak.

What the operator sees

Symptom on a 4 GB Pi 5: Swap: 2.0Gi / 2.0Gi (100% full) with all other processes running normally. Once swap saturates, meshtasticd's own pages start thrashing (continuous si/so activity in vmstat) and any latency-sensitive co-tenant on the box (local map server, dashboard, monitoring stack) degrades. Eventually load climbs and the box stops servicing inbound HTTP requests in time.

Recovery

sudo systemctl restart meshtasticd

Immediately frees VmSwap back to 0 and VmPeak back to ~280 MB. Radio API on :9443 is responsive again within ~10 seconds. No data loss observed on the radio side.

Mitigation in my fleet (operator workaround, not a fix)

I deployed a weekly meshtasticd-restart.timer to bound the growth at 7 days. Treating this as a band-aid until the underlying allocation pattern is found.

Hypothesis

I'm not deep in the firmware allocator paths, but the combination of:

  • VmData ~900 GB (mostly uncommitted but reserved)
  • VmRSS small but VmSwap multi-GB (touched-then-cold pages)
  • Only triggered under high-traffic preset + heavy local API polling

…feels consistent with either (a) growing per-packet/per-route state that gets allocated and not freed (Router history, NodeDB, MQTT queue?), or (b) a glibc/jemalloc fragmentation pattern where freed allocations leave large arenas reserved but never returned to the OS. (a) would be a real leak; (b) would be malloc behavior that's only visible because of the workload.

Happy to capture /proc/$pid/smaps, /proc/$pid/maps, or attach a pmap snapshot from a leaky box if useful — I can rebuild the leak in a few days now that we've reset. Also happy to try MALLOC_ARENA_MAX=2 as a probe if that would help separate (a) from (b).

Related

Cross-link from operator-side ops memo: this triggered a cascade where my local NOC's cloud snapshot pipeline went stale for ~3h because the map service on the leaky box stopped responding under swap pressure. Not a meshtasticd bug per se, but the swap thrash made it the proximate cause.

Metadata

Metadata

Labels

help wantedWe'd welcome help on this issuelinux-nativerelated to running meshtastic as daemon on native linux

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions