Moby — Structure#

Layout pattern#

Custom Monorepo with Sub-module Extraction in Progress

Moby began as a single Go module and is actively being decomposed: api/ and client/ are now independent Go modules (github.com/moby/moby/api, github.com/moby/moby/client) that the root module references via require. The daemon/ package is itself a deep sub-hierarchy with 247+ packages, functioning as a large internal monolith. This is not a standard cmd/internal/pkg layout—the business logic lives almost entirely inside daemon/, with pkg/ relegated to a shrinking set of utility packages.


Directory map#

repositories/moby/
├── api/                  # INDEPENDENT SUB-MODULE — Docker HTTP API types, Swagger spec
│   ├── types/            # Request/response structs (container, image, network, swarm…)
│   ├── pkg/              # API-internal utilities
│   └── swagger.yaml      # OpenAPI specification for the Docker Engine API
│
├── client/               # INDEPENDENT SUB-MODULE — Go client library for Docker API
│   ├── *.go              # ~150 files, one per API operation (container_start.go, etc.)
│   ├── internal/         # client-internal helpers
│   └── pkg/              # client-internal packages
│
├── cmd/
│   ├── dockerd/          # Main daemon binary entry point
│   └── docker-proxy/     # Userland TCP/UDP/SCTP proxy for port mapping (Linux only)
│
├── daemon/               # THE CORE — virtually all daemon logic (247+ sub-packages)
│   ├── *.go              # Top-level daemon type and methods
│   ├── builder/          # Dockerfile build engine and remote context fetching
│   ├── cluster/          # Docker Swarm mode orchestration (executor, converters)
│   ├── command/          # cobra CLI wiring, startup, shutdown, signal handling
│   ├── config/           # Daemon configuration struct and defaults
│   ├── container/        # Container state/lifecycle struct
│   ├── containerd/       # containerd image store integration (modern path)
│   ├── events/           # Internal event bus
│   ├── graphdriver/      # Legacy storage drivers (overlay2, btrfs, zfs, vfs, windows)
│   ├── images/           # Image management (legacy graphdriver path)
│   ├── initlayer/        # Linux init layer setup
│   ├── internal/         # ~45 daemon-private packages (see below)
│   ├── libnetwork/       # Embedded SDN library (bridge, overlay, macvlan, ipvlan…)
│   ├── links/            # Legacy container links
│   ├── listeners/        # TLS / Unix socket / Windows named pipe listener setup
│   ├── logger/           # Pluggable logging drivers (json-file, syslog, journald, fluentd…)
│   ├── names/            # Container name registry
│   ├── network/          # Network request/response translation
│   ├── pkg/              # daemon-local packages (oci, opts, plugin, registry)
│   ├── server/           # HTTP API server and router (one package per resource type)
│   ├── snapshotter/      # containerd snapshotter integration (modern storage path)
│   ├── stats/            # Container stats collection
│   └── volume/           # Volume management (local driver, mount tracking, safepath)
│
├── dockerversion/        # Version constants injected at build time
├── errdefs/              # Error sentinel values (wraps github.com/containerd/errdefs)
│
├── integration/          # Modern integration tests (per-resource directories)
│   ├── build/, container/, image/, network/, volume/, plugin/, service/, …
│   └── internal/         # Integration test helpers
│
├── integration-cli/      # Legacy integration test harness (deprecated, not removed)
│   ├── checker/, cli/, daemon/, environment/, fixtures/
│
├── internal/             # Root-level internal utilities
│   ├── iterutil/         # Iteration helpers
│   ├── namesgenerator/   # Random human-readable name generator
│   ├── sliceutil/        # Slice helpers
│   ├── test/             # Test helpers (request, environment)
│   └── testutil/         # Assertion helpers for tests
│
├── pkg/                  # Public (exported) utility packages
│   ├── authorization/    # HTTP authorization plugin middleware
│   ├── homedir/          # Home directory resolution
│   ├── ioutils/          # I/O helpers (atomic file write, etc.)
│   ├── longpath/         # Windows long path workarounds
│   ├── meminfo/          # /proc/meminfo parsing
│   ├── parsers/          # Kernel version, OS string parsers
│   ├── pidfile/          # PID file management
│   ├── plugingetter/     # Interface for looking up installed plugins
│   ├── plugins/          # Plugin discovery, socket transport, protobuf RPC gen tool
│   ├── pools/            # bytes.Buffer pool
│   ├── process/          # Cross-platform process utilities
│   ├── sysinfo/          # Linux cgroup/kernel feature detection
│   ├── tailfile/         # Tail-from-end of file
│   └── useragent/        # HTTP User-Agent string construction
│
├── contrib/              # AppArmor profiles, SELinux policy, init scripts, OTel helper
├── docs/                 # API docs (mostly stubs pointing to external docs site)
├── hack/                 # Build, test, and validation scripts
│   ├── dockerfile/       # Tool installation scripts used in multi-stage Dockerfile
│   ├── make/             # Shell script build targets
│   ├── test/             # Test runner helpers
│   └── validate/         # Linting and validation scripts (vendoring, copyright, etc.)
├── man/                  # man page sources (separate vendor)
├── project/              # Project governance docs (PACKAGERS.md, CONTRIBUTORS, etc.)
├── releases/             # Release notes
└── vendor/               # Vendored dependencies (full copy)

