caddyserver/caddy · warning

stopping admin server: %ds timeout

Error message

stopping admin server: %ds timeout

What it means

This message is the cause attached to the 10-second context given to http.Server.Shutdown when stopping the previous admin server. If graceful shutdown exceeds 10s (a client holding a connection open, a slow streaming request on /metrics or /load), the context expires and this cause is surfaced so the operator sees it was a timeout, not a random failure.

Source

Thrown at admin.go:743

func adminPathAllowed(reqPath, allowedPath string) bool {
	if allowedPath == "" || allowedPath == "/" {
		return strings.HasPrefix(reqPath, allowedPath)
	}
	if reqPath == allowedPath {
		return true
	}
	if strings.HasSuffix(allowedPath, "/") {
		return strings.HasPrefix(reqPath, allowedPath)
	}
	return strings.HasPrefix(reqPath, allowedPath+"/")
}

func stopAdminServer(srv *http.Server) error {
	if srv == nil {
		return fmt.Errorf("no admin server")
	}
	timeout := 10 * time.Second
	ctx, cancel := context.WithTimeoutCause(context.Background(), timeout, fmt.Errorf("stopping admin server: %ds timeout", int(timeout.Seconds())))
	defer cancel()
	err := srv.Shutdown(ctx)
	if err != nil {
		if cause := context.Cause(ctx); cause != nil && errors.Is(err, context.DeadlineExceeded) {
			err = cause
		}
		return fmt.Errorf("shutting down admin server: %v", err)
	}
	Log().Named("admin").Info("stopped previous server", zap.String("address", srv.Addr))
	return nil
}

// AdminRouter is a type which can return routes for the admin API.
type AdminRouter interface {
	Routes() []AdminRoute
}

// AdminRoute represents a route for the admin endpoint.

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Close or shorten long-lived connections to the admin endpoint (raise scrape_interval/timeout sanity, close idle clients)
  2. Avoid hammering /load with concurrent changes; wait for one change to settle
  3. If it recurs with no obvious client, capture which connection blocks shutdown (ss -tnp against the admin port) and fix that client
Defensive patterns

Strategy: retry

Prevention

When it happens

Trigger: A long-lived HTTP connection to the admin endpoint (e.g. an open metrics scrape, SSE, or a stalled POST /load) that does not finish within the 10s graceful window while a config change replaces the admin server.

Common situations: Prometheus scraping /metrics with a slow exporter; scripts that open keep-alive connections to localhost:2019 and never close them; frequent config changes in quick succession.

Understand the failure class

Related errors


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