SimpliFi — RADIUS (FreeRADIUS) guide
Module M2. RADIUS lets any hotspot or PPPoE server that speaks RADIUS (Ruijie, Grandstream, Cambium, OpenWrt/CoovaChilli, pfSense, Huawei, Cisco, MikroTik in RADIUS mode, …) check every customer login against SimpliFi. SimpliFi writes the FreeRADIUS tables; a FreeRADIUS server reads them straight from the SimpliFi MySQL database and writes accounting back.
Customer ──WiFi──► Router / AP (NAS) ──UDP 1812/1813──► FreeRADIUS (VPS / on-site box)
│ │ MySQL (TCP 3306, TLS)
└──redirect──► SimpliFi portal ▼
(cPanel, HTTPS) ◄──── SimpliFi database (cPanel MySQL)
1. Why cPanel cannot host RADIUS itself
- RADIUS is UDP (1812 auth, 1813 accounting). Shared hosting only proxies HTTP(S) to PHP; you
cannot open UDP ports, run a daemon (
radiusd) or install packages (freeradius) without root. - PHP requests end after each page view — a RADIUS server must listen permanently.
So FreeRADIUS runs on any small Linux box: a $4–6/month VPS (1 vCPU / 512 MB–1 GB RAM is plenty for thousands of users), or an on-site mini PC / Raspberry Pi at the ISP. It talks to the cPanel database over the internet through cPanel's Remote MySQL.
2. What SimpliFi writes (tables)
All tables are created by the SimpliFi migrations (2026_10_19_000023_create_freeradius_tables
plus 2026_10_20_020000_add_freeradius_v3_columns_to_radacct_table). RADIUS usernames and NAS
addresses are globally unique (one FreeRADIUS serves every workspace); tenant_id is
bookkeeping only.
| Table | Written by | Content |
|---|---|---|
radcheck |
SimpliFi (RadiusRepository::upsertUser) |
Cleartext-Password :=, Expiration := (M d Y H:i:s, RADIUS server timezone), Simultaneous-Use := (package shared users), Max-All-Session := (optional total seconds) |
radreply |
SimpliFi | Session-Timeout, Mikrotik-Rate-Limit (up/down, e.g. 512k/2048k, plus burst fields for a burst package — see §2.1), WISPr-Bandwidth-Max-Up/Down (bit/s, always the plan rate), Mikrotik-Total-Limit(+-Gigawords) and ChilliSpot-Max-Total-Octets(+-Gigawords) for data caps, Framed-IP-Address, Acct-Interim-Interval (300 s) |
radusergroup |
SimpliFi (optional group) |
user → group |
nas |
SimpliFi (upsertNas when a device is saved in RADIUS mode) |
nasname = device NAS IP, shortname, secret, type = other, server |
radacct |
FreeRADIUS | accounting — SimpliFi reads usage, "online" and live clients from it |
radpostauth |
FreeRADIUS (optional) | login log |
Which reply attributes a device honours depends on the vendor (see docs/VENDORS.md): MikroTik
uses Mikrotik-*; CoovaChilli ChilliSpot-* + WISPr; Grandstream and Cambium honour
Session-Timeout and WISPr bandwidth; data caps are only enforced by MikroTik and CoovaChilli.
Revoking a session deletes the user's rows, so the next login is refused. A client that is still
connected keeps going until its Session-Timeout — sending a RADIUS Disconnect-Request (CoA,
UDP 3799) to the NAS is not possible from cPanel.
2.1 Burst and the fair-use policy (reduced speed)
Burst. A package with burst switched on renders the full RouterOS form into
Mikrotik-Rate-Limit, for example 512k/2048k 1024k/4096k 768k/3072k 8/8
(rate, burst rate, burst threshold, burst time in seconds; rx = customer upload,
tx = download). WISPr-Bandwidth-Max-Up/Down always keep the plan rate, because WISPr has
no burst fields. Only MikroTik-compatible gear reads the burst fields; everything else uses WISPr
and gets the plan rate (docs/VENDORS.md, note 5).
Fair use. When a session on a "continue at reduced speed" package reaches its allowance,
App\Services\Hotspot\FupEnforcer calls the driver, which asks
RadiusRepository::applyRateLimit() to rewrite the user's reply rows:
Mikrotik-Rate-LimitandWISPr-Bandwidth-Max-Up/Downbecome the reduced rate (no burst — the allowance that paid for it is spent);Mikrotik-Total-Limit(+-Gigawords) andChilliSpot-Max-Total-Octets(+-Gigawords) are deleted — the customer is meant to stay online, so the NAS must not cut them off at the cap it was given earlier;Cleartext-Password,Expiration,Simultaneous-Use,Session-Timeout,Framed-IP-AddressandAcct-Interim-Intervalare untouched: the session keeps its time.
Honest limitation. A NAS reads
radreplywhen it authenticates a user. Shared cPanel hosting cannot send a RADIUS Disconnect-Request / CoA (UDP 3799) to force a re-authentication, so the reduced rate takes effect at the customer's next re-authentication — immediately when their device reconnects, or whenSession-Timeoutelapses. Until then they continue at the full rate, which means a customer can overshoot the allowance for the rest of their current session. There is no way around this from cPanel; the ways around it are to run the FreeRADIUS box itself with a CoA sender, or to use MikroTik in RADIUS or Cloud Sync mode, where SimpliFi also kicks the live session over cloud sync and the re-authentication happens at once (≤ 20 s).
Keep Acct-Interim-Interval short enough (the default 300 s) that hotspot:usage notices the
allowance being crossed promptly: SimpliFi can only act on the counters accounting gives it.
2.2 PPPoE subscribers: usage from accounting
A PPPoE subscriber on a RADIUS-mode device is counted from radacct as well — the NAS writes
acctinputoctets (the customer's upload), acctoutputoctets (download) and acctsessiontime
for the PPPoE username exactly as it does for a hotspot login, and pppoe:usage (every
5 minutes) sums them.
- Accounting decides how fresh the numbers are. Between interim updates SimpliFi sees
nothing new, so
Acct-Interim-Interval(SimpliFi sends 300 s;simplifi.radius.interim_interval) is the real resolution of a PPPoE allowance. A NAS configured with a long interval — or with accounting switched off — can let a subscriber overshoot by whatever they download in between: with accounting off there is no PPPoE usage at all on a RADIUS-mode device, and fair use cannot fire. Enable accounting (/radius set accounting=yeson MikroTik) and keep the interval at a few minutes. - Counting per period.
usage()sums every accounting session ever stored for the username, so SimpliFi records the value it reads at the start of each allowance period as a baseline and only counts what grows on top of it (pppoe_accounts.meta.counters). Trimmingradaccttherefore never hands a customer free data: a sum that drops is treated as a counter restart, and the usage already counted stays on the account. - The reduced rate applies at the next re-authentication.
applyFupToPppoe()rewrites the same reply rows as above for the PPPoE username, and the live PPP session is deliberately not disconnected: a PPPoE plan is a monthly subscription, so forcing a re-dial just to apply a throttle is exactly what fair use is meant to avoid (and cPanel cannot send a CoA either). The customer continues at the full rate until their session re-dials. An ISP who wants it immediate should run the router in Cloud Sync or Direct API mode, where SimpliFi switches the PPP profile itself. - A "stop" package never disconnects a PPPoE subscriber mid-month; only "continue at reduced
speed" plans are enforced here (
docs/MIKROTIK.md§"PPPoE usage").
3. Install FreeRADIUS on Ubuntu (22.04 / 24.04) or Debian 12
The quick way, from a copy of this repository on the server:
sudo bash deploy/freeradius/install-ubuntu.sh
sudo nano /etc/freeradius/3.0/simplifi.env # database credentials (see §4)
sudo systemctl restart freeradius
sudo freeradius -XC # "Configuration appears to be OK"
What the script does (do it by hand if you prefer):
apt-get install freeradius freeradius-mysql freeradius-utils default-mysql-client- Copies
deploy/freeradius/mods-available/sqland…/sqlcounterto/etc/freeradius/3.0/mods-available/and links them intomods-enabled/.sqlreads the database credentials from the environment ($ENV{SIMPLIFI_DB_*}), setsread_clients = yes/client_table = "nas", and includes the stock MySQLqueries.conf. - Copies
deploy/freeradius/sites-available/simplifi(auth 1812 + acct 1813; PAP, CHAP, MS-CHAP;-sql,expiration,noresetcounter; accounting + session via SQL) and replaces the stockdefault/inner-tunnelsites. - Installs
/etc/freeradius/3.0/simplifi.env(fromsimplifi.env.example, mode 640, groupfreerad) and the systemd drop-infreeradius.service.d/simplifi.conf(EnvironmentFile=). - Installs
simplifi-nas-reload.sh+/etc/cron.d/simplifi-radius: FreeRADIUS loads clients from thenastable only at start-up, so the script checks the table every minute and restarts FreeRADIUS when a device was added or changed. Alternative without restarts:sites-available/simplifi-dynamic-clients(per-packet SQL client lookup; instructions inside). - Opens UDP 1812/1813 in
ufwif present. Also open them in your VPS provider's firewall.
On-site box (no public IP): run the same install on a mini PC in the ISP's network; routers then use its LAN IP as RADIUS server and the box reaches cPanel MySQL outbound.
4. cPanel: allow the RADIUS server into MySQL
- cPanel › MySQL Databases — create a dedicated user (e.g.
cpuser_radius) with a long random password and add it to the SimpliFi database. GrantSELECT, INSERT, UPDATE, DELETE(FreeRADIUS needsINSERT/UPDATEonradacct,INSERTonradpostauth,SELECTon the rest; cPanel grants privileges per database, not per table). - cPanel › Remote MySQL — add the VPS's public IP (only that IP; never
%). - The MySQL host in
simplifi.envis the cPanel server's hostname or shared IP, notlocalhost. Port 3306 must be reachable from the VPS (some hosts open it only on request). - Prefer TLS: uncomment the
tls { … }block inmods-available/sqlwhen the host supports it. - Time zones: FreeRADIUS writes accounting times with MySQL's
NOW()/FROM_UNIXTIME(); keep the MySQL server, the FreeRADIUS box andRADIUS_TIMEZONE(SimpliFi.env, default UTC — Ghana is UTC+0) aligned soExpirationand "online" detection are correct.
Security note: this MySQL user can technically read the whole SimpliFi database. Keep the password secret, restrict Remote MySQL to the RADIUS server's IP, and keep the VPS patched.
5. SimpliFi settings
In the SimpliFi .env on cPanel:
RADIUS_SERVER_HOST=radius.example.com # shown to ISPs on every RADIUS device page
RADIUS_AUTH_PORT=1812
RADIUS_ACCT_PORT=1813
RADIUS_INTERIM_INTERVAL=300 # Acct-Interim-Interval sent to devices
RADIUS_TIMEZONE=UTC # timezone of the FreeRADIUS server (Expiration)
RADIUS_STALE_MINUTES=30 # open sessions without updates are "offline" after this
A platform setting radius.server_host (if the platform admin sets one) overrides
RADIUS_SERVER_HOST. Then php artisan config:cache.
6. Add a RADIUS device in SimpliFi
Routers & Controllers › Add Device → pick the vendor → connection type RADIUS → enter the
NAS IP (the public IP the router's RADIUS packets come from; for an on-site FreeRADIUS, the
router's LAN IP) and optionally a NAS identifier. Leave the secret blank to generate one. Saving
writes the nas row; the device page shows the server, ports and (on click) the shared secret, plus
vendor-specific steps. Two devices cannot share a NAS IP — FreeRADIUS identifies clients by source IP
(behind one NAT, use one device entry for the site).
7. Testing
# On the RADIUS box (localhost client "testing123" from the stock clients.conf):
radtest <username> <password> 127.0.0.1 0 testing123
# → "Received Access-Accept … Session-Timeout = …, Mikrotik-Rate-Limit = …"
# Debug mode (stop the service first) shows every SQL query and reply:
sudo systemctl stop freeradius && sudo freeradius -X
# From another machine, as a NAS registered in SimpliFi:
echo "User-Name=<username>,User-Password=<password>" | radclient -x <radius-ip>:1812 auth <nas-secret>
# Accounting round trip:
echo "User-Name=<username>,Acct-Status-Type=Start,Acct-Session-Id=test1,NAS-IP-Address=<nas-ip>" \
| radclient -x <radius-ip>:1813 acct <nas-secret>
Get a username/password by selling a voucher or creating a manual session in SimpliFi. "Test
connection" on a RADIUS device page re-registers the NAS and shows when the last accounting packet
from that NAS arrived. Access-Reject usually means: wrong secret (check the NAS IP matches the
packet source), the user expired (Expiration), or Simultaneous-Use reached (stale radacct rows
— the NAS should send Accounting-On after reboot).
8. Operations
- Back up
radacctwith the rest of the database; prune old rows (e.g. > 12 months) periodically. Pruning is safe for PPPoE allowances: SimpliFi keeps what it already counted (§2.2). journalctl -u freeradiusfor errors;freeradius -XCafter any config change.- Upgrading FreeRADIUS keeps
/etc/freeradius/3.0/*(answer "keep your version" for our files).