Stubby stops working after a while

I upgraded from my old snapshot build (r28349) to the latest releast last week, and since then my stubby seems to be stopping to work after an uncertain amount of time.
At first I thought it was related to my ISP disconnect every 24hours, but then it happened during the day with no disconnect.
What happens is that DNS queries aren't resolved anymore and I get a timeout when using my router (dnsmasq) as resolver. Restarting stubby solves the issue.

I tried to debug it using log_level = 7 (DEBUG) when running stubby, but there are no messages containing any error. Only alternating opening and closing of connections. In cases when the issue occurs (not contained in the log excerpt below), there was just a connection opening, but no closing for a long time.
Actually I'm surprised that setting the log level to DEBUG doesn't show every request stubby receives.

Logs and System Information
Sat Jun 13 16:09:02 2026 daemon.err stubby[13202]: [14:09:02.646412] STUBBY: 1.0.0.1                                  : Conn closed: TLS - Resps=    23, Timeouts  =     0, Curr_auth =Success, Keepalive(ms)=  9000
Sat Jun 13 16:09:02 2026 daemon.err stubby[13202]: [14:09:02.646468] STUBBY: 1.0.0.1                                  : Upstream   : TLS - Resps= 16197, Timeouts  =     0, Best_auth =Success
Sat Jun 13 16:09:02 2026 daemon.err stubby[13202]: [14:09:02.646494] STUBBY: 1.0.0.1                                  : Upstream   : TLS - Conns=   329, Conn_fails=     0, Conn_shuts=     26, Backoffs     =     0
Sat Jun 13 16:09:02 2026 daemon.err stubby[13202]: [14:09:02.646903] STUBBY: 2606:4700:4700::1111                     : Conn closed: TLS - Resps=    26, Timeouts  =     0, Curr_auth =Success, Keepalive(ms)=  9000
Sat Jun 13 16:09:02 2026 daemon.err stubby[13202]: [14:09:02.646941] STUBBY: 2606:4700:4700::1111                     : Upstream   : TLS - Resps= 16404, Timeouts  =     0, Best_auth =Success
Sat Jun 13 16:09:02 2026 daemon.err stubby[13202]: [14:09:02.646967] STUBBY: 2606:4700:4700::1111                     : Upstream   : TLS - Conns=   329, Conn_fails=     0, Conn_shuts=     25, Backoffs     =     0
Sat Jun 13 16:09:02 2026 daemon.err stubby[13202]: [14:09:02.647194] STUBBY: 2606:4700:4700::1001                     : Conn closed: TLS - Resps=    19, Timeouts  =     0, Curr_auth =Success, Keepalive(ms)=  9000
Sat Jun 13 16:09:02 2026 daemon.err stubby[13202]: [14:09:02.647228] STUBBY: 2606:4700:4700::1001                     : Upstream   : TLS - Resps= 16301, Timeouts  =     0, Best_auth =Success
Sat Jun 13 16:09:02 2026 daemon.err stubby[13202]: [14:09:02.647254] STUBBY: 2606:4700:4700::1001                     : Upstream   : TLS - Conns=   330, Conn_fails=     0, Conn_shuts=     25, Backoffs     =     0
Sat Jun 13 16:09:02 2026 daemon.err stubby[13202]: [14:09:02.647502] STUBBY: 1.1.1.1                                  : Conn closed: TLS - Resps=    24, Timeouts  =     0, Curr_auth =Success, Keepalive(ms)=  9000
Sat Jun 13 16:09:02 2026 daemon.err stubby[13202]: [14:09:02.647536] STUBBY: 1.1.1.1                                  : Upstream   : TLS - Resps= 15894, Timeouts  =     0, Best_auth =Success
Sat Jun 13 16:09:02 2026 daemon.err stubby[13202]: [14:09:02.647561] STUBBY: 1.1.1.1                                  : Upstream   : TLS - Conns=   329, Conn_fails=     0, Conn_shuts=     25, Backoffs     =     0
Sat Jun 13 16:09:11 2026 daemon.err stubby[13202]: [14:09:11.991804] STUBBY: 1.0.0.1                                  : Conn opened: TLS - Strict Profile
Sat Jun 13 16:09:11 2026 daemon.err stubby[13202]: [14:09:11.992130] STUBBY: 2606:4700:4700::1111                     : Conn opened: TLS - Strict Profile
Sat Jun 13 16:09:11 2026 daemon.err stubby[13202]: [14:09:11.992508] STUBBY: 2606:4700:4700::1001                     : Conn opened: TLS - Strict Profile
Sat Jun 13 16:09:11 2026 daemon.err stubby[13202]: [14:09:11.993075] STUBBY: 1.1.1.1                                  : Conn opened: TLS - Strict Profile
Sat Jun 13 16:09:12 2026 daemon.err stubby[13202]: [14:09:12.028749] STUBBY: 1.0.0.1                                  : Verify passed : TLS
Sat Jun 13 16:09:12 2026 daemon.err stubby[13202]: [14:09:12.041549] STUBBY: 2606:4700:4700::1001                     : Verify passed : TLS
Sat Jun 13 16:09:12 2026 daemon.err stubby[13202]: [14:09:12.053467] STUBBY: 2606:4700:4700::1111                     : Verify passed : TLS
Sat Jun 13 16:09:12 2026 daemon.err stubby[13202]: [14:09:12.063583] STUBBY: 1.1.1.1                                  : Verify passed : TLS
Sat Jun 13 16:10:13 2026 daemon.err stubby[13202]: [14:10:13.613804] STUBBY: 1.1.1.1                                  : Conn closed: TLS - Resps=   889, Timeouts  =     0, Curr_auth =Success, Keepalive(ms)=  9000
Sat Jun 13 16:10:13 2026 daemon.err stubby[13202]: [14:10:13.613857] STUBBY: 1.1.1.1                                  : Upstream   : TLS - Resps= 16783, Timeouts  =     0, Best_auth =Success
Sat Jun 13 16:10:13 2026 daemon.err stubby[13202]: [14:10:13.613883] STUBBY: 1.1.1.1                                  : Upstream   : TLS - Conns=   330, Conn_fails=     0, Conn_shuts=     25, Backoffs     =     0
Sat Jun 13 16:10:13 2026 daemon.err stubby[13202]: [14:10:13.614304] STUBBY: 1.0.0.1                                  : Conn closed: TLS - Resps=   980, Timeouts  =     0, Curr_auth =Success, Keepalive(ms)=  9000
Sat Jun 13 16:10:13 2026 daemon.err stubby[13202]: [14:10:13.614338] STUBBY: 1.0.0.1                                  : Upstream   : TLS - Resps= 17177, Timeouts  =     0, Best_auth =Success
Sat Jun 13 16:10:13 2026 daemon.err stubby[13202]: [14:10:13.614363] STUBBY: 1.0.0.1                                  : Upstream   : TLS - Conns=   330, Conn_fails=     0, Conn_shuts=     26, Backoffs     =     0
Sat Jun 13 16:10:13 2026 daemon.err stubby[13202]: [14:10:13.614591] STUBBY: 2606:4700:4700::1111                     : Conn closed: TLS - Resps=   976, Timeouts  =     0, Curr_auth =Success, Keepalive(ms)=  9000
Sat Jun 13 16:10:13 2026 daemon.err stubby[13202]: [14:10:13.614625] STUBBY: 2606:4700:4700::1111                     : Upstream   : TLS - Resps= 17380, Timeouts  =     0, Best_auth =Success
Sat Jun 13 16:10:13 2026 daemon.err stubby[13202]: [14:10:13.614651] STUBBY: 2606:4700:4700::1111                     : Upstream   : TLS - Conns=   330, Conn_fails=     0, Conn_shuts=     25, Backoffs     =     0
Sat Jun 13 16:10:13 2026 daemon.err stubby[13202]: [14:10:13.614888] STUBBY: 2606:4700:4700::1001                     : Conn closed: TLS - Resps=  1032, Timeouts  =     0, Curr_auth =Success, Keepalive(ms)=  9000
Sat Jun 13 16:10:13 2026 daemon.err stubby[13202]: [14:10:13.614921] STUBBY: 2606:4700:4700::1001                     : Upstream   : TLS - Resps= 17333, Timeouts  =     0, Best_auth =Success
Sat Jun 13 16:10:13 2026 daemon.err stubby[13202]: [14:10:13.614946] STUBBY: 2606:4700:4700::1001                     : Upstream   : TLS - Conns=   331, Conn_fails=     0, Conn_shuts=     25, Backoffs     =     0
Sat Jun 13 16:10:15 2026 daemon.err stubby[13202]: [14:10:15.241245] STUBBY: 1.1.1.1                                  : Conn opened: TLS - Strict Profile
Sat Jun 13 16:10:15 2026 daemon.err stubby[13202]: [14:10:15.241682] STUBBY: 1.0.0.1                                  : Conn opened: TLS - Strict Profile
Sat Jun 13 16:10:15 2026 daemon.err stubby[13202]: [14:10:15.242088] STUBBY: 2606:4700:4700::1111                     : Conn opened: TLS - Strict Profile
Sat Jun 13 16:10:15 2026 daemon.err stubby[13202]: [14:10:15.292518] STUBBY: 1.1.1.1                                  : Verify passed : TLS
Sat Jun 13 16:10:15 2026 daemon.err stubby[13202]: [14:10:15.307708] STUBBY: 1.0.0.1                                  : Verify passed : TLS
Sat Jun 13 16:10:15 2026 daemon.err stubby[13202]: [14:10:15.318288] STUBBY: 2606:4700:4700::1111                     : Verify passed : TLS
Sat Jun 13 16:10:15 2026 daemon.err stubby[13202]: [14:10:15.392616] STUBBY: 2606:4700:4700::1001                     : Conn opened: TLS - Strict Profile
Sat Jun 13 16:10:15 2026 daemon.err stubby[13202]: [14:10:15.438416] STUBBY: 2606:4700:4700::1001                     : Verify passed : TLS
ubus call system board

