If you’ve ever had your internet go down in the middle of a video call, you know the pain. I decided to fix that by adding LTE failover to my OPNsense router using a Ubiquiti U5G Max. The U5G Max exposes its cellular WAN through a GRE tunnel, which means you don’t need a UniFi gateway to use it — any router that supports GRE can take advantage of it.
This wasn’t entirely straightforward. OPNsense refuses to assign an IP to a GRE tunnel interface through the GUI, there are MTU issues that silently break web browsing, and there’s a system setting that’s disabled by default without which failover simply won’t trigger. This guide covers everything I learned getting it working — the final, tested configuration with all the gotchas addressed.

What you’ll end up with
- Automatic failover to LTE within ~6 seconds of your primary WAN going down
- Automatic failback when the primary recovers
- All VLANs covered, no per-rule configuration needed
- Survives reboots
- No UniFi gateway required
Before You Start
Why priority-based failover (not gateway groups)
I initially tried policy-based routing with gateway groups on individual firewall rules. It works, but it has an annoying side effect: it routes all traffic through the gateway group, including traffic destined for local devices. This breaks inter-VLAN communication because even local traffic gets pushed toward the WAN.
You can work around it with “local traffic exemption” rules, but it’s messy and error-prone. The cleaner approach — and what this guide uses — is system-level failover via gateway priorities. You assign each gateway a priority number, enable gateway switching, and OPNsense handles the rest. All firewall rules stay set to “Default” gateway, local traffic routes normally, and every VLAN gets failover automatically.
The tradeoff is that it’s all-or-nothing — every VLAN fails over together. If you need selective failover per VLAN, you’ll need the policy-based approach with local traffic exemption rules.
Prerequisites
- U5G Max adopted into your UniFi controller (managed by your Cloud Key, Dream Machine, or whatever runs your UniFi Network Application). Firmware should be up to date.
- U5G Max on your management VLAN — the same VLAN where your other UniFi infrastructure lives (Cloud Key, switches, access points). The U5G needs to communicate with the controller for adoption and management, so it belongs on this VLAN alongside the rest of your UniFi gear.
- U5G Max has a fixed/static IP on the management VLAN (DHCP reservation or static assignment). This is critical — the GRE tunnel endpoints are defined by IP address. If the U5G’s IP changes, the tunnel breaks silently.
- An active cellular data plan — physical SIM tends to be more reliable than eSIM for initial activation.
- SSH access to both the U5G Max and OPNsense.
In my setup, the U5G Max is at 192.168.1.15 and OPNsense is at 192.168.1.1, both on the management VLAN. Your IPs will be different — the specific addresses don’t matter as long as both devices are on the same VLAN and the U5G has a fixed address.
Throughout this guide, I’ll use my actual IPs in examples. Replace them with yours wherever they appear.
Step 1: Gather the Tunnel Parameters from the U5G Max
SSH into the U5G Max and inspect the GRE tunnel it automatically creates:
ssh [email protected]
ip a show gre1
You’ll see something like:
14: gre1@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1476
link/gre 192.168.1.15 peer 192.168.1.1
inet 100.127.125.128/31 scope global gre1
valid_lft forever preferred_lft forever
inet6 fe80::5efe:c0a8:10f/64 scope link
valid_lft forever preferred_lft forever
There are two layers of addresses here:
| Layer | U5G Max side | OPNsense side | Purpose |
|---|---|---|---|
| Outer tunnel | 192.168.1.15 | 192.168.1.1 | Real VLAN IPs — how the devices reach each other |
| Inner tunnel | 100.127.125.128 | 100.127.125.129 | Point-to-point addresses inside the GRE tunnel |
The inner addresses (100.127.125.128/31) appear to be hardcoded on the U5G Max. Your OPNsense side will always be 100.127.125.129.
The outer peer address must match OPNsense’s actual IP on the management VLAN. If they don’t match, the tunnel won’t come up. If you only see a link-local fe80:: IPv6 address with no global inet6, skip IPv6 — it’s not needed for failover.
Step 2: Create the GRE Tunnel in OPNsense
Navigate to Interfaces → Other Types → GRE and click +.
| Field | Value |
|---|---|
| Local address | Your management VLAN interface |
| Remote address | 192.168.1.15 (U5G’s fixed MGMT VLAN IP) |
| Tunnel local address | 100.127.125.129 |
| Tunnel remote address | 100.127.125.128 |
| Tunnel netmask / prefix | 31 |
| Description | U5G_GRE |
Click Save. This creates the gre0 device.
Step 3: Assign the Interface and Apply the Tunnel IP
This step has a workaround baked into it because of an OPNsense limitation.
Assign the interface
- Go to Interfaces → Assignments
- Find
gre0in the dropdown and click Add - Click on the new interface and configure it:
- Enable: âś…
- Description:
WAN_LTE - IPv4 Configuration Type:
None - IPv6 Configuration Type:
None
- Save and Apply changes
You might be wondering why we’re setting IPv4 to “None” on something that clearly needs an IP. If you try “Static IPv4” and enter 100.127.125.129/31, OPNsense will reject it with “Cannot assign an IP configuration type to a tunnel interface”. This is a known limitation — tunnel interfaces don’t support GUI-based IP assignment.
Apply the IP manually
SSH into OPNsense (or use Interfaces → Diagnostics → Shell) and run:
ifconfig gre0 inet 100.127.125.129 100.127.125.128 netmask 255.255.255.254
Verify with ifconfig gre0 — you should see inet 100.127.125.129 --> 100.127.125.128 netmask 0xfffffffe.
This assignment is not persistent and will vanish on reboot. The next step makes it permanent.
Step 4: Make the IP Survive Reboots
Create a startup script:
vi /usr/local/etc/rc.syshook.d/start/99-gre_ip.sh
Contents:
#!/bin/sh
sleep 10
/sbin/ifconfig gre0 inet 100.127.125.129 100.127.125.128 netmask 255.255.255.254
Make it executable:
chmod +x /usr/local/etc/rc.syshook.d/start/99-gre_ip.sh
The sleep 10 gives the GRE interface time to initialize before the IP is applied. If failover doesn’t work after a reboot, try increasing this to 15 or 20.
Step 5: Configure the Gateways
This step configures both the LTE gateway and your primary WAN gateway for monitoring and failover. Both need proper configuration — if either one is misconfigured, failover won’t work.
Create the LTE gateway
Go to System → Gateways → Configuration, click +, and enable advanced mode.
| Field | Value |
|---|---|
| Name | WAN_LTE_TUNNELV4 |
| Interface | WAN_LTE |
| Address Family | IPv4 |
| Priority | 200 |
| IP Address | 100.127.125.128 |
| Upstream Gateway | ❌ Unchecked |
| Far Gateway | âś… Checked |
| Disable Gateway Monitoring | ❌ Unchecked |
| Failover States | âś… Checked |
| Failback States | âś… Checked |
| Monitor IP | 8.8.8.8 |
| Probe Interval | 1 |
| Time Period | 6 |
| Loss Interval | 2 |
Save and Apply.
About “Far Gateway”: Normally a gateway must be on the same subnet as its interface. Since we assigned the tunnel IP manually via CLI, OPNsense doesn’t know that 100.127.125.128 is directly reachable. “Far Gateway” tells OPNsense to trust you and add a host route to that address regardless. This is standard for tunnel interfaces and VPN setups — you wouldn’t need it on a regular WAN.
Configure the primary WAN gateway
Edit your existing primary WAN gateway with these settings:
| Field | Value |
|---|---|
| Priority | 100 |
| Upstream Gateway | âś… Checked |
| Disable Gateway Monitoring | ❌ Unchecked |
| Failover States | âś… Checked |
| Failback States | âś… Checked |
| Monitor IP | 8.8.4.4 |
| Probe Interval | 1 |
| Time Period | 6 |
| Loss Interval | 2 |
Save and Apply.
How these settings work together
Upstream Gateway marks a gateway as a candidate for the default route. Only check this on the primary WAN — it tells OPNsense “this is the preferred path, use it whenever it’s healthy.” Leaving it unchecked on the LTE gateway means LTE will only be selected when the primary is down. Priority adds a second layer of preference: 100 (primary) beats 200 (LTE). Monitor IPs must be different per gateway — I use 8.8.8.8 for LTE and 8.8.4.4 for primary, both Google DNS but different endpoints. The Time Period must be at least 2 Ă— (Probe Interval + Loss Interval), so with 1 and 2, the minimum is 6. Failover/Failback States flush active connections when a gateway transitions, forcing clients to immediately reconnect through the new path instead of hanging for 30-60 seconds.
Step 6: Enable Gateway Switching
By default, OPNsense only picks a default gateway at startup or when an interface is physically connected/disconnected. If your ISP goes down but the physical link stays up — which is the most common failure scenario — OPNsense will not switch to the LTE gateway, even though dpinger sees the primary as offline.
- Go to System → Settings → General
- Check Allow default gateway switching
- Click Save
This tells OPNsense to actively re-evaluate and switch the default route whenever gateway monitoring detects a change. Without it, the Failover States and Failback States options on your gateways also do nothing — they depend on gateway switching being active.
Why this matters especially for PPPoE connections
My primary WAN is a PPPoE connection, and this is where gateway switching really earns its keep. PPPoE adds a session layer on top of the physical link — your router authenticates with the ISP and establishes a PPP session over Ethernet. The critical thing is that a PPPoE session can die while the physical Ethernet link stays perfectly healthy. The fiber or DSL sync remains active, the cable is plugged in, the link light is green, but the PPPoE session has dropped and you have no internet.
Without gateway switching, OPNsense only reacts to physical link changes, so it would never notice a PPPoE session drop — dpinger would detect the loss, but OPNsense wouldn’t act on it. With gateway switching enabled, the monitoring-based re-evaluation catches this.
With a non-PPPoE WAN (plain DHCP or static IP behind a modem/router), the failure modes are slightly different. If the upstream device dies or the cable is unplugged, the physical interface goes down and OPNsense detects that without needing gateway switching. However, there are still scenarios where the link stays up but connectivity is lost — an upstream router crash where the Ethernet handshake is maintained, an ISP routing failure, or a CGNAT issue. Gateway switching covers these too. So regardless of your WAN type, enabling it is the more robust choice.
Note on the “Upstream Gateway” flag: Gateway switching and the upstream flag work together. When gateway switching is enabled, OPNsense constantly re-evaluates which gateway should be the default. The upstream flag on your primary WAN tells it “this is the preferred candidate” — without it, OPNsense may not know which gateway to prefer and can select the wrong one. In my testing, failover also works without gateway switching enabled (and without the upstream flag), because OPNsense still re-evaluates gateways when a physical interface goes up or down. However, that only covers cable-pull scenarios. With gateway switching enabled and the upstream flag set on the primary WAN, you get proper failover for both physical link loss and ISP outages where the link stays up — which is the more robust configuration.
Step 7: Configure Outbound NAT
Without NAT on the GRE interface, traffic through the LTE tunnel carries your internal 192.168.x.x addresses. The cellular carrier will drop these.
- Go to Firewall → NAT → Outbound
- Switch to Hybrid outbound NAT rule generation and Save
- Click + to add a manual rule:
| Field | Value |
|---|---|
| Interface | WAN_LTE |
| Protocol | any |
| Source address | LOCAL_VLANS net |
| Destination address | any |
| Translation / target | Interface address |
| Description | NAT out via LTE |
- Save and Apply
About LOCAL_VLANS net: This is an OPNsense interface group I created that bundles all my internal VLAN interfaces (desk, servers, IoT, guest) under one name. Using it as a source means any VLAN in the group gets covered by a single rule. To create one, go to Interfaces → Other Types → Group. If you don’t use interface groups, substitute your LAN subnet or select RFC1918 networks to cover all private ranges.
Hybrid mode preserves your existing automatic NAT rules while letting you add manual ones. Your primary WAN NAT is unaffected.
Step 8: Fix the MTU
This one is sneaky. After everything was configured, I found that ping worked, chat apps worked, but web pages wouldn’t load. GRE encapsulation adds overhead that reduces the effective packet size, and large packets get silently dropped.
You can confirm the problem from the OPNsense shell:
# Works:
ping -S 100.127.125.129 -s 1400 8.8.8.8
# Fails with 100% packet loss:
ping -S 100.127.125.129 -s 1476 8.8.8.8
Two fixes are needed:
Set the interface MTU
Go to Interfaces → [WAN_LTE], set MTU to 1400 and MSS to 1360, then Save and Apply.
Add a normalization rule
Go to Firewall → Settings → Normalization, click Add:
| Field | Value |
|---|---|
| Interface | WAN_LTE |
| Direction | any |
| Protocol | tcp |
| Max MSS | 1360 |
Save and Apply. The value 1360 provides headroom for cellular carrier encapsulation on top of the GRE overhead. If you still have issues, try 1300.
Step 9: Set Up DNS for Failover
If you run a local DNS server like AdGuard, you might find that the tunnel works during failover but pages still don’t load. The reason: your DNS server’s upstream queries are routed through the primary WAN, which is down.
Set your DNS server’s upstream resolver to 9.9.9.9 (Quad9). Public DNS routes through whichever WAN is currently active, so resolution works regardless of which gateway is handling traffic.
Alternatively, configure OPNsense’s Unbound DNS (Services → Unbound DNS → General) to use both WAN and WAN_LTE as outgoing interfaces.
Step 10: Test Everything
Tunnel connectivity
From the OPNsense shell, test each hop in sequence:
# 1. U5G reachable on the MGMT VLAN?
ping 192.168.1.15
# 2. Tunnel peer reachable?
ping -S 100.127.125.129 100.127.125.128
# 3. Internet reachable through the tunnel?
ping -S 100.127.125.129 8.8.8.8
If #1 fails, the U5G isn’t reachable on the VLAN. If #2 fails, the GRE tunnel or IP assignment is wrong. If #3 fails, there’s a NAT or upstream routing issue.
Failover
- Disconnect your primary WAN cable
- Wait about 6–10 seconds
- Browse to whatismyip.com — you should see your cellular carrier’s IP
- Reconnect the cable — traffic should return to the primary WAN automatically
A note on this test: Unplugging a cable triggers a physical link state change, which OPNsense detects regardless of whether gateway switching is enabled. This means a cable-pull test can pass even if your configuration is incomplete. It’s a good first test, but it only covers one failure mode. In a real outage — especially with PPPoE — the cable stays plugged in and the physical link stays up while connectivity is lost. To be confident failover actually works in production, make sure gateway switching is enabled (Step 6). You can simulate a more realistic outage by disabling the WAN interface in OPNsense’s GUI or by logging into your modem and dropping the PPPoE session, rather than pulling a cable.
Reboot persistence
- Reboot OPNsense
- SSH in and run
ifconfig gre0— confirm100.127.125.129 --> 100.127.125.128is present - Repeat the failover test
If the GRE IP is missing after reboot, increase the sleep value in the startup script.
Optional: Pushover Notifications on Gateway Switch
If you want to know the moment your connection fails over to LTE or fails back to the primary WAN, you can hook into OPNsense’s gateway monitoring system to send Pushover notifications. No service or cron job needed — it runs automatically.
How the hook system works
OPNsense’s failover detection is a chain of components that are already running:
- dpinger runs continuously in the background as a daemon, pinging your monitor IPs (
8.8.4.4and8.8.8.8) on each gateway - When dpinger detects a state change (gateway goes down or comes back up), it triggers OPNsense’s gateway watcher
- The gateway watcher processes the event — updates routes, reloads firewall filters — and then automatically executes every script in
/usr/local/etc/rc.syshook.d/monitor/in numerical order - The core script
10-dpingerhandles the route switch. Any script you add with a higher number runs after it
So the chain is: dpinger → gateway watcher → 10-dpinger (core, handles the route switch) → 50-gw-notify.sh (yours, sends the notification).
There’s nothing to enable, no daemon to start. As long as your script is in that directory and has the executable flag set, OPNsense calls it automatically on every gateway alarm state change. This is the same rc.syshook mechanism used by the GRE persistence script earlier — just a different hook directory (monitor/ instead of start/).
The 50- prefix is a convention: OPNsense core uses 10- or 20- for its own scripts, plugins use 50-. It just controls execution order. You could use any number higher than 10.
Setup
Create a Pushover application at pushover.net if you haven’t already. You need an Application Token and your User Key.
Create the script on OPNsense:
vi /usr/local/etc/rc.syshook.d/monitor/50-gw-notify.sh
- Paste the following script, replacing
YOUR_APP_TOKENandYOUR_USER_KEYwith your Pushover credentials:
#!/bin/sh
# ============================================================================
# Gateway Failover/Failback Pushover Notification Script for OPNsense
# ============================================================================
#
# Sends a Pushover notification whenever OPNsense switches the active
# default gateway (failover or failback).
#
# Location: /usr/local/etc/rc.syshook.d/monitor/50-gw-notify.sh
# Dependencies: curl (included in OPNsense base)
# ============================================================================
# --- Pushover credentials ---------------------------------------------------
PUSHOVER_APP_TOKEN="YOUR_APP_TOKEN"
PUSHOVER_USER_KEY="YOUR_USER_KEY"
# --- Configuration ----------------------------------------------------------
STATE_FILE="/tmp/gw_notify_last_active.state"
LOCK_FILE="/tmp/gw_notify.lock"
LOG_TAG="gw-notify"
# --- Functions --------------------------------------------------------------
log_msg() {
/usr/bin/logger -t "${LOG_TAG}" "$1"
}
send_pushover() {
_title="$1"
_message="$2"
_priority="${3:-0}"
/usr/local/bin/curl -s \
--form-string "token=${PUSHOVER_APP_TOKEN}" \
--form-string "user=${PUSHOVER_USER_KEY}" \
--form-string "title=${_title}" \
--form-string "message=${_message}" \
--form-string "priority=${_priority}" \
--form-string "sound=siren" \
--max-time 10 \
https://api.pushover.net/1/messages.json > /dev/null 2>&1
if [ $? -eq 0 ]; then
log_msg "Pushover notification sent: ${_title}"
else
log_msg "ERROR: Failed to send Pushover notification"
fi
}
get_active_gateway() {
# Parse the default IPv4 route to find the active gateway
_gw_ip=$(/usr/bin/netstat -rn -f inet | /usr/bin/awk '/^default/ { print $2; exit }')
_gw_if=$(/usr/bin/netstat -rn -f inet | /usr/bin/awk '/^default/ { print $NF; exit }')
echo "${_gw_ip}|${_gw_if}"
}
resolve_gateway_name() {
_gw_ip="$1"
_gw_if="$2"
# Try to match via OPNsense's pluginctl
_status=$(/usr/local/sbin/pluginctl -d gateway 2>/dev/null)
if [ -n "${_status}" ]; then
_name=$(echo "${_status}" | /usr/bin/sed -n \
"s/.*\"name\":\"\([^\"]*\)\".*\"address\":\"${_gw_ip}\".*/\1/p" \
| head -1)
if [ -n "${_name}" ]; then
echo "${_name}"
return
fi
fi
# Fallback: configctl
_status2=$(/usr/local/sbin/configctl interface list gateways json 2>/dev/null)
if [ -n "${_status2}" ]; then
_name2=$(echo "${_status2}" | /usr/bin/grep -B5 "\"gateway\":\"${_gw_ip}\"" \
| /usr/bin/sed -n 's/.*"name":"\([^"]*\)".*/\1/p' | head -1)
if [ -n "${_name2}" ]; then
echo "${_name2}"
return
fi
fi
echo "Unknown (${_gw_if})"
}
# --- Main -------------------------------------------------------------------
# Lock to prevent duplicate notifications from rapid alarm events
if [ -f "${LOCK_FILE}" ]; then
_lock_age=$(( $(date +%s) - $(stat -f %m "${LOCK_FILE}" 2>/dev/null || echo 0) ))
if [ "${_lock_age}" -lt 5 ]; then
log_msg "Skipping duplicate trigger (lock age: ${_lock_age}s)"
exit 0
fi
fi
touch "${LOCK_FILE}"
# Wait for the routing table to settle after the gateway switch
sleep 3
# Get current active default gateway
_active=$(get_active_gateway)
_active_ip=$(echo "${_active}" | cut -d'|' -f1)
_active_if=$(echo "${_active}" | cut -d'|' -f2)
if [ -z "${_active_ip}" ] || [ "${_active_ip}" = "" ]; then
log_msg "WARNING: No default gateway found in routing table"
send_pushover \
"OPNsense: No Default Gateway" \
"No default IPv4 route found in the routing table. All WAN connectivity may be down.
Time: $(date '+%Y-%m-%d %H:%M:%S')" \
"1"
rm -f "${LOCK_FILE}"
exit 1
fi
_gw_name=$(resolve_gateway_name "${_active_ip}" "${_active_if}")
# Check if the active gateway actually changed
_last_active=""
if [ -f "${STATE_FILE}" ]; then
_last_active=$(cat "${STATE_FILE}")
fi
_current_state="${_gw_name}|${_active_ip}|${_active_if}"
if [ "${_current_state}" = "${_last_active}" ]; then
log_msg "Gateway unchanged (${_gw_name}), no notification sent"
rm -f "${LOCK_FILE}"
exit 0
fi
echo "${_current_state}" > "${STATE_FILE}"
# Determine event type based on gateway name
# Adjust these patterns to match your gateway names
_event_type="Gateway Changed"
_priority=0
case "${_gw_name}" in
*WAN_PPPOE*|*WAN_DHCP*)
_event_type="Failback to Primary WAN"
_priority=0
;;
*LTE*|*TUNNEL*)
_event_type="Failover to LTE Backup"
_priority=1
;;
esac
_old_name="(first run)"
if [ -n "${_last_active}" ]; then
_old_name=$(echo "${_last_active}" | cut -d'|' -f1)
fi
_message="Active gateway: ${_gw_name}
Gateway IP: ${_active_ip}
Interface: ${_active_if}
Previous: ${_old_name}
Time: $(date '+%Y-%m-%d %H:%M:%S')"
log_msg "${_event_type}: now using ${_gw_name} (${_active_ip} on ${_active_if})"
send_pushover "OPNsense: ${_event_type}" "${_message}" "${_priority}"
rm -f "${LOCK_FILE}"
exit 0
- Make it executable:
chmod +x /usr/local/etc/rc.syshook.d/monitor/50-gw-notify.sh
What the script does
The script tracks the current active gateway and only notifies you when it actually changes — not on every dpinger alarm flap. Here’s the flow:
- A lock file (
/tmp/gw_notify.lock) prevents duplicate notifications if multiple alarm events fire in quick succession (within 5 seconds of each other) - A 3-second delay lets the routing table settle after the gateway switch before the script checks anything
- It reads the current default route from
netstat -rnto find the active gateway IP and interface - It resolves the IP to a friendly name (like
WAN_PPPOEorWAN_LTE_TUNNELV4) using OPNsense’spluginctl - It compares the current gateway to the state file (
/tmp/gw_notify_last_active.state) — if the gateway hasn’t changed, it exits silently - If the gateway has changed, it sends a Pushover notification and updates the state file
The notification includes the active gateway name, its IP, the interface, the previous gateway, and a timestamp. Failovers to LTE are sent at high priority (which bypasses quiet hours on your phone), while failbacks to the primary WAN are sent at normal priority. If no default route is found at all (both paths down), it sends a high-priority warning.
Note: The state file lives in
/tmpand won’t survive a reboot. This is intentional — the first gateway alarm after a reboot always sends a notification, which confirms which gateway came up as default.
Testing
Run it manually to verify Pushover delivery:
/usr/local/etc/rc.syshook.d/monitor/50-gw-notify.sh
You should receive a notification showing your current active gateway (WAN_PPPOE if everything is normal).
Run it a second time to verify deduplication:
/usr/local/etc/rc.syshook.d/monitor/50-gw-notify.sh
This time you should not get a notification. Check the log to confirm:
clog -f /var/log/system.log | grep gw-notify
You should see “Gateway unchanged, no notification sent.”
Reset the state and run again to simulate a reboot scenario:
rm /tmp/gw_notify_last_active.state
/usr/local/etc/rc.syshook.d/monitor/50-gw-notify.sh
You should get another notification.
Trigger a real failover to test the full chain:
Unplug your primary WAN cable and wait about 10 seconds. You should receive a notification saying it failed over to LTE. Plug the cable back in and wait — you should get a second notification confirming failback to the primary WAN. Then check the logs to confirm the script was triggered automatically by the monitor hook (not just by your manual runs):
clog -f /var/log/system.log | grep gw-notify
If you only see entries from your manual runs and nothing from the cable-pull test, check that the script is executable:
ls -la /usr/local/etc/rc.syshook.d/monitor/50-gw-notify.sh
Look for the x flag in the permissions. If it’s missing, run chmod +x again.
Final Configuration Summary
| Component | Setting | Value |
|---|---|---|
| U5G Max | VLAN | Management VLAN (with Cloud Key and other UniFi devices) |
| U5G Max | IP | Fixed/static (e.g., 192.168.1.15) |
| GRE Tunnel | Remote address | U5G’s fixed MGMT VLAN IP |
| GRE Tunnel | Tunnel local | 100.127.125.129 |
| GRE Tunnel | Tunnel remote | 100.127.125.128 |
| GRE Tunnel | Netmask | /31 |
| Interface | Name | WAN_LTE |
| Interface | IPv4 Type | None (manual via CLI) |
| Interface | MTU | 1400 |
| Interface | MSS | 1360 |
| Gateway (LTE) | IP | 100.127.125.128 |
| Gateway (LTE) | Priority | 200 |
| Gateway (LTE) | Upstream Gateway | ❌ |
| Gateway (LTE) | Monitor IP | 8.8.8.8 |
| Gateway (LTE) | Far Gateway | âś… |
| Gateway (Primary) | Type | PPPoE (also works with DHCP/static) |
| Gateway (Primary) | Priority | 100 |
| Gateway (Primary) | Upstream Gateway | âś… |
| Gateway (Primary) | Monitor IP | 8.8.4.4 |
| Both Gateways | Probe Interval | 1 |
| Both Gateways | Time Period | 6 |
| Both Gateways | Loss Interval | 2 |
| Both Gateways | Failover States | âś… |
| Both Gateways | Failback States | âś… |
| Gateway Switching | System → Settings → General | ✅ Enabled |
| Outbound NAT | Mode | Hybrid |
| Outbound NAT | Source | LOCAL_VLANS net |
| Normalization | Max MSS | 1360 |
| DNS Upstream | Resolver | 9.9.9.9 |
| Persistence | Script | /usr/local/etc/rc.syshook.d/start/99-gre_ip.sh |
Troubleshooting
GRE tunnel won’t come up
- Is the U5G reachable?
ping 192.168.1.15 - Does OPNsense’s MGMT VLAN IP match the peer address in
ip a show gre1on the U5G? - Does the U5G still have its fixed IP, or did it change via DHCP?
- Are the outer and inner addresses correct in the GRE config?
Gateway shows offline
- Is the GRE IP assigned? Run
ifconfig gre0 - Is “Disable Gateway Monitoring” unchecked?
- Is the Monitor IP set to
8.8.8.8? - Is “Far Gateway” checked? Without it, OPNsense can’t route to the tunnel gateway
- Try
service dpinger restart - Last resort: reboot and immediately re-apply the GRE IP
Websites don’t load over the tunnel (but ping works)
- Almost certainly an MTU issue. Test:
ping -S 100.127.125.129 -s 1400 8.8.8.8 - Apply MTU
1400and MSS1360as described in Step 8 - Still failing? Lower MSS to
1300
Failover doesn’t trigger
- Check gateway switching first — System → Settings → General, “Allow default gateway switching” must be checked. Without it, OPNsense only switches gateways on boot or physical link changes, not when dpinger detects an outage.
- Is “Upstream Gateway” checked on the primary WAN and unchecked on the LTE? Without the upstream flag, enabling gateway switching can cause OPNsense to select the wrong gateway as the default.
- Is monitoring enabled on both gateways? Both must show RTT and Loss values (not “~” or empty) on the Gateways page
- Are priorities correct? Primary:
100, LTE:200 - Verify routing:
netstat -rn | grep default
DNS fails during failover
- Set your DNS upstream to
9.9.9.9, or configure Unbound to use both WAN interfaces
Slow reconnection after failover
- Enable Failover States and Failback States on both gateways
- Ensure gateway switching is enabled (required for state flushing to trigger)
- Time Period minimum: 2 Ă— (Probe Interval + Loss Interval)
GRE IP missing after reboot
- Script exists?
cat /usr/local/etc/rc.syshook.d/start/99-gre_ip.sh - Script executable?
ls -la /usr/local/etc/rc.syshook.d/start/99-gre_ip.sh - Try increasing the
sleepvalue
Local network breaks with policy-based failover
- This happens when you assign a gateway group on firewall rules — local traffic gets routed to WAN. Add a local exemption rule above the failover rule (source: VLAN net, destination:
LOCAL_VLANS net, gateway: Default), or switch to the system-level priority approach used in this guide.
That’s it. About 30 minutes of configuration once you know what you’re doing, and you get seamless LTE failover that kicks in within seconds and recovers automatically. No UniFi gateway required.