caddyserver/caddy · error

server %s: listener address repeated: %s (already claimed by

Error message

server %s: listener address repeated: %s (already claimed by server '%s')

What it means

App.Validate builds a set of all listener addresses (expanding port ranges) across servers and rejects duplicates. Each concrete network/host/port must be claimed by exactly one server, or config validation fails, naming both the offending and the earlier server.

Source

Thrown at modules/caddyhttp/app.go:431

}

// Validate ensures the app's configuration is valid.
func (app *App) Validate() error {
	lnAddrs := make(map[string]string)

	for srvName, srv := range app.Servers {
		// each server must use distinct listener addresses
		for _, addr := range srv.Listen {
			listenAddr, err := caddy.ParseNetworkAddress(addr)
			if err != nil {
				return fmt.Errorf("invalid listener address '%s': %v", addr, err)
			}
			// check that every address in the port range is unique to this server;
			// we do not use <= here because PortRangeSize() adds 1 to EndPort for us
			for i := uint(0); i < listenAddr.PortRangeSize(); i++ {
				addr := caddy.JoinNetworkAddress(listenAddr.Network, listenAddr.Host, strconv.FormatUint(uint64(listenAddr.StartPort+i), 10))
				if sn, ok := lnAddrs[addr]; ok {
					return fmt.Errorf("server %s: listener address repeated: %s (already claimed by server '%s')", srvName, addr, sn)
				}
				lnAddrs[addr] = srvName
			}
		}

		// logger names must not have ports
		if srv.Logs != nil {
			for host := range srv.Logs.LoggerNames {
				if _, _, err := net.SplitHostPort(host); err == nil {
					return fmt.Errorf("server %s: logger name must not have a port: %s", srvName, host)
				}
			}
		}
	}
	return nil
}

func removeTLSALPN(srv *Server, target string) {

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Give each server distinct addresses/ports (or non-overlapping ranges)
  2. If both should share an address, merge them into one server and route by host matcher
  3. Check for overlapping port ranges when using ranges

Example fix

// before
srv1.listen=[":443"], srv2.listen=[":443"]
// after
srv1.listen=[":443"] with host matchers for both sites
Defensive patterns

Strategy: validation

Validate before calling

seen := map[string]string{}
for sn, s := range srvCfg.Servers {
    for _, a := range s.Listen {
        na, _ := caddy.ParseNetworkAddress(a)
        for i := uint(0); i < na.PortRangeSize(); i++ {
            key := caddy.JoinNetworkAddress(na.Network, na.Host, strconv.Itoa(int(na.StartPort+i)))
            if prev, dup := seen[key]; dup {
                return fmt.Errorf("%s duplicated by %s and %s", key, prev, sn)
            }
            seen[key] = sn
        }
    }
}

Prevention

When it happens

Trigger: Two servers both listening on :443, overlapping port ranges (server A: 8080-8090, server B: 8085), or the same unix socket path in two servers. Since map iteration order varies, either server may be named first.

Common situations: Adding a second site block that gets its own server object with the same address; splitting HTTP/1-only and HTTP/3 servers but keeping identical listen addresses; port-range configs that accidentally overlap.

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/df93dcbcf95036bf. Report an issue: GitHub.