{
        "kernel": "6.12.87",
        "hostname": "router",
        "system": "ARMv8 Processor rev 0",
        "model": "Banana Pi BPI-R4 (2x SFP+)",
        "board_name": "bananapi,bpi-r4",
        "rootfs_type": "squashfs",
        "release": {
                "distribution": "OpenWrt",
                "version": "25.12.4",
                "firmware_url": "https://downloads.openwrt.org/",
                "revision": "r32933-4ccb782af7",
                "target": "mediatek/filogic",
                "description": "OpenWrt 25.12.4 r32933-4ccb782af7",
                "builddate": "1778712129"
        }
}
uci export dhcp
package dhcp

config dnsmasq
        option domainneeded '1'
        option boguspriv '1'
        option filterwin2k '0'
        option localise_queries '1'
        option rebind_protection '1'
        option rebind_localhost '1'
        option local '/<home domain>/'
        option domain '<home domain>'
        option expandhosts '1'
        option nonegcache '0'
        option cachesize '1000'
        option authoritative '1'
        option readethers '1'
        option leasefile '/tmp/dhcp.leases'
        option resolvfile '/tmp/resolv.conf.d/resolv.conf.auto'
        option nonwildcard '1'
        option localservice '1'
        option ednspacket_max '1232'
        option filter_aaaa '0'
        option filter_a '0'
        list interface 'private_lan'
        list interface 'guest_lan'
        list interface 'iot_lan'
        list interface 'vpn'
        option noresolv '1'
        option proxydnssec '1'
        list server '127.0.0.1#5453'
        list server '/pool.ntp.org/1.1.1.1'

