RoonAppliance memory leak causing high RAM usage on Ubuntu 24.04.4 LTS (ref#FIB170)

Hi! What’s not quite right with Roon?

· None of the above quite fits

None of the above quite fits

· None of these quite match

Tell us what's going on

· RoonAppliance memory leak — grows to ~60% of system RAM over a few days, repeats regularly

Running RoonServer 2.71 (build 1683), production, on Ubuntu 24.04.4 LTS (kernel 6.17.0-35-generic), 31GB RAM, x86_64.

RoonAppliance (the appliance process, not RoonServer itself) grows unbounded over a few days of uptime until it exhausts system memory. Most recent instance: after 3 days 20 hours uptime, RSS reached ~19GB (59.7% of total system RAM), pushing the host's 8GB swap to fully exhausted and RAM to 379MB free. Caught it manually before an OOM kill actually happened — journalctl -k shows no OOM events, but the system was at the edge.

This has recurred a few times. A systemctl restart roonserver.service reliably fixes it — RSS drops from 19GB to ~1.1GB immediately after restart — but it comes back over the following days.

No crash reports or errors in the Roon logs I could find pointing at a cause. Happy to grab diagnostics (Roon's built-in diagnostic export, specific log files) if that helps triage.

Tell us about your home network

· Wired network via over TP-Link TL-SG108PE Gigabit PoE Switch

Hey @Helge_Gudmundsen, welcome to the community, and thanks for the detailed report.

The memory growth you’re seeing is not expected, especially with the swap getting exhausted over a few days. Since a simple service restart clears it, we’d like to collect a log set around the next rise in memory so we can see what RoonServer and RoonAppliance are doing before the system gets tight.

Please follow the log collection steps, then upload the resulting logs with our secure uploader. Once that’s done, note the exact local time, date, and what the memory usage looked like when it starts climbing again, so we can line that up with the server logs. If anything changed recently on this machine, such as the latest update, a power event, or attached storage, please include that too.

Thank you, and let us know once the logs are up.

Hello, and thank you for the reply. The memory growth is linear from the start of the service. Currently it is at around 6GB, I will let it grow for a few days until memory is close to exhaustion, then collect logs and send them.

Perfect, thank you @Helge_Gudmundsen that sounds ideal.

Logs are uploaded using the secure uploader:
RoonLogs-helgeg-helgeg@helgegudmundsen.com-20260907-1103.zip

Capture time: 2026-09-07 11:03 WEST (UTC+1)

At capture, RoonAppliance had been running 3 days 19 hours since the last restart (2026-09-03 15:39 WEST) and was using ~17.5GB RSS. System memory was 27GB/31GB used, only 488MB RAM free, and swap (8GB) was completely exhausted — the system was genuinely under pressure at the time of capture, closely matching the severity of my original report.

I’m also attaching roon-appliance-rss-growth-curve.csv (included in the zip), which has RoonAppliance’s memory usage sampled every ~16 minutes continuously from the 2026-09-03 15:39 restart through the log capture — 345 data points showing the full climb from baseline (~1.2GB) up to ~17.5GB over that period, in case it’s useful for correlating against the server-side logs beyond the single snapshot above.

Hi @Helge_Gudmundsen,

Thank you for the logs and especially for the RSS sampling CSV, that combination let us pin this down precisely, and the diagnostics arrived in exactly the state we needed.

The growth is entirely in the .NET managed heap. At the point your logs end, RoonServer reports 17.1 GB physical, of which 16.5 GB is live managed objects, native memory, handles and steady-state thread count are all flat for four days. Your library (17,057 tracks) and Roon’s internal caches are completely static over the whole capture, so nothing on the library or search side is involved.

The driver is your Roon Server’s cloud push connection. Across the full ten days of logs there are 8,729 push connection sessions, and not one of them survived past 125 seconds. The median lifetime is exactly 120.0 seconds, and 71% are exactly 120.0 seconds to the second. Normally that connection stays up for hours or days.

Every time it drops, Roon treats it as a network change and re-initializes its entire cloud layer: a full re-login to your Roon account, TIDAL and KKBOX token refreshes, a discovery re-registration, a device-map re-registration, an ARC port check, and a re-publish of Roon’s services across every network interface on the machine. That is happening roughly 840 times a day.

Measuring the memory samples against those events, each cycle allocates a burst of ~23 MB and briefly spawns ~95 extra threads, then releases most but not all of it, a net ~7 MB retained per cycle. At 30 cycles an hour that is ~200 MB/hour, which matches your curve almost exactly, and the same figures reproduce across all three RoonServer runs in your logs.

Two things are going on:

The retention itself is on us. Even on a genuinely unstable connection, Roon should not hold onto memory across a reconnect, and we’re filing that internally with your logs attached.

But the reason it’s reconnecting every two minutes is local to your setup, and fixing that will stop the bleeding immediately. Your Roon Server is advertising 29 local IP addresses, roughly 28 Docker bridge networks plus a Tailscale interface. A hard, unvarying 120-second ceiling on an outbound connection almost always means something in the path is expiring the flow: routing or source-address selection across that many interfaces, or NAT idle timeout on the Vodafone router Roon detected via UPnP.

One test we’d like you to run:

Temporarily stop Docker and Tailscale, restart RoonServer, and let it sit for an hour:

sudo systemctl stop docker docker.socket
sudo tailscale down
sudo systemctl restart roonserver

Then check whether the push connection holds past two minutes:

grep -c "push2] connected" ~/.roon/RoonServer/Logs/RoonServer_log.txt

If that count stays low (a handful per hour instead of ~30) and your RSS curve flattens, we’ve confirmed it. Please keep your 16-minute sampling running through the test so we can compare the two curves directly.

Let us know how the test goes. :folded_hands:

Hi, thank you for looking into this. It is turning into a fun debugging journey :slight_smile:

I ran the test you asked for. One adjustment on timing: it ran ~9 hours (23:31–08:29) instead of the planned 1 hour — at first I called it early as inconclusive but the restore command didn’t actually get run until this morning, so I ended up with a full overnight dataset by accident. Turned out to matter — see below.

What I did

  • Stopped Docker (all 43 containers, including our Tailscale container) and RoonServer’s own network hairpin from ~28 Docker bridge networks + Tailscale
  • Restarted RoonServer at 23:31:52
  • Sampled RoonAppliance RSS every 16 minutes for the full window (CSV attached)
  • Counted push2] connected lines

Results

Reconnect frequency: unchanged. grep -c "push2] connected" across the ~9-hour test log gives 32–38 reconnects/hour every hour, all night — statistically identical to your baseline (~30/hour, 840/day). Removing the 28 Docker bridges and Tailscale did not stop the 120-second disconnect cycle. Whatever’s expiring that connection, it isn’t those interfaces.

But the leak rate dropped sharply. RSS went 673MB → 858MB over the ~8.5 hours after the initial post-restart transient settled (first ~70 minutes ran hot, 70–170MB/hour, then it dropped to a steady 13–20MB/hour for the rest of the night). That’s roughly 10–13x slower than the ~180–200MB/hour baseline you measured from my original logs — despite the reconnects still firing at the same rate.

My read

Sounds like the interface count isn’t causing the reconnects, but it may be scaling the damage per reconnect — with only ~1-2 interfaces to re-publish services across instead of 29, each cloud-layer re-init cycle has far less to do, so the ~7MB/cycle retention you measured may drop proportionally even though the cycle itself still fires just as often.

On the Vodafone NAT-timeout

I looked into this locally. Two checks:

  1. hlab’s own conntrack timeout is the Linux default (432,000s / 5 days)sysctl net.netfilter.nf_conntrack_tcp_timeout_established. Nothing on my side is configured to tear down connections at 120s.

  2. The actual disconnect log line is the same every single cycle, all ~300+ times across the test (this was captured with zero Docker bridges and zero Tailscale present):

    Trace: [push2] retrying connection in 0ms
    Trace: [push2] exception thrown. restarting connection (The operation was canceled.)
    

    OperationCanceledException is the .NET signature of a client-side timeout/cancellation token firing — not a network-level failure. A NAT or router session teardown would typically surface as a reset/aborted-connection error, not “operation was canceled.” The reconnects also alternate cleanly between two named backend pods (push-connector-v2-0 / push-connector-v2-1) every cycle, which reads like RoonServer’s own client code giving up after ~120s waiting on something from the connector (a ping/pong?) rather than a NAT mapping expiring underneath it.

Given that, I am not sure the Vodafone router’s NAT timeout is the driver here — this looks like it’s coming from the push-connector backend or RoonServer’s own websocket client timeout logic. Might be worth looking at that instead of the local network path.

Attached

  • HelgeGudmundsenTestResults-20260910.zip — RSS sample every 16 min, full test window, and full RoonServer/Logs folder covering the test

Let me know what you’d like to check next — happy to re-run isolating fewer variables (e.g., just Tailscale, or just a subset of the Docker bridges) if that would help narrow down the actual 120-second trigger. I will also keep poking at my network setup locally.

Thanks for the follow-up @Helge_Gudmundsen! Very useful information here. We’ve shared it with the development ticket tied to your issue, and will follow up once our development team has had a closer look.

We’ll be in touch as soon as possible! :folded_hands: