AdguardTeam/AdGuardHome · critical

creating dns server: %w

Error message

creating dns server: %w

What it means

AdGuard Home could not create its DNS server instance (dnsforward.Server created via dnsServerNew with the TLS manager, HTTP registrar, and clients storage). Creation fails when the supplied parameters are invalid, e.g. bad TLS material registration or listener/socket problems on the requested addresses.

Source

Thrown at internal/home/dns.go:131

		DNSFilter:   globalContext.filters,
		Stats:       globalContext.stats,
		QueryLog:    globalContext.queryLog,
		PrivateNets: parseSubnetSet(config.DNS.PrivateNets),
		Anonymizer:  anonymizer,
		DHCPServer:  globalContext.dhcpServer,
		EtcHosts:    hc,
		LocalDomain: config.DHCP.LocalDomainName,
		TLSManager:  tlsManager,
	}

	err = initDNSServer(
		ctx,
		params,
		httpReg,
		confModifier,
	)
	if err != nil {
		return fmt.Errorf("creating dns server: %w", err)
	}

	for _, route := range config.HTTPConfig.DoH.Routes {
		mux.Handle(route, globalContext.dnsServer)
	}

	return nil
}

// initDNSServer initializes the [context.dnsServer].  To only use the internal
// proxy, none of the arguments are required, but params must be non-nil and
// valid.  In other cases all the arguments also must not be nil.  It also must
// not be called unless [config] and [globalContext] are
// initialized.
func initDNSServer(
	ctx context.Context,
	params dnsforward.DNSCreateParams,
	httpReg aghhttp.Registrar,

View on GitHub (pinned to b41aefbe51)

Solutions

  1. Check the wrapped error for the exact cause (bind: address already in use is typical)
  2. Free the conflicting port (e.g. disable systemd-resolved stub listener or stop the other DNS server)
  3. Verify listening interface IPs in config.yaml still exist on the host
  4. Run with CAP_NET_BIND_SERVICE or as root when binding ports <1024

Example fix

# before
# port 53 occupied by systemd-resolved
# after
sudo mkdir -p /etc/systemd/resolved.conf.d
echo -e '[Resolve]
DNSStubListener=no' | sudo tee /etc/systemd/resolved.conf.d/adguard.conf
sudo systemctl restart systemd-resolved
Defensive patterns

Strategy: validation

Validate before calling

// pre-flight: ensure DNS ports are free
import "net"
func portFree(network string, port int) bool {
    l, err := net.Listen(network, fmt.Sprintf(":%d", port))
    if err != nil { return false }
    l.Close()
    return true
}
// refuse to start if 53/853/443 conflict
for _, p := range []struct{n string; v int}{{"udp",53},{"tcp",53}} {
    if !portFree(p.n, p.v) { return fmt.Errorf("port %d/%s busy", p.v, p.n) }
}

Try / catch

if err := initDNS(ctx, params); err != nil {
    var se *net.OpError
    if errors.As(err, &se) && se.Op == "listen" {
        log.Error("DNS port conflict — stop competing resolver", slogutil.KeyError, err)
    }
    return fmt.Errorf("creating dns server: %w", err)
}

Prevention

When it happens

Trigger: Calling initDNS when dnsServerNew fails: invalid listening addresses, port already in use, failure wiring the HTTP/Doh registrar or TLS manager into the dnsforward server constructor.

Common situations: Port 53 (or DoH/DoT/DoQ ports) already bound by systemd-resolved or another DNS server; invalid listen interface IPs in config; insufficient privileges to bind low ports.

Related errors


AI-assisted analysis of AdguardTeam/AdGuardHome@b41aefbe51 (2026-08-27). Data as JSON: /api/errors/039d63a0f3dbd59c. Report an issue: GitHub.