I've wanted a decent mobile client for LibreNMS for a long time. My phone opens to the LibreNMS web UI more often than I'd like, and every time I pinch-zoom into a device page I think about how much nicer a proper mobile app would be. Then I forget about it until the next page-load.

LibreNMS has been running my home and small-business monitoring for close to a decade. It's genuinely good software. The web UI is dense and fast on a desktop. On a phone, though, it's the same desktop UI with a viewport-meta tag, which means RRD graphs you can barely read, menus that assume you have a mouse pointer, and navigation that feels like scrolling through a spreadsheet with oven mitts on.

A couple of third-party mobile apps have shown up over the years. Each one has eventually gone quiet — unmaintained, broken against newer LibreNMS releases, or pulled from the store. For a long time I chalked that up to "solo developers lose interest," which is the easy explanation.

This week I sat down to build one and found out the real reason. It's not lost interest.

None of what follows is a knock on LibreNMS. Fourteen years of volunteer work on a tool that handles hardware from hundreds of vendors with varying MIB quality is remarkable by any measure. The friction I hit is a specific gap between the project's priorities and a mobile app developer's needs, not a flaw.


The plan

Flutter, iOS first. Multi-instance. Push notifications through a small Kubernetes-hosted relay that bridged LibreNMS's API alert transport to APNs and FCM. Subscription pricing — modest, with a trial. The explicit design rule was no RRD images as the primary display, because that's the thing every past LibreNMS mobile app has leaned on and that's what makes them feel old.

I had the scaffold running on a simulator by Tuesday. Device list worked, instance switcher worked, the whole shell was there. Wednesday I started building the device-detail screen, which is the screen. Everything else is navigation.

That's when I hit the wall.


What the API actually returns

For a device-detail screen you need, at minimum: CPU, memory, storage, and per-interface throughput. The docs at docs.librenms.org/API list these endpoints:

  • GET /api/v0/devices/:hostname/health/device_processor
  • GET /api/v0/devices/:hostname/health/device_mempool
  • GET /api/v0/devices/:hostname/health/device_storage
  • GET /api/v0/devices/:hostname/ports

Here's what they actually do against LibreNMS 26.4.1.

Ports. The /ports endpoint returns only ifName by default. No rates, no speeds, no admin state — just the interface name. To get anything else, you pass a columns= query parameter listing every field you want. I spent about ninety minutes convinced my home lab had zero traffic across every interface on every device before I noticed that footnote. It's documented. It's easy to miss. It makes the endpoint feel broken when you first hit it.

Class names. The /health/:type endpoint wants the device_ prefix on its class strings — device_processor, device_mempool, device_storage. The shorter forms that read naturally (processor, mempool) return a clean 200 OK with an empty array. The server happily queries the wrong internal table and tells you it found nothing. Another hour.

The one that actually matters. Once the class names were right and the columns param was in place, I hit LibreNMS issue #17737. Opened May 2025, still open:

The REST API endpoint for CPU usage returns an empty response despite the device having processor data available in the web UI.

I reproduced it immediately against my own instance. /devices/:id/health/device_processor returns {"graphs": [], "count": 0}. Same for device_mempool. Same for device_storage. The web UI shows the data. The graph image endpoint (/graphs/health/device_processor) renders a perfectly good PNG. The JSON endpoint a mobile app needs to build a structured UI returns nothing.

Athena, one of my Proxmox boxes, has a Ryzen 9 5950X showing 13% CPU in the web UI. To the v0 JSON API, athena has no processors.


Why mobile apps don't survive this

Once you see it, the pattern is obvious. A shipped mobile app has two options:

  1. Embed LibreNMS's server-rendered graph images. They work, they're up to date, they come straight off the LibreNMS server. They also look like they came off a 2008 RRD server, because they did.
  2. Wait for the JSON API to surface the data. That's been pending for nearly a year on this specific issue, and it's part of a broader gap where LibreNMS's web UI has clean access to everything and the v0 API only has some of it.

Both of those are bad foundations for a product someone will pay for.

The audience doesn't help either. LibreNMS admins at home, in small shops, and inside small-to-mid MSPs are not a mass market. A niche app that needs to chase an evolving open-source API to stay alive can't amortise its own maintenance. When the math stops working, the app goes quiet.

That's the mobile-LibreNMS graveyard. Not disinterest, not burnout, not "mobile is hard." Just a structural mismatch between what the web UI can show and what the API will hand you as JSON.


Credit where it's due

LibreNMS's maintainers have been running a monitoring system against almost the entire internet's worth of vendor hardware, on volunteer time, for fourteen years. That's not a small thing. The gap I ran into isn't a flaw in the project — it's a place where the obvious priority (making the web UI excellent) doesn't yet overlap with what someone building a different kind of client needs.

If I had more runway I'd submit a pull request for #17737 and stop writing about it. That would be a more useful contribution than this post. I haven't ruled that out.


What I kept

The Flutter app doesn't ship. What did come out of the week:

  • A working understanding of why every previous mobile attempt has ended up where it did. I'd rather have that than a middling app I'd need to abandon a year later.
  • A reusable pattern for small relay services — Node + Fastify + Postgres + Vitest, with tests gating the Docker build. I'll use that shape again the next time something needs to receive webhooks.
  • A Flutter architecture (Riverpod + freezed + Dio + a set of tolerant JSON decoders that survive PHP-style loose typing) I liked enough to keep around as a template.
  • A short, specific list of API changes that would unblock a real mobile client if I ever want to revisit this, or if someone upstream is curious what the view from outside looks like.

And one operational lesson for next time: query the production API surface before designing the UI that depends on it. I wrote three days of Flutter against endpoints I hadn't actually hit. The UI I built assumed JSON would arrive in a shape the server has never actually emitted. That's on me, and it's a mistake I can't afford to repeat on the next product.


If LibreNMS ships a fix for #17737, or lands a v1 JSON API that mirrors what the web UI already displays, I'll come back to this. The itch hasn't gone anywhere. A proper mobile LibreNMS client is still a thing I want to exist.

If you're thinking about building one yourself: start with the API. Query every endpoint your design depends on, against a real instance, before you pick a framework. Read #17737 and know what you're walking into.

That alone will save you a weekend.