Entry points#

BinaryPathPurpose
dockerdcmd/dockerd/main.goThe Docker Engine daemon. Calls reexec.Init() (for worker process re-execution), then command.NewDaemonRunner() which boots cobra CLI, reads config, starts the HTTP API server and attaches to containerd.
dockerd (Windows)cmd/dockerd/main_windows.goWindows-specific service wrapper; same cobra entrypoint but wrapped in a Windows Service runner.
docker-proxycmd/docker-proxy/main_linux.goUserland network proxy for container port bindings (TCP, UDP, SCTP). A separate, minimal binary with no daemon dependency.

Package organization#

daemon/internal/ — Private daemon packages (~45 packages)#

These are declared internal to prevent use outside daemon/ subtree:

PackagePurpose
builder-next/BuildKit integration adapters — bridges moby’s daemon APIs to BuildKit worker
capabilities/Linux capabilities parsing
cleanups/Ordered cleanup function registry for graceful shutdown
containerfs/Container filesystem path abstraction
directory/Recursive directory size calculation
distribution/Image push/pull, registry transfer, xfer queuing
filedescriptors/FD count / leak checking
filters/Docker list filter parsing and matching
idtools/UID/GID mapping for user namespaces
image/Image store, layer management, tarexport
layer/Layer chain management (legacy graphdriver path)
libcontainerd/containerd client abstraction — local (in-process) and remote (gRPC) modes
metrics/Prometheus metrics registration
nri/Node Resource Interface hooks (CRI plugin integration)
opts/CLI option types (ulimits, device mappings, etc.)
plugin/Managed plugin lifecycle (v2 plugin system)
progress/Progress reporting for image transfers
quota/Disk quota enforcement
refstore/Image reference/tag store
restartmanager/Container restart policy state machine
rootless/Rootless daemon detection and path adjustments
runconfig/Container config validation and defaults
stream/I/O stream multiplexing (stdin/stdout/stderr attach)
system/OS-level helpers (xattrs, ioctl, umask)

Public packages (pkg/)#

