Open-Source Wikis

/

Moby

/

Systems

/

API server

moby/moby

API server

Purpose

daemon/server/ implements the HTTP plumbing of the Engine API. It owns route registration, request lifecycle, middleware chain, error-to-status mapping, and the small backend interfaces each handler depends on. It does not know about containers, images, or networks directly — those live behind backend interfaces that Daemon implements.

Layout

daemon/server/
├── server.go          # Server struct, mux, otel http handler, error response
├── middleware.go
├── middleware/        # Version, CORS, debug, experimental, auth (UID), prometheus
├── httpstatus/        # error -> HTTP status code translation
├── httputils/         # decoders, JSON helpers, hijack support
├── router/            # one package per resource group (container, image, network, ...)
├── backend/           # backend interfaces (ContainerBackend, etc.)
├── buildbackend/      # builder backend interface
├── imagebackend/      # image backend interface
├── networkbackend/    # network backend interface
├── volumebackend/     # volume backend interface
├── swarmbackend/      # swarm backend interface

Request path

graph LR
  Listener --> Mux[gorilla/mux + version matcher]
  Mux --> OTEL[otelhttp wrapper]
  OTEL --> MWChain[middleware chain]
  MWChain --> Handler[router.Route handler]
  Handler --> Backend[Backend interface impl]
  Backend --> Daemon[Daemon]

The dispatcher is Server.makeHTTPHandler in daemon/server/server.go. It:

  1. Wraps the route in otelhttp.NewHandler for OpenTelemetry tracing.
  2. Sets dockerversion.UAStringKey and an OTel "trigger=api" baggage value on the context.
  3. Builds the middleware chain via handlerWithGlobalMiddlewares.
  4. Calls the route's httputils.APIFunc(ctx, w, r, vars).
  5. On error, maps to an HTTP status via httpstatus.FromError and writes either plain text (for clients on API < 1.24, the now-unsupported floor) or a JSON common.ErrorResponse.
  6. Returns HTTP 499 if the client disconnected mid-request.

Routers

Each resource group is a router.Router implementation in daemon/server/router/<group>/:

Router Endpoints handled
container/ /containers/..., /exec/..., /_ping-adjacent control routes.
image/ /images/..., image search, history, prune.
build/ /build, /build/prune, /build/cancel (delegates to BuildKit or classic).
network/ /networks/....
volume/ /volumes/....
system/ /info, /version, /events, /df, /auth, /_ping.
swarm/ /swarm, /services, /tasks, /nodes, /secrets, /configs.
plugin/ /plugins/... (managed plugins).
session/ /session (BuildKit hijacked stream).
grpc/ /grpc for BuildKit gRPC over HTTP/2.
checkpoint/ /containers/{name}/checkpoints (CRIU experimental).
debug/ /debug/pprof/..., profiling endpoints (when --debug).
distribution/ /distribution/.../json for inspecting registry manifests.

router.Router and router.Route are tiny interfaces in daemon/server/router/router.go; they exist so that route registration, method, and path are all data, not code.

Versioning

Every route is mounted twice by Server.CreateMux:

m.Path(versionMatcher + r.Path()).Methods(r.Method()).Handler(f)
m.Path(r.Path()).Methods(r.Method()).Handler(f)

Where versionMatcher = "/v{version:[0-9.]+}". Handlers that need to vary per API version pull the version mux variable and use daemon/internal/versions/. The minimum API version is enforced in the VersionMiddleware (daemon/server/middleware/version.go). Default and minimum versions live in daemon/config/.

Middleware

Middleware is a function type in daemon/server/middleware/. Built-in chain:

Middleware Source Role
VersionMiddleware version.go Enforces min/max API version, sets headers.
CORSMiddleware cors.go Configurable CORS for the TCP socket.
UIDAllowlistMiddleware uid_allowlist.go Restricts socket access by peer UID where supported.
ExperimentalMiddleware experimental.go Gates experimental endpoints on daemon config.
DebugRequestMiddleware debug.go Logs request bodies in debug mode (with redactions).

The Prometheus middleware (daemon/server/middleware/prometheus.go) emits per-route counters when metrics are enabled. The authorization plugin chain lives in pkg/authorization/ and is wired into the server when the --authorization-plugin flag is set.

Hijacked streams

/containers/{name}/attach, /exec/{id}/start, and /grpc upgrade the HTTP connection. Helpers in daemon/server/httputils/ implement the hijack handshake; the routers call them and then hand off raw net.Conn instances to the relevant backend.

Error mapping

Errors flowing back to the handler are mapped to HTTP status codes by httpstatus.FromError in daemon/server/httpstatus/. It walks the error chain and matches against errdefs.Is* predicates (errdefs/). For example, errdefs.NotFound becomes 404, errdefs.InvalidParameter becomes 400, errdefs.Conflict becomes 409.

Tests

server_test.go next to the server file covers the dispatcher. Per-router unit tests live alongside each handler (container_routes_test.go, etc.) and use small fakes for the backend interfaces.

Entry points for modification

  • New endpoint: declare a method on the relevant backend interface, implement it on Daemon, register a router.Route in the matching router package, update api/swagger.yaml.
  • New middleware: implement middleware.Middleware and register via Server.UseMiddleware in daemon/command.
  • New error type: add an errdefs.Is* predicate and update httpstatus.FromError.

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

API server – Moby wiki | Factory