Short answer: a production-grade FreeRADIUS setup for an ISP needs four things working together, not just an installed daemon: a NAS client registry with per-device shared secrets and an explicit CoA port, a PPPoE/IPoE authentication path that resolves subscriber identity correctly for both connection types, a CoA/disconnect layer that translates the same billing event into the right vendor-specific attribute for MikroTik, Nokia, Cisco, or Huawei, and accounting hygiene that tells a genuinely stale session apart from one that is still connected. FreeRADIUS itself is free; building and maintaining that surrounding layer is the real cost most ISPs underestimate.
Register NAS clients correctly — this is where most setups break first
Every router or OLT that will send RADIUS traffic needs to be registered as a NAS client with a stable management IP (or hostname), a shared secret for Access-Request/Access-Accept, and — separately — a CoA secret and an explicit CoA port for Change-of-Authorization/Disconnect traffic. Treating the CoA secret and port as "the same as auth" is a common misconfiguration: RFC 3576 CoA traffic is a distinct UDP stream, and a NAS that listens for Access-Requests on 1812 may expect CoA on a completely different port (3799 is the common default, but it varies by vendor and firmware). A NAS with no CoA port configured should fail loudly in logs rather than silently drop every CoA/disconnect attempt — that single missing field is the most common reason ISPs report "CoA isn't working" months after go-live.
Rotate shared secrets periodically and never reuse one secret across unrelated locations or tenants — a leaked secret from one site should not expose every other site's RADIUS traffic. Also record the NAS vendor explicitly (MikroTik, Cisco, Huawei, Juniper, Ubiquiti, Nokia BNG, or other) rather than assuming one vendor fits the whole network; the vendor field is what drives attribute translation later.
| NAS vendor | Auth path | CoA speed attribute | Practical note |
|---|---|---|---|
| MikroTik (RouterOS) | PPPoE, Hotspot, IPoE/DHCP | Mikrotik-Rate-Limit (VSA 14988) | Also supports a RouterOS API fallback disconnect (PPP active, hotspot active/host, DHCP lease) when the CoA packet itself gets no ACK |
| Nokia BNG | PPPoE, IPoE | Alc-Subscriber-QoS-Override (VSA 6527) | Has no Mikrotik-Rate-Limit support — sending it NAKs the CoA; needs separate upload/download attribute instances |
| Cisco / Huawei / Juniper | PPPoE, IPoE | Standard RFC attributes (varies by platform) | CoA/Disconnect support and attribute naming differ by IOS-XE/VRP/Junos release — verify in a lab before relying on parity with MikroTik |
| Ubiquiti | PPPoE, Hotspot | Vendor-specific, limited CoA maturity | Common on smaller WISP deployments; confirm CoA support per firmware before depending on live throttling |
PPPoE authentication: the identity is explicit, the edge cases are not
PPPoE sends an explicit username and password in every Access-Request, so subscriber lookup is direct. The real edge cases are everywhere else: a subscriber migrating from a legacy portal may need their RADIUS password "learned" from the first successful NAS login rather than pre-provisioned (a sticky-password mode some ISPs enable per-NAS during migration); a mistyped username with trailing whitespace should be normalized rather than silently rejected; and a subscriber who is already connected and re-authenticates from the same device should never be counted twice against their simultaneous-session limit — only a genuinely different device competes for that slot. Session limits are enforced by counting live (non-stale) sessions for the subscriber's identity set and comparing against the plan's allowed concurrent-session count, then returned to the NAS as the Simultaneous-Use reply attribute.
Password comparisons should use a constant-time comparison, not a simple string equality check — a timing side-channel on every login attempt is a real (if slow) way to leak a RADIUS password one character at a time.
IPoE and MAC-based authentication: no login prompt means the network has to know who is who
IPoE removes the PPP login screen entirely — the subscriber's device gets an IP via DHCP and RADIUS has to infer identity from Calling-Station-Id (the device MAC) or, if even that is absent, from Framed-IP-Address for static assignments. This makes MAC binding effectively mandatory for IPoE: a subscriber configured for IPoE that presents no MAC at all should be rejected outright rather than silently authenticated against the wrong record. For a subscriber's first-ever device, the correct behavior is auto-bind on first sight — up to the subscriber's session limit — rather than requiring a staff member to manually pre-register every MAC before a customer can get online; beyond that limit, a new device should be rejected and logged for staff review, not silently granted a slot. An existing MAC binding belonging to a different, still-active subscriber should never be silently reassigned, since that is how two customers end up billed for one connection or vice versa.
A subtlety that trips up naive IPoE implementations: a device re-authenticating on its own existing session (a DHCP lease renewal, a hotspot MAC probe) is not a new concurrent session and should never be counted against the session limit or treated as a disconnect-reconnect event — only a session that has gone genuinely stale should be closed.
CoA and disconnect: translate the event, don't assume the ACK
Change of Authorization (RFC 3576/5176) is what makes a billing platform feel instant instead of batch-processed: a payment clears, a plan upgrades, a fair-usage cap is crossed — and the live session's speed changes or the session is disconnected without the subscriber reconnecting. The mechanics matter more than they first appear. A CoA-Request carries a Message-Authenticator (HMAC-MD5 over the packet with the shared secret) and expects an explicit CoA-ACK (RADIUS code 44) or Disconnect-ACK (code 41) in response; a NAS that is unreachable, firewalled, or simply slow can leave the request unanswered, and assuming success on a timeout is a direct path to revenue leakage — a subscriber who should have lost access stays online until something else notices. The safer pattern is: send, wait for the real ACK, and if none arrives, queue a retry rather than mark the action as done.
The attribute translation problem is the other half: the same "set this subscriber to 10 Mbps" event has to become Mikrotik-Rate-Limit for a MikroTik NAS and Alc-Subscriber-QoS-Override (as two separate VSA instances, one per direction) for a Nokia BNG — sending the MikroTik attribute to Nokia hardware gets the packet NAK'd outright, not just ignored. A deployment with more than one NAS vendor needs this translation to happen per-NAS at send time, not baked into a single hardcoded attribute name. For MikroTik specifically, a RouterOS API fallback (walking PPP active sessions, hotspot active/host tables, and DHCP leases) is worth having as a second path when the CoA packet itself gets no response — useful when a firewall blocks the CoA port but the management API is still reachable. For the deeper mechanics of which billing events should trigger CoA and how to log them safely, see our FreeRADIUS CoA guide for plan changes and renewals.
Accounting hygiene: a session with no stop time is not automatically a live session
If a NAS loses power or crashes mid-session, it cannot send an Accounting-Stop — the radacct row simply stays open forever unless something closes it. The reliable way to tell a stale session from a live one is the NAS's own interim-accounting interval: each NAS reports usage updates on a fixed cadence, and a session that has gone silent for longer than that interval (plus a short grace period, since networks jitter) should be treated as dead — closed with a synthetic stop time derived from its last known update, and followed by a disconnect to clear any lingering state on the NAS side. Flag it too early and you risk closing a session that is merely between interim updates; flag it too late and a crashed router's "ghost" sessions quietly inflate your concurrent-session counts and block real reconnects from a subscriber who is actually trying to come back online. Detailed usage, dispute, and NAS-level reporting built on top of clean accounting data is covered in our RADIUS accounting reports guide.
Wiring FUP throttling through RADIUS instead of around it
Fair-usage throttling should change the same reply attribute that normal plan speed uses, not a parallel system. When a subscriber crosses their fair-usage cap, the Access-Accept (on their next login) and any live CoA should both read from the plan's throttled speed instead of its normal speed, using the exact same vendor-translation logic described above — a MikroTik subscriber gets a lower Mikrotik-Rate-Limit string, a Nokia subscriber gets a lower Alc-Subscriber-QoS-Override pair. Keeping FUP and normal plan speed as two branches of the same code path (rather than two separate systems that both try to set bandwidth) is what prevents the two from drifting out of sync — see our FUP management for ISPs page and MikroTik queue management guide for the bandwidth-control side of this.
Monitoring AAA uptime, not just subscriber-level auth failures
A NAS that silently stops talking to RADIUS is often invisible until a flood of subscriber complaints arrives. Two independent signals are more reliable than one: does the NAS currently have any open accounting sessions, and does a direct UDP probe to its RADIUS/CoA port get a response. Either being positive counts the NAS as alive. A graduated state machine avoids false alarms from one missed check — moving from healthy to a warning state after roughly 10 minutes of silence, to a down state after 30 minutes, and only declaring a NAS fully dead after it stays down for a full week, at which point it stops cluttering an active-device dashboard. Recovery from any down state should be logged with the actual downtime duration, which is what an uptime SLA report for a given NAS and date range is built from.
Free FreeRADIUS vs a managed AAA/billing layer: an honest comparison
FreeRADIUS the daemon is genuinely free and open source — there is no license cost, and nothing above is impossible to build yourself. What the comparison below is really about is engineering time and ongoing operational ownership, not a feature FreeRADIUS is missing.
| Capability | Bare FreeRADIUS (self-hosted) | Billing-integrated AAA layer |
|---|---|---|
| Core AAA protocol (auth/acct/CoA) | Free, included | Free, included |
| Multi-vendor CoA attribute translation | You write and maintain per-vendor logic | Built in — MikroTik, Nokia, and others translated automatically per NAS |
| CoA retry queue (no silent "assume success") | You build a job/queue system for it | Built in — unanswered CoA/disconnect is queued and retried automatically |
| Subscriber MAC auto-bind for IPoE | Custom SQL triggers or external script | Built in — bind, release, and reclaim tied to subscriber lifecycle |
| Stale-session cleanup tied to per-NAS interim interval | A cron script you write and tune yourself | Built in — per-NAS interim interval drives the stale threshold automatically |
| NAS health monitoring with graduated states | External monitoring tool, bolted on separately | Built in — warning/down/dead states with SLA reporting per NAS |
| Ties auth/CoA decisions to plan, wallet, invoice state | Requires custom SQL views or external glue code | Built in — one subscriber record drives RADIUS, billing, and support together |
| Reseller/partner NAS access boundaries | Not a FreeRADIUS concept — custom policy layer needed | Built in for multi-partner/LCO hierarchies |
If your ISP has in-house engineering time and a single NAS vendor, running bare FreeRADIUS with hand-written SQL and cron scripts is a legitimate choice — plenty of small ISPs do exactly this. The tradeoff shows up as you scale past one NAS vendor, add partner/reseller hierarchies, or need billing state (wallet balance, plan expiry, FUP) to drive live network policy automatically instead of through a nightly batch job.
Setup checklist
1. Register each NAS with a unique shared secret, a separate CoA secret, and an explicit CoA port — verify the port with a UDP reachability check before going live.
2. Decide PPPoE vs IPoE per NAS/rollout and confirm your NAS vendor's CoA attribute and ACK behavior for that connection type in a lab, not just on paper.
3. Map each subscriber's plan to RADIUS reply attributes (speed, session limit, IP/pool) and confirm the Access-Accept carries the correct vendor-specific rate-limit attribute.
4. Wire FUP throttling to the same attribute path as normal plan speed, not a separate system.
5. Build (or adopt) a CoA retry queue that only treats an explicit ACK as success.
6. Set a stale-session threshold per NAS based on its actual interim-accounting interval, with a grace window, and verify it against real crash/reconnect scenarios.
7. Monitor NAS heartbeat with graduated states — do not alert on a single missed check, and do not silently mark a core router dead after one bad probe.
NowaCRM's AAA layer is built on exactly this operational model: NAS clients register with per-device CoA secrets and ports, PPPoE and IPoE both resolve subscriber identity (username, bound MAC, or Framed-IP) through the same lookup path, CoA/disconnect translate automatically per NAS vendor — MikroTik's Mikrotik-Rate-Limit, Nokia BNG's Alc-Subscriber-QoS-Override — with a retry queue for anything that doesn't ACK, stale sessions close against each NAS's own interim interval, and NAS health escalates through warning/down/dead states with SLA reporting instead of a single flapping alert. See our RADIUS billing for ISPs and MikroTik RADIUS billing pages for how this ties into plan management and invoicing, and ISP subscriber management for how MAC bindings and device limits sit inside the subscriber record.