config odhcpd 'odhcpd'
        option maindhcp '0'
        option leasefile '/tmp/odhcpd.leases'
        option leasetrigger '/usr/sbin/odhcpd-update'
        option loglevel '4'
        option piodir '/tmp/odhcpd-piodir'
        option hostsdir '/tmp/hosts'

config dhcp 'private'
        option interface 'private_lan'
        option start '100'
        option limit '50'
        option leasetime '24h'
        option dhcpv4 'server'
        option dhcpv6 'server'
        option ra 'server'
        option ra_slaac '1'
        option ra_flags 'managed-config other-config'

config dhcp 'guest'
        option interface 'guest_lan'
        option start '100'
        option limit '50'
        option leasetime '1h'
        option dhcpv4 'server'
        option dhcpv6 'server'
        option ra 'server'
        option ra_slaac '1'
        option ra_flags 'managed-config other-config'

config dhcp 'iot'
        option interface 'iot_lan'
        option start '100'
        option limit '50'
        option leasetime '24h'
        option dhcpv4 'server'
uci export stubby
package stubby

config stubby 'global'
        option manual '0'
        list dns_transport 'GETDNS_TRANSPORT_TLS'
        option tls_authentication '1'
        option tls_query_padding_blocksize '128'
        option appdata_dir '/var/lib/stubby'
        option edns_client_subnet_private '1'
        option idle_timeout '9000'
        option round_robin_upstreams '1'
        list listen_address '127.0.0.1@5453'
        list listen_address '0::1@5453'
        list trigger 'fiber_wan'
        list trigger 'tethering_wan'
        option dnssec_return_status '1'
        option log_level '7'

config resolver
        option address '1.1.1.1'
        option tls_auth_name 'cloudflare-dns.com'

config resolver
        option address '1.0.0.1'
        option tls_auth_name 'cloudflare-dns.com'

config resolver
        option address '2606:4700:4700::1111'
        option tls_auth_name 'cloudflare-dns.com'

config resolver
        option address '2606:4700:4700::1001'
        option tls_auth_name 'cloudflare-dns.com'

While 9000 ms idle_timeout should be low enough for Cloudflare, the stubby log still shows conn_shuts, which means Cloudflare timed you out, experiment with 8500, 8000, etc. to see if it helps.

Stubby is suffering from a silent hang, likely related to TLS persistent connection management. Possibly some incompatibility with the SNAPSHOT branch.

I would also split the test once it hangs: query stubby directly on its listening port, then query dnsmasq. For example, if stubby listens on 127.0.0.1#5453, compare drill @127.0.0.1 -p 5453 openwrt.org with a normal lookup through dnsmasq. If direct stubby is already stuck, then the idle_timeout/upstream angle is likely. If stubby answers but dnsmasq does not, look at dnsmasq forwarding or cache state instead. That saves a bit of guessing.

One extra thing I’d isolate is IPv6 vs IPv4 upstreams, because your config tries both Cloudflare families. When it hangs, temporarily comment the 2606:4700 entries (or the IPv4 ones) and restart stubby, just long enough to see if only one family gets stuck. If only IPv6 fails, it may be the WAN/ISP path rather than stubby itself.