caddyserver/caddy · warning
shutting down admin server: %v
Error message
shutting down admin server: %v
What it means
Wrapper returned when http.Server.Shutdown of the admin endpoint fails for any reason (after substituting the timeout cause if applicable). The %v carries the underlying net/http error; the most common value is context deadline exceeded, i.e. the graceful stop didn't finish in 10 seconds.
Source
Thrown at admin.go:750
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.
type AdminRoute struct {
Pattern string
Handler AdminHandler
}
type adminHandler struct {
mux *http.ServeMuxView on GitHub (pinned to 50e54ee279)
Solutions
- Inspect the wrapped error text — 'context deadline exceeded' means clients held connections: find and close them
- Reduce admin endpoint traffic during config changes (pause scrapes/health checks during deploy)
- If it persists, report upstream with the inner error and connection dump
Defensive patterns
Strategy: retry
Try / catch
if err := changeConfig(http.MethodPost, "/load", newCfg, "", false); err != nil {
var apiErr caddy.APIError
if errors.As(err, &apiErr) && strings.Contains(apiErr.Error(), "shutting down admin server") {
time.Sleep(time.Second)
// one retry after old connections drain
err = changeConfig(http.MethodPost, "/load", newCfg, "", false)
}
if err != nil {
log.Fatal(err)
}
} Prevention
- Find and close connections blocking admin shutdown (ss -tnp on the admin port)
- Schedule config pushes when admin traffic is quiet
- Treat timeout-flavored shutdown errors as transient; retry once before escalating
When it happens
Trigger: Config change forcing the old admin server to stop while a request or connection is still active past the 10s budget; shutdown erroring for OS-level reasons (listener already closed, FD issues).
Common situations: Hanging clients on the admin port; repeated rapid config loads; containers with aggressive health-check polling of the admin endpoint during reload.
Related errors
- stopping admin server: %ds timeout
- no admin server
- request path is missing object ID
- malformed object path
- unknown object ID '%s'
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/ae792ef249785f06.
Report an issue: GitHub.