AdguardTeam/AdGuardHome · error

starting dhcp server: %w

Error message

starting dhcp server: %w

What it means

Raised when the DHCP server (v4 and/or v6) fails to start after a new configuration is applied via POST /control/dhcp/set_config. The wrapped error comes from the underlying DHCP server's Start method and typically indicates a socket-binding or interface problem (e.g. UDP port 67/68 or DHCPv6 ports already in use, missing CAP_NET_RAW/CAP_NET_ADMIN privileges, or a nonexistent interface).

Source

Thrown at internal/dhcpd/http_unix.go:220

		} else {
			err = fmt.Errorf("checking static ip: %w", err)

			return http.StatusInternalServerError, err
		}
	}

	if !hasStaticIP {
		err = aghnet.IfaceSetStaticIP(ctx, s.conf.Logger, cmdCons, ifaceName)
		if err != nil {
			err = fmt.Errorf("setting static ip: %w", err)

			return http.StatusInternalServerError, err
		}
	}

	err = s.Start(ctx)
	if err != nil {
		return http.StatusBadRequest, fmt.Errorf("starting dhcp server: %w", err)
	}

	return 0, nil
}

type dhcpServerConfigJSON struct {
	V4            *v4ServerConfJSON `json:"v4"`
	V6            *v6ServerConfJSON `json:"v6"`
	InterfaceName string            `json:"interface_name"`
	Enabled       aghalg.NullBool   `json:"enabled"`
}

func (s *server) handleDHCPSetConfigV4(
	conf *dhcpServerConfigJSON,
) (srv DHCPServer, enabled bool, err error) {
	if conf.V4 == nil {
		return nil, false, nil
	}

View on GitHub (pinned to b41aefbe51)

Solutions

  1. Check what holds the DHCP ports: ss -lunp | grep -E ':67|:68' (or :547 for DHCPv6) and stop the conflicting daemon (e.g. systemctl stop dnsmasq)
  2. Ensure the binary has privileges: run as root/CAP_NET_ADMIN+CAP_NET_RAW, or set capabilities with setcap 'cap_net_raw,cap_net_admin=eip'
  3. Verify the configured interface name exists (ip link) and fix InterfaceName in the config
  4. If a previous AdGuard instance died uncleanly, make sure no orphaned process still holds the socket before retrying

Example fix

// before
sudo dnsmasq --no-daemon   # still running elsewhere
curl -X POST http://127.0.0.1:3000/control/dhcp/set_config -d '{"enabled":true,...}'
// -> 400 starting dhcp server: listen udp :67: bind: address already in use

// after
sudo systemctl stop dnsmasq
curl -X POST http://127.0.0.1:3000/control/dhcp/set_config -d '{"enabled":true,...}'
// -> 200
Defensive patterns

Strategy: validation

Validate before calling

// before enabling, check ports and interface
func dhcpPrereqsOK(iface string) bool {
	if _, err := net.InterfaceByName(iface); err != nil { return false }
	for _, p := range []int{67, 68} {
		c, err := net.ListenPacket("udp", fmt.Sprintf(":%d", p))
		if err != nil { return false }
		c.Close()
	}
	return true
}

Try / catch

err := srv.Start(ctx)
if err != nil {
    if strings.Contains(err.Error(), "address already in use") { /* stop conflicting daemon, retry */ }
    if strings.Contains(err.Error(), "permission denied") { /* re-run with CAP_NET_RAW */ }
}

Prevention

When it happens

Trigger: Calling the set_config HTTP endpoint with Enabled=true where s.Start(ctx) fails: another DHCP daemon (dnsmasq, systemd-networkd, an earlier AdGuard instance) holds UDP 67/68, the binary lacks raw-socket capabilities, or the configured interface name does not exist on the host.

Common situations: Running AdGuard Home alongside an existing DHCP server; running in a container without NET_ADMIN/NET_RAW capabilities; renaming or removing network interfaces after a config was saved; stale socket from a previous crashed instance.

Related errors


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