The pkg/ directory has been shrinking as packages migrate to standalone github.com/moby/* repositories. What remains is used by both the daemon and external integrators:

PackagePurpose
authorization/Middleware for HTTP auth plugin system
plugins/Plugin discovery (socket, TCP), pluginrpc-gen code generator
plugingetter/Interface for plugin lookup (decoupling)
sysinfo/Runtime cgroup/seccomp/AppArmor capability detection
ioutils/Atomic file write, temp dir management
parsers/Kernel version strings, OS identification

Layering#

The dependency flow is broadly:

cmd/dockerd
    └─► daemon/command (cobra setup, bootstrap)
            └─► daemon/ (core Daemon type)
                    ├─► daemon/server/ (HTTP API)
                    ├─► daemon/libnetwork/ (networking)
                    ├─► daemon/internal/libcontainerd/ (container runtime)
                    ├─► daemon/internal/distribution/ (image registry)
                    ├─► daemon/cluster/ (Swarm orchestration)
                    └─► daemon/internal/layer/ + daemon/graphdriver/ (storage, legacy)
                        OR daemon/containerd/ + daemon/snapshotter/ (storage, modern)

api/         (independent module — type definitions only, no daemon deps)
client/      (independent module — HTTP client, depends on api/)
pkg/         (utilities — no daemon deps, usable externally)
errdefs/     (error sentinel package — minimal, no daemon deps)

This is broadly a layered architecture but without strict enforcement (daemon internals freely cross-reference each other via the flat daemon.Daemon god-struct).


Build system#

  • Build tool: make with Docker BuildKit (docker buildx bake) as the primary build executor. Local builds can also use ./hack/make.sh directly.
  • Key targets:
    • make binary — statically linked dockerd and docker-proxy via BuildKit
    • make dynbinary — dynamically linked binaries
    • make binary-cross — cross-compile for non-Linux targets
    • make test — unit + integration + docker-py tests
    • make test-integration — runs integration suite inside a Docker-in-Docker container
    • make test-unit — unit tests only, usable locally without Docker
    • make install — install binaries to system path
  • Docker: Multi-stage Dockerfile with ~40+ named stages. Key stages:
    • base — Go toolchain
    • containerd-build, runc-build, tini-build, rootlesskit-build — build runtime dependencies from source
    • binary — final daemon binary
    • dev — development container with all tools (delve, golangci-lint, gotestsum, etc.)
  • Build tags: DOCKER_BUILDTAGS env var injects compile-time feature flags (e.g., seccomp, journald, exclude_graphdriver_btrfs)
  • Version injection: dockerversion/ constants set via -ldflags "-X ..." at build time

Notable structural decisions#

  1. daemon/ as a monolith with nested internal/: Rather than extracting subsystems into separate top-level packages, all daemon logic lives under daemon/. The internal/ subdirectory enforces that ~45 implementation packages cannot be imported outside the daemon subtree — an unusual use of Go’s internal mechanism applied to a sub-directory, not just the root module.

  2. Dual storage paths in parallel: daemon/graphdriver/ (legacy overlay2, btrfs, zfs) and daemon/containerd/ + daemon/snapshotter/ (modern containerd snapshotters) coexist. A runtime selector (daemon/image_store_choice.go) chooses the path based on daemon configuration. This structural duplication reflects a multi-year migration that hasn’t completed.

  3. Sub-module extraction as architectural signal: api/ and client/ being independent modules means they have their own versioning, go.mod, test suites, and CI — even while physically living in the same repository. This is a stepping stone toward a fully split repository (similar to how containerd/containerd and opencontainers/runc were previously embedded in this repo).

  4. integration-cli/ legacy coexistence: The old CLI-driven integration test harness (integration-cli/) exists alongside the newer integration/ suite, which uses the Go client library directly. The old harness is deprecated but not deleted, creating structural duplication in the test layer.

  5. libnetwork/ as an embedded sub-project: daemon/libnetwork/ is a standalone SDN implementation (formerly its own repository — docker/libnetwork) with its own internal hierarchy of 35+ packages including driverapi/, ipamapi/, networkdb/, iptables/, osl/ (OS layer). Its re-integration into the moby tree trades external versioning for tighter coupling.

  6. Multi-stage Dockerfile as the authoritative build spec: The Dockerfile (not Makefile) is the single source of truth for building — it specifies exact tool versions for containerd, runc, tini, rootlesskit, and Go itself. The Makefile simply invokes docker buildx bake, delegating all build logic to BuildKit.

  7. Platform compilation coverage: Nearly every subsystem has _linux.go, _windows.go, _unix.go, _freebsd.go, and _unsupported.go variants, reflecting genuine multi-platform support rather than just Linux. The daemon/command/ directory alone has 8 platform-variant files.