Open-Source Wikis

/

Moby

/

How to contribute

/

Patterns and conventions

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.Wrapf from github.com/pkg/errors are still used widely; new code prefers fmt.Errorf("...: %w", err).
  • Domain error helpers live in errdefs/ (e.g. errdefs.NotFound, errdefs.InvalidParameter). HTTP status codes come from daemon/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.RWMutex and sync.Map are common. Prefer narrow, well-documented locking.
  • golang.org/x/sync/semaphore and singleflight (from resenje.org/singleflight) are used to dedupe expensive work — see disk-usage and image queries on Daemon.
  • Goroutines started outside request handlers should have a clear shutdown path tied to Daemon.Shutdown or service-specific Done channels.

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:

  1. Update api/swagger.yaml.
  2. Update or add types under api/types/<group>/.
  3. Update the relevant router and backend.
  4. Add or update a test under integration/<group>/.
  5. 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/reexeccmd/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>/. Use skip.If(t, ...) from gotest.tools/v3/skip for environment-conditional tests; see TESTING.md.
  • The integration-cli suite 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.

Patterns and conventions – Moby wiki | Factory