moby/moby
Patterns and conventions
Conventions you will see across the Moby Go code, and that PRs are expected to follow.
Module layout
Three Go modules live in this repo (per README.md):
| Module | Path | Purpose | Stability |
|---|---|---|---|
github.com/moby/moby/v2 |
repo root | Daemon and binaries. | No library API guarantees. |
github.com/moby/moby/api |
api/ |
Shared request/response types and Swagger. | SemVer-tagged independently. |
github.com/moby/moby/client |
client/ |
Go HTTP client for the Engine API. | SemVer-tagged independently. |
Per CONTRIBUTING.md, source goes under api, client, or daemon. New utilities should land under daemon/internal/ rather than the legacy top-level pkg/.
Per-OS file conventions
The codebase uses Go build constraints by filename suffix extensively:
_linux.go,_windows.go,_freebsd.go,_darwin.go— OS-specific._unix.go— Unix-y systems (Linux + FreeBSD + Darwin)._unsupported.go/_nolinux.go/_others.go— fallback stubs.
This is how files like daemon/oci_linux.go and daemon/oci_windows.go coexist.
Backend interfaces
Routers under daemon/server/router/ deliberately depend only on small interfaces declared in daemon/server/backend/ (and friends). The Daemon struct satisfies them. This keeps HTTP code testable and lets each subsystem control its public contract. When adding endpoints, declare a new method on the right backend interface, implement it on Daemon, and wire a new router.Route in the matching router package.
Errors
errors.Wrap/errors.Wrapffromgithub.com/pkg/errorsare still used widely; new code prefersfmt.Errorf("...: %w", err).- Domain error helpers live in
errdefs/(e.g.errdefs.NotFound,errdefs.InvalidParameter). HTTP status codes come fromdaemon/server/httpstatus/. - The router's HTTP layer turns errors into JSON via the central handler in
daemon/server/server.go.
Logging
Use github.com/containerd/log:
log.G(ctx).WithField("container", id).Info("starting")log.G(ctx) reads a logger from the request context; the daemon installs structured fields in the server middleware. Avoid fmt.Print-style logging.
Context propagation
Every backend method takes a context.Context as its first argument. Cancellation is honored: the HTTP handler in daemon/server/server.go returns HTTP 499 when the client disconnects mid-request. Long-running operations (pulls, pushes, builds) must respect ctx and stream progress back over the response body.
Concurrency
sync.RWMutexandsync.Mapare common. Prefer narrow, well-documented locking.golang.org/x/sync/semaphoreandsingleflight(fromresenje.org/singleflight) are used to dedupe expensive work — see disk-usage and image queries onDaemon.- Goroutines started outside request handlers should have a clear shutdown path tied to
Daemon.Shutdownor service-specificDonechannels.
API versioning
Every endpoint is mounted both at /v{version}/path and /path by daemon/server/server.go (versionMatcher). Handlers introspect the version mux variable when behavior changes between API versions. Helpers in daemon/internal/versions/ keep comparisons consistent. The minimum supported API version is enforced by the version middleware in daemon/server/middleware/.
Swagger as source of truth
api/swagger.yaml defines the wire shape. Many types in api/types/ are hand-written, but the goal is for them to be derivable from Swagger. When changing an endpoint:
- Update
api/swagger.yaml. - Update or add types under
api/types/<group>/. - Update the relevant router and backend.
- Add or update a test under
integration/<group>/. - Run
hack/validate/swagger.
Reexec / privileged helpers
Several daemon paths need to run code in a different namespace or with a specific binary identity. The repo uses github.com/moby/sys/reexec — cmd/dockerd/main.go calls reexec.Init() first thing. Subsystems register reexec entry points (search the codebase for reexec.Register).
Plugins
Two plugin systems coexist:
- Legacy ("v1") plugins: simple HTTP/JSON-RPC over a Unix socket. Code:
pkg/plugins/. - Managed ("v2") plugins: OCI-distributed, lifecycle managed by the daemon. Code:
daemon/pkg/plugin/.
When adding a new plugin extension point (auth, log, network, volume, IPAM), follow the existing pattern: define an interface, register a getter via pkg/plugingetter, and resolve via the plugin manager.
Tests
- Unit tests live next to the code under test, using
gotest.tools/v3/assert. - Integration tests go under
integration/<area>/. Useskip.If(t, ...)fromgotest.tools/v3/skipfor environment-conditional tests; seeTESTING.md. - The
integration-clisuite is frozen — do not add tests there.
DCO sign-off
All commits must be signed off (git commit -s). This is enforced by CI and the validation scripts in hack/validate/.
See also
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.