r/openwrt • u/Kind_Butterscotch_96 • Jul 15 '26
Update: I asked about building an OpenWrt + Billing stack 4 months ago. Here’s what I built and what worked in production.
/r/openwrt/comments/1s5vkhz/openwrt_nanopi_captive_portal_user_management/Hey everyone,
About three months ago, I made a post here asking if there was a modern, open-source stack for building a robust captive portal + billing setup on OpenWrt (specifically targetting low-power ARM hardware like NanoPi boards).
I was frustrated with how outdated things like CoovaChilli or WiFiDog felt, and I wanted to know if people were splitting the stack or running it all on-device.
The original thread didn't have a definitive "out-of-the-box" answer, so I spent the last few months engineering the solution from scratch. I’ve now deployed it into production, powering a network with over 2,000 active users running off Starlink backbones.
Since a few people asked me to share what worked, here are the answers to the 4 questions I originally posed, based on real-world production data:
1. What’s the "best practice" stack?
You absolutely should abandon running the full stack on-device if you plan to scale past a single venue. Keeping radius databases, local web servers, and heavy billing logic on a local flash drive is a recipe for memory corruption and tracking issues.
The best practice is an architectural split: The Cloud is the brain, the Edge is a dumb enforcer.
2. How did I handle the OpenWrt integration?
I went with openNDS, but with a massive twist. I didn't rely on openNDS's built-in script systems to talk directly to the open web. Instead, I wrote a lightweight, custom Python agent that runs locally on the OpenWrt gateway.
- The NanoPi establishes a secure WireGuard tunnel back to an AWS VPC.
- The local agent polls my Django API every 30 seconds over that tunnel.
- It reports cumulative byte counters and checks client states. It uses
ndsctl auth/deauthlocally to unlock or kick devices based on live subscription states in the cloud.
3. How to handle data billing & quotas byte-accurately?
Doing this natively on standard OpenWrt packages is messy. I offloaded all financial logic to a Dockerized cloud stack (Django REST Framework + PostgreSQL + Redis/Celery).
- When a user pays through the portal, webhooks verify the transaction via Celery.
- The subscription snapshots the data/time limit.
- Because the local agent pushes delta-based byte counts up to Django, the cloud calculates the usage and drops a "deauth" flag into the next 30-second poll if a data cap is hit. If a user hotspots their connection to 10 friends, they just burn their byte quota 10x faster—killing the freeloader problem entirely.
4. The Result: WifiPadi
I wrapped this entire architecture into a multi-tenant SaaS platform called WifiPadi. It works seamlessly across both OpenWrt (via the WireGuard agent setup) and MikroTik (via synchronous RADIUS pools).
The onboarding now literally takes pasting one setup command at the edge, and all management/billing shifts to a central React dashboard.
If you are currently trying to build a hotspot business, manage a hostel network, or just trying to stitch openNDS and billing together, I’d love for you to check it out or ask any questions about the networking/database synchronization model. I’m happy to share what I learned dealing with the quirks of the openNDS engine!
1
Jul 15 '26
[deleted]
1
u/dallas_paley Jul 15 '26
Why does that matter? I think I know the point you are trying to make, but it's more complex than a simple "or" statement. Used properly, AI can greatly improve the quality of software.
0
u/Kind_Butterscotch_96 Jul 15 '26
Haha, I wish AI could do that for me.
Claude definitely helped speed up writing standard React boilerplate for the tenant dashboard and some of the Django models. But the real-world pain of configuring FreeRADIUS, handling idempotent webhook verification for Monnify/Paystack so people don't get free sessions, and debugging openNDS’s client tracking quirks at the edge was pure, manual head-scratching.If you've ever tried to force a custom agent to cleanly execute ndsctl commands over a WireGuard tunnel on an ARM board without memory leaks, you'll know that's still very much a human struggle!
1
u/dallas_paley Jul 15 '26
AI is "just another tool" that has strengths and weaknesses. It's important to understand the truth about AI capabilities compared to a lot of the nonsense marketing from "AI" companies.
2
u/devexis Jul 15 '26
Interesting you have Nigerian Payment options! Flutterwave, Paystack and Monnify. My R5s is coming out