caddyserver/caddy · error
%s app module: start: %v
Error message
%s app module: start: %v
What it means
run() iterates the config's apps calling Start(); the first app whose Start() returns an error aborts startup with '<name> app module: start: %v' (e.g. 'http app module: start: ...'). Prior apps are stopped for a clean unwind. The app name is the module namespace of the app (http, tls, pk, or a plugin app).
Source
Thrown at caddy.go:458
}
}()
// Start
err = func() error {
started := make([]string, 0, len(ctx.cfg.apps))
for name, a := range ctx.cfg.apps {
err := a.Start()
if err != nil {
// an app failed to start, so we need to stop
// all other apps that were already started
for _, otherAppName := range started {
err2 := ctx.cfg.apps[otherAppName].Stop()
if err2 != nil {
err = fmt.Errorf("%v; additionally, aborting app %s: %v",
err, otherAppName, err2)
}
}
return fmt.Errorf("%s app module: start: %v", name, err)
}
started = append(started, name)
}
return nil
}()
if err != nil {
return ctx, err
}
globalMetrics.configSuccess.Set(1)
globalMetrics.configSuccessTime.SetToCurrentTime()
// TODO: This event is experimental and subject to change.
ctx.emitEvent("started", nil)
// now that the user's config is running, finish setting up anything else,
// such as remote admin endpoint, config loader, etc.
err = finishSettingUp(ctx, ctx.cfg)
return ctx, errView on GitHub (pinned to 50e54ee279)
Solutions
- Read the wrapped %v — for http it usually names the exact address that failed to bind.
- Free the conflicting listener (ss -ltnp / lsof -i :<port>) or change the site's port.
- For privileged ports: setcap 'cap_net_bind_service=+ep' $(which caddy) or run via a systemd unit with CAP_NET_BIND_SERVICE.
- Retry the load/restart after fixing; earlier apps were already unwound automatically.
Example fix
# before: port 80 taken by nginx
example.com {
bind 0.0.0.0
}
# after: free the port or move aside
systemctl stop nginx && caddy run --config Caddyfile Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: check every listen address is bindable before loading.
for _, addr := range listenAddrs {
ln, err := net.Listen("tcp", addr)
if err != nil {
return fmt.Errorf("would fail to bind %s: %w", addr, err)
}
ln.Close()
} Try / catch
if _, err := caddy.Run(cfg); err != nil {
if strings.Contains(err.Error(), "app module: start:") {
// named app failed; unwrap and fix its resource (port/socket/creds)
}
} Prevention
- Reserve ports with a single owner (systemd socket activation or explicit allocation).
- Grant cap_net_bind_service instead of running as root for :80/:443.
- Run one Caddy instance per host unless ports are explicitly partitioned.
When it happens
Trigger: http app failing to bind configured listen addresses (port in use, permission on <1024, bad unix socket); tls app failing ACME/TLS provider setup; a plugin app whose Start errors; happens on initial load and on config reloads via unsyncedDecodeAndRun.
Common situations: Another process (or a second Caddy instance) already bound the port; binding :80/:443 as non-root without setcap; unix socket dir missing or wrong permissions; plugin resource limits (file descriptors) at startup.
Related errors
- %v; additionally, aborting app %s: %v
- starting caddy administration endpoint: %v
- listening on %s: %v
- method not allowed
- %v; additionally, cleanup: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/339b7214c58889e1.
Report an issue: GitHub.