System Admin here, looking for a permanent fix for the following issue (without manual system file edits). Since networking isn't my primary focus, I've used AI to help document the technical details, which I've pasted below.
## Environment
- **OPNsense version:** 26.1.6_2-amd64
- **FreeBSD:** 14.3-RELEASE-p10
- **OpenSSL:** 3.0.20
- **ISP:** Optus NBN Australia
- **WAN:** DHCP with 900 second lease (renews every 7.5 minutes)
- **Unbound:** DNS over TLS (DoT) to 1.1.1.1:853 and 9.9.9.9:853
- **Downstream:** Two Pi-hole instances forwarding to Unbound at 192.168.9.254:53
- Pi-hole Core v6.4.1
- FTL v6.6
- Web Interface v6.5
---
## The Problem
Both Pi-hole instances intermittently log the following warning:
```
WARNING Connection error (192.168.9.254#53): TCP connection failed while
receiving payload length from upstream (Connection prematurely closed by
remote server)
```
The errors occur with precise regularity — every 7.5 minutes — matching exactly the Optus DHCP lease renewal interval.
---
## Investigation
### Step 1 — Ruled out Unbound restarts
Checked the resolver log for Unbound stop/start events during the error windows:
```
grep "start of service\|service stopped" /var/log/resolver/latest.log
```
Unbound was **not restarting** during the error windows. The TCP connection was being closed without Unbound being killed.
---
### Step 2 — Found the DHCP correlation
Cross-referencing the Pi-hole FTL log against the OPNsense system log revealed a consistent pattern:
**System log:**
```
2026-05-09T15:33:59 dhclient-script: Reason RENEW on igc0 executing
2026-05-09T15:33:59 dhclient-script: Creating resolv.conf
2026-05-09T15:41:29 dhclient-script: Reason RENEW on igc0 executing
2026-05-09T15:41:29 dhclient-script: Creating resolv.conf
2026-05-09T15:48:59 dhclient-script: Reason RENEW on igc0 executing
2026-05-09T15:48:59 dhclient-script: Creating resolv.conf
```
**Pi-hole FTL log:**
```
2026-05-09 15:38:15 WARNING Connection error (192.168.9.254#53): TCP connection failed
2026-05-09 15:41:46 WARNING Connection error (192.168.9.254#53): TCP connection failed
2026-05-09 15:50:07 WARNING Connection error (192.168.9.254#53): TCP connection failed
```
Every single TCP error correlates with a preceding DHCP RENEW. No RENEW = no error.
---
### Step 3 — Confirmed IP is not changing
The DHCP lease file confirms the WAN IP is stable across renewals:
```
option dhcp-lease-time 900;
```
The cached IP file also confirms no change:
```
cat /tmp/igc0_oldip
175.32.32.48
```
The renewal is a simple lease extension — the IP, gateway, and DNS servers do not change.
---
### Step 4 — Traced the dhclient script
Reading `/usr/local/opnsense/scripts/interfaces/dhclient-script`, the `BOUND|RENEW|REBIND|REBOOT` block contains the following:
```sh
changes="no"
if [ -n "$old_ip_address" ]; then
if [ "$old_ip_address" != "$new_ip_address" ]; then
delete_old_address
delete_old_routes
changes="yes"
fi
fi
if [ "$reason" = BOUND ] || \
[ "$reason" = REBOOT ] || \
[ -z "$old_ip_address" ] || \
[ "$old_ip_address" != "$new_ip_address" ]; then
add_new_address
add_new_routes
changes="yes"
fi
add_new_resolv_conf # <-- called unconditionally on every RENEW
if [ "$changes" = "yes" ] ; then
/usr/local/sbin/configctl -d interface newip $interface force
fi
```
`add_new_resolv_conf` is called **unconditionally** on every RENEW, regardless of whether anything has actually changed. When the IP has not changed, `changes` remains `"no"` and `configctl interface newip` is correctly skipped — but `add_new_resolv_conf` still runs every time.
---
### Step 5 — Traced what add_new_resolv_conf does
```sh
add_new_resolv_conf()
{
$LOGGER "Creating resolv.conf"
ARGS="-i ${interface} -4nd"
for nameserver in ${new_domain_name_servers}; do
ARGS="${ARGS} -a ${nameserver}"
done
/usr/local/sbin/ifctl ${ARGS}
/usr/local/sbin/ifctl -i ${interface} -4sd ${new_domain_name:+"-a ${new_domain_name}"}
return 0
}
```
This calls `ifctl` to update the interface nameserver state on every RENEW. Even though the nameservers haven't changed, this briefly disrupts Unbound's **outgoing DNS over TLS connections** to 1.1.1.1:853 and 9.9.9.9:853.
---
### Step 6 — Connected to Pi-hole FTL behaviour
Pi-hole FTL v6 holds TCP connections open for reuse as a performance feature. During the DoT disruption window caused by `add_new_resolv_conf`, Unbound closes the inbound TCP connection from Pi-hole. FTL doesn't detect the closure until it tries to reuse the connection — at which point it logs the warning.
The Pi-hole dnsmasq log confirms the connection fails and then immediately succeeds on retry with a new connection:
```
11:44:13 dnsmasq[928]: TCP connection failed: Connection prematurely closed by remote server
11:44:13 dnsmasq[928]: config error is REFUSED (EDE: network error)
11:44:13 dnsmasq[929]: forwarded rumt-zh.com to 192.168.9.254
11:44:13 dnsmasq[929]: reply rumt-zh.com is 113.240.76.236
```
DNS resolution continues normally — this is a reliability and logging noise issue rather than a complete outage.
---
## Things Tried That Did Not Fix It
- `edns-tcp-keepalive: yes` on Unbound — Pi-hole FTL does not send the EDNS keepalive option in requests so Unbound never includes it in responses
- `tcp-idle-timeout: 120000` on Unbound — does not address DoT disruption triggered by RENEW
- Outgoing Network Interfaces → All — stopped Unbound from fully restarting on RENEW but `add_new_resolv_conf` still disrupts DoT
- `serve-expired-client-timeout` adjustments — no effect
- `edns-packet-max=1232` on Pi-hole — reduces TCP usage but does not eliminate it
---
## Suggested Fix
Wrap `add_new_resolv_conf` with a condition so it only runs when the IP actually changes, or on first `BOUND`/`REBOOT` — not on every `RENEW` when nothing has changed:
```sh
# Current (runs unconditionally on every RENEW):
add_new_resolv_conf
# Suggested (only runs when something has actually changed):
if [ "$changes" = "yes" ] || [ "$reason" = "BOUND" ] || [ "$reason" = "REBOOT" ]; then
add_new_resolv_conf
fi
```
This prevents `ifctl` from unnecessarily updating the interface nameserver state on RENEW events where the IP, gateway, and nameservers are identical to what was already configured.
---
## Note
I have not applied this fix directly as the file `/usr/local/opnsense/scripts/interfaces/dhclient-script` is owned by an OPNsense package and would be overwritten on upgrades. Ideally this would be addressed in the script itself in a future release.
Happy to provide any additional logs or information if helpful.
Today I enabled Unbound on OPNsense and configured it to connect to Quad9's upstream servers with IP addresses 9.9.9.9 & 149.112.112.112. Below is an extract from my notes.
I also run Unbound on an internal system. It's been reconfigured to forward upstream to OPNsense.
Reading your post I then tweaked the settings in Unbound on OpenBSD and set "forward-tcp-upstream: yes". Using pftop on OPNsense I can watch the TCP DNS queries from OpenBSD. I also ran tcpdump of port 53 to OPNsense on OpenBSD.
I'm with iiNet who default to having a lease time of 1800 seconds. Their allowable lease time range is 300 to 3600 seconds. You could experiment with your Optus service and see what ranges you can obtain.
I dropped my DHCP lease time down to 300 seconds and monitored the DNS queries. My observations aren't conclusive but it appears OPNsense gracefully closes connections periodically and not necessarily when the DHCP lease is renewed.
Is there something preventing you upgrading your system to 26.1.7_3?
OPNsense DNS Configurations:
QuoteQuad9 already performs DNSSEC validation. Disable it in OPNsense:
1. Services -> Unbound DNS -> Advanced -> General Settings
1.1. Aggressive NSEC: un-ticked
1.2. Strict QNAME Minimisation: un-ticked
1.3. Apply the settings
Set up forwarders for all zones to upstream DNS servers:
2. Services -> Unbound DNS -> DNS over TLS -> Custom forwarding
2.1. Create a custom forwarding entry by clicking on "+"
2.2. Leave the Domain field empty
2.3. Server IP: 9.9.9.9
2.4. Server Port: 853
2.5. Verify CN: dns.quad9.net
2.6. Description: Quad9 Threat-blocking with DNSSEC
2.7. Click on Save
2.8. Create another custom forwarding entry
2.9. Leave the Domain field empty
2.10. Server IP: 149.112.112.112
2.11. Server Port: 853
2.12. Verify CN: dns.quad9.net
2.13. Description: Quad9 Threat-blocking with DNSSEC
2.14. Click on Save
Disable System Nameservers:
3. Services -> Unbound DNS -> DNS over TLS - > Use System Nameservers
3.1. Ensure this option is un-ticked.
N.B. This setting is also carried over to Query Forwarding.
3.2. Apply the settings
Enable Unbound:
4. Services -> Unbound DNS -> General
4.1. Enable Unbound: ticked
4.2. Listen Port: 53
4.3. Apply the settings
I've attached the image of my System -> Settings -> General. Note, I've disabled the option to Allow DNS server list to be overridden by DHCP/PPP on WAN.
Also attached is an image of my DHCP client configuration settings. The Lease Requirements is where I've applied settings. My DHCP requests only asks for the subnet mask and default gateway - that's all.
Hopefully this helps you.