Dnsmasq / https-dns-proxy race condition prevents boot-time DNSSEC & DNS resolution

I found an interesting little problem. At first, I thought my RTC (Real Time Clock) was drifting enough during boot that DNSSEC wouldn’t work… but:

echo "System Time: $(date +'%Y-%m-%d %H:%M:%S.%N')"; echo "RTC Time:    $(hwclock --show --utc --format '%Y-%m-%d %H:%M:%S.%N' 2>/dev/null || hwclock --show --utc)"

System Time: 2026-08-22 06:58:46. RTC Time: Sat Aug 22 06:58:46 2026

ls -la /etc/rc.d/

lrwxrwxrwx    1 root     root            17 Aug 21 18:29 S19dnsmasq -> ../init.d/dnsmasq
lrwxrwxrwx    1 root     root            18 Jun 29 07:59 S19dropbear -> ../init.d/dropbear
lrwxrwxrwx    1 root     root            18 Jun 29 07:59 S19firewall -> ../init.d/firewall
lrwxrwxrwx    1 root     root            25 Jun 29 07:59 S20https-dns-proxy -> ../init.d/https-dns-proxy
lrwxrwxrwx    1 root     root            17 Jun 29 07:59 S20network -> ../init.d/network

Look at these lines:

lrwxrwxrwx    1 root     root            17 Aug 21 18:29 S19dnsmasq -> ../init.d/dnsmasq
lrwxrwxrwx    1 root     root            25 Jun 29 07:59 S20https-dns-proxy -> ../init.d/https-dns-proxy

dnsmasq starts up before https-dns-proxy, so there’s a race condition. Because there’s no DNS resolution, dnsmasq-full cannot do DNSSEC. Because dnsmasq-full cannot do DNSSEC, https-dns-proxy cannot do DNS resolution. Disable Network >> DNS >> DNSSEC tab >> DNSSEC, and it springs to life again… DNS resolution works, and you can then re-enable DNSSEC… at least until the next reboot.

dnsmasq is pulled in early, as soon as the interfaces initialize, and that can’t easily be changed (I attempted to do so, the setting was ignored)… so we’ll trigger a dnsmasq reload later in the boot, after https-dns-proxy is running, so dnsmasq isn’t in a race condition, and we’ll wait until we know DNS resolution is working (or the timer times out) before completing the boot.

We can’t make https-dns-proxy start up any earlier so it starts before dnsmasq, because it’d be starting up before the firewall. That’s a Bad Thing.

Issue:

vi /etc/init.d/apk-boot-sync

You’ll see a blank file. Press ‘i’ to enter edit mode, then type in the code below.

#!/bin/sh /etc/rc.common

START=99
boot() {
        sleep 5
        # Force dnsmasq to restart AFTER the https-dns-proxy is up
        /etc/init.d/dnsmasq restart

        # Wait until downloads.openwrt.org can resolve successfully
        local i=0
        while [ $i -lt 120 ]; do
                if nslookup downloads.openwrt.org > /dev/null 2>&1; then
                        break
                fi
                sleep 2
                i=$((i+1))
        done
}

When you’re done, press Esc to exit edit mode, then press :wq to save and exit.

Make the file executable:

chmod +x /etc/init.d/apk-boot-sync

If you’ve got dnsmasq-full installed, enable DNSSEC and ‘DNS check unsigned’, then reboot the router.

Now, it’s going to necessarily take longer to boot (up to 2 minutes more)… you’ll see the WiFi icon on your WiFi lan devices connect, but the LuCI interface login will clock for a bit as the router waits for DNS resoluion to be operational and the boot to complete.

But now that race condition doesn’t persist, so DNS resolution will take place as soon as log-in. No apk update Error Codes. No websites that don’t load.