I replaced my ISP’s ONT/router combo with my own XPON stick. The fiber still comes into my home as usual — but now it lands in a stick in SFU (Single Family Unit) bridge mode that passes the ISP VLAN straight through to OPNsense. No ISP router in the path, no double NAT. OPNsense owns the WAN VLAN interface and the DHCP client.
The stick is sold as an HSGQ XPON SFP stick — based on my research, basically an ODI DFP-34X-2C2 underneath, though I am not fully sure, so treat that as best-effort identification. I bought it from a local marketplace (Shopee, affiliate link). It lives in the SFP port of my managed switch, not directly in the router.
I say XPON for the hardware (it does both) and GPON for the line itself — my ISP runs GPON, and the ONU state below is GPON-specific.
Why#
- Public IP on my router. The ISP ONT does not do bridge mode, and I need the public address on OPNsense — not one NAT behind another box. The stick terminates the PON and bridges frames; one box owns the address and the routing policy.
- Observability. The ISP box exposes no API and no SNMP. Before, when the WAN went down I could not tell whether it was an ISP routing problem or my fiber line going dark. Now I can query ONU state straight off the stick and watch gateway status in my own monitoring. More on this below.
- Performance. The ISP unit is a cheap all-in-one — GPON, router, and Wi-Fi in one plastic box. It is out of the forwarding path entirely now.
- Control. My DHCP, DNS, firewall, and routing policy all live on a box I own — no double NAT, no mystery middlebox deciding what my edge does.
Path#
The full path looks like this:
How it is wired#
- The stick bridges the ISP VLAN. On the switch SFP port carrying the stick: tagged VLAN 1491 (the VLAN from the ISP), native untagged VLAN 90 (PVID) for stick management — web/SSH lives there. I keep management quarantined on its own VLAN on purpose — the stick firmware is outdated and I do not trust it on any other segment.
- OPNsense tags VLAN 1491 on its switch uplink and runs DHCP there (WAN VLAN interface). The same uplink gives OPNsense reachability to the stick management address on VLAN 90, which is what the watchdog SSHes to and what the ICMP probes target.
- The stick exposes ONU state (
O1…O5). O5 means ranging and OMCI are up and the line is operational. Anything below that means the optics are not ready and no amount of DHCP retrying will help.
Observability#
This is the part I like most. The stick is scraped by Strykar’s gpon-exporter straight into Prometheus, and Grafana renders the project’s GPON stats dashboard (47 panels, provisioned as-is with my datasource). ONU state, RX/TX power, bias current, SoC temperature, voltage, FEC corrections, BIP errors, alarms — the full optical picture, refreshed every minute.
That answers the question the ISP box never could: when the WAN goes down, is it routing (L3) or is the line dark (L1)? RX flatlined with LOS means fiber (L1). Optics fine but gateway down means ISP side (L3). Two different incidents, two different responses, no more guessing.

Alongside it, all in the same stack:
- Gateway status scraped off OPNsense into Prometheus, with alerts on gateway Offline and a recording rule for which WAN holds the default route. Grafana draws that as a timeline, so failovers show up as events, not mysteries.
- ICMP probes from OPNsense/Prometheus against the stick management address, so I know whether the stick itself is reachable independent of the ISP path.
- Watchdog mail from the recovery described below — every outage arrives with duration and repair count, not just “it is down”.
Issues#
The pattern that kept biting me: the link goes away and comes back, but the DHCP client never notices.
- Fiber cuts are common here. The line drops, the stick re-ranges, and when the light returns OPNsense is still holding a stale lease with no traffic flowing.
- Stick reboots. Same result: ISP-side lease expires while OPNsense keeps the old one.
- Remote restarts from the ISP side. Same symptom again: ONT restarts, lease is dead.
Either way the manual cure is Interfaces -> Overview -> Refresh on the WAN to pull a fresh lease. My ISP assigns a sticky address via DHCP, so the same address usually comes back. Manual and annoying, and exactly the kind of thing that breaks at the wrong hour.
And the obvious automation is its own trap: a dumb loop reconfiguring the WAN every minute would also fire while the ONT is still ranging — flapping the interface and spamming DHCP with zero chance of success. So there are two problems, not one: the refresh must happen without me, and it must not happen when the line is not ready.
The fix: only refresh on O5#
Both problems have the same answer: automate the Refresh I did by hand, but gate it on the optics. Notice the path is bad, confirm the line is really back (O5), then pull a fresh lease. It runs on OPNsense itself, no external dependency, every minute:
- Read the WAN IPv4 on the VLAN interface.
- Probe an external target (ISP gateway / reliable public IP) bound to that source address. If the probe succeeds, the path is fine.
- Track the stick management IP for a down-to-up rising edge (catches reboots).
- Ask the stick for ONU state over SSH (
diag gpon get onu-state).
Rules:
- If GPON is not O5: do not reconfigure. Queue one notification until the state changes. Reconfiguring a non-ready ONT only flaps.
- If GPON is O5 and probes fail for 2 consecutive minutes: reconfigure the WAN interface (
configctl interface reconfigure). Keep retrying about once a minute while it stays O5 and bad. A stick reboot edge plus bad path repairs immediately, still gated on O5.
State is small files under /var/db/ (fail count, GPON state, stick edge, alert flags) plus one log file. Cooldown is disabled on purpose — the O5 gate is the guardrail, and in my experience the ISP DHCP server has tolerated rapid client retries.
One stick quirk: its SSH needs legacy key exchange (diffie-hellman-group1-sha1 / ssh-rsa), and the password is fed via SSH_ASKPASS so it never lands in argv or logs. Credentials live on the box only, never in git.
Mail without spam#
OPNsense sends mail to me over Gmail SMTP. I split it:
- Generic async outbox: any service drops a
*.mailfile, a worker sends it once a minute. The watchdog never talks SMTP directly. - The watchdog enqueues on failure (first 2 mails immediate, then at most one combined mail every ~30 minutes while still down, counting suppressed attempts), on recovery with outage duration and repair count, and one-shot when GPON is not ready.
- Monit watches the check in failure-only mode and restarts core processes (DNS, time sync, SSH, tunnel daemon).
Monit success mail was noise. Failure-only plus explicit recovery mail was the right split.
What I kept#
- No repair without O5. That single check removed all the flap-reconfigure spam after reboots.
- Stick port with tagged 1491 and untagged 90 for management, quarantined on its own VLAN — the firmware is outdated and untrusted, so nothing else sits on that segment.
- OPNsense owns DHCP and policy. The stick stays dumb: terminate PON, bridge frames.
- Everything reversible from the UI: Cron toggle, Monit toggle, no hidden state.
If you run an XPON stick on a GPON line into OPNsense, the lessons travel well: keep the stick in bridge and let the router own the VLAN + DHCP, make the line observable separately from routing, and gate any DHCP repair on optical state.
