MinIO — Structure#

Layout pattern#

Custom / Inverted Standard Go Layout

MinIO deviates from the canonical Go layout in a striking way: the entire application lives in a single cmd/ package (~453 Go files, package name cmd), with no traditional separation of handlers/services/repositories into sub-packages. The internal/ tree holds genuinely shared sub-libraries, but application logic is not layered — it is flat within cmd/. The root main.go is a thin wrapper that delegates immediately to cmd.Main(). There is no pkg/ directory; everything exported for the rest of the ecosystem lives in separate repositories (github.com/minio/pkg, etc.).

Directory map#

minio/
├── main.go                   # Root entry point — just calls cmd.Main(os.Args)
├── cmd/                      # The entire application (~453 .go files, package "cmd")
│   ├── main.go               # CLI app construction (minio server, fmtgen commands)
│   ├── server-main.go        # Server bootstrap and initialization
│   ├── object-api-interface.go  # ObjectLayer central interface
│   ├── erasure-*.go          # Erasure coding engine (encode/decode, sets, pools)
│   ├── admin-handlers*.go    # Admin API HTTP handlers
│   ├── bucket-*.go           # Bucket lifecycle, policy, notification handlers
│   ├── iam-*.go              # Identity and access management
│   ├── site-replication*.go  # Cross-site replication
│   ├── batch-*.go            # Batch job framework
│   ├── xl-storage*.go        # Low-level disk storage layer
│   ├── testdata/             # Test fixtures (xl.meta, config files, etc.)
│   └── ...
├── internal/                 # 34 internal sub-packages (shared libraries)
│   ├── amztime/              # AWS-compatible time formatting
│   ├── arn/                  # Amazon Resource Name parsing
│   ├── auth/                 # Credential types and signing
│   ├── bpool/                # Byte-slice buffer pool
│   ├── bucket/               # Bucket-policy sub-libraries
│   │   ├── bandwidth/        # Bandwidth throttling
│   │   ├── encryption/       # SSE configuration
│   │   ├── lifecycle/        # ILM lifecycle parsing
│   │   ├── object/           # Per-object lock
│   │   ├── replication/      # Replication config parsing
│   │   └── versioning/       # Bucket versioning config
│   ├── cachevalue/           # Generic timed-refresh cache
│   ├── color/                # Terminal colorisation
│   ├── config/               # Configuration subsystem
│   │   ├── api/              # S3 API knobs
│   │   ├── batch/            # Batch job config
│   │   ├── compress/         # Compression config
│   │   ├── dns/              # Federated DNS config
│   │   ├── etcd/             # etcd backend config
│   │   ├── heal/             # Healing config
│   │   ├── identity/         # IDP config (LDAP, OpenID)
│   │   ├── ilm/              # ILM (lifecycle) config
│   │   ├── lambda/           # Lambda / webhook config
│   │   ├── notify/           # Event notification config
│   │   ├── policy/           # Bucket policy config
│   │   ├── scanner/          # Background scanner config
│   │   ├── storageclass/     # STANDARD / RRS storage class
│   │   └── subnet/           # MinIO subnet config
│   ├── crypto/               # Encryption (SSE-C, SSE-S3, SSE-KMS)
│   ├── deadlineconn/         # Net conn with deadline
│   ├── disk/                 # OS-level disk stats and info
│   ├── dsync/                # Distributed reader-writer mutex (quorum-based)
│   ├── etag/                 # S3 ETag parsing and generation
│   ├── event/                # Event notification types and targets
│   ├── grid/                 # Custom intra-cluster RPC (mux WebSocket)
│   ├── handlers/             # HTTP utility middleware
│   ├── hash/                 # Content-hash reader/writer
│   ├── http/                 # HTTP transport utilities
│   ├── init/                 # Package-level init: sets TZ=UTC
│   ├── ioutil/               # Extended io utilities
│   ├── jwt/                  # JWT parsing / validation
│   ├── kms/                  # KMS abstraction (KES integration)
│   ├── lock/                 # Local reader-writer lock with deadlock detection
│   ├── logger/               # Structured logger with audit support
│   ├── lsync/                # Local lock primitives
│   ├── mcontext/             # Request-scoped metadata context
│   ├── mountinfo/            # Mount point detection
│   ├── net/                  # URL/host utilities
│   ├── once/                 # Error-aware sync.Once
│   ├── pubsub/               # In-process pub/sub
│   ├── rest/                 # HTTP REST client (admin API client)
│   ├── ringbuffer/           # Fixed-size ring buffer
│   ├── s3select/             # S3 Select (SQL over objects)
│   │   ├── csv/              # CSV parser
│   │   ├── json/             # JSON parser
│   │   ├── jstream/          # Streaming JSON tokeniser
│   │   ├── parquet/          # Parquet reader
│   │   ├── simdj/            # SIMD-accelerated JSON
│   │   └── sql/              # SQL parser/executor for S3 Select
│   └── store/                # Persistent event/WAL store
├── docs/                     # Topic-specific documentation and shell test scripts
│   ├── distributed/          # Distributed mode setup + integration test scripts
│   ├── bucket/               # Lifecycle, replication, versioning shell tests
│   ├── iam/                  # IAM policies and identity integration tests
│   ├── erasure/              # Erasure-coding documentation
│   └── ...                   # (24 topic subdirs total)
├── buildscripts/             # Build/CI helpers
│   ├── gen-ldflags.go        # ldflags generation for version embedding
│   ├── cross-compile.sh      # Cross-compilation helper
│   ├── race.sh               # Race detector test runner
│   └── ...
├── dockerscripts/            # Docker entrypoint and utilities
├── helm/                     # Helm chart for Kubernetes deployment
├── Dockerfile                # Production Dockerfile (downloads pre-built binary)
├── Dockerfile.release        # Multi-stage build: compile from source
├── Dockerfile.scratch        # Minimal scratch-based image
├── Makefile                  # Primary build system
├── go.mod                    # Module root
└── main.go                   # Thin entry point

Entry points#

FileBinary / Purpose
main.go (root)Sole binary entry point. Imports internal/init (TZ side-effect), then calls cmd.Main(os.Args).
cmd/main.goRegisters two CLI commands (server, fmtgen) via github.com/minio/cli, then dispatches.
cmd/server-main.goThe server subcommand — starts the full MinIO server (HTTP, erasure, IAM, etc.).
cmd/fmtgen-main.gofmtgen subcommand — a code-generation utility (not a server).

MinIO produces one binary (minio). There is no cmd/ subdirectory tree with multiple binaries — cmd/ is a single package, not a directory of separate main packages.

Package organization#

  • Internal packages (internal/): These are genuine shared libraries with clearly bounded responsibilities, extracted from the application core:

    • internal/grid — custom cluster RPC layer (multiplexed WebSocket + typed message handlers)
    • internal/dsync — distributed reader-writer mutex using quorum consensus
    • internal/crypto — SSE-C/SSE-S3/SSE-KMS encryption primitives
    • internal/kms — KES (Key Encryption Service) abstraction
    • internal/event — S3 event notification types, targets (Kafka, NATS, Redis, etc.)
    • internal/s3select — full S3 Select SQL engine over CSV/JSON/Parquet
    • internal/config/* — per-feature configuration structs and parsers
    • internal/bucket/* — S3 bucket-policy-related parsing (lifecycle, replication, versioning, WORM)
    • internal/logger — structured logger with audit log support
    • internal/auth — AWS-style credential types and signing
    • internal/bpool, internal/ioutil, internal/ringbuffer — low-level performance utilities
    • internal/init — forces TZ=UTC via package init side-effect (must be first import)
  • Public packages (pkg/): None. MinIO has no pkg/ directory. Reusable code that might benefit other consumers is published as separate Go modules under github.com/minio/pkg (external repository).

  • Layering: MinIO does not follow clean architecture or hexagonal layering. The cmd/ package is the entire application — HTTP handlers, business logic, erasure engine, IAM, replication, and storage all coexist as peers within the same package. The internal/ packages form a dependency ring around cmd/ rather than being hierarchical layers within it.

Build system#

  • Build tool: GNU Make (Makefile) with Go toolchain
  • Key targets:
    • make build — standard go build ./...; produces the minio binary
    • make install — installs with race detector
    • make test — runs verifiers (lint + gen check) then go test -tags kqueue,dev ./...
    • make test-race — runs tests with -race flag via buildscripts/race.sh
    • make crosscompile — cross-compiles for multiple targets via buildscripts/cross-compile.sh
    • make lintgolangci-lint with custom .golangci.yml
    • make check-gen — verifies _gen.go files are committed and go.sum is clean
    • make test-replication, make test-iam, make test-decom, etc. — integration test suites driven by shell scripts in docs/
  • ldflags: buildscripts/gen-ldflags.go generates version embedding at build time
  • Docker: Yes, multi-stage. Dockerfile.release compiles from source (golang:1.24-alpine + ubi9/ubi-micro base). Dockerfile.release also downloads mc (MinIO client). Dockerfile is a thin wrapper that pulls pre-built binaries from dl.min.io for production releases. Dockerfile.scratch produces a minimal scratch image. EXPOSE 9000.
  • CI: GitHub Actions (.github/workflows/) with jobs for: Go build (go.yml), cross-compilation (go-cross.yml), linting (go-lint.yml), healing tests (go-healing.yml), resiliency tests (go-resiliency.yml), IAM integrations (iam-integrations.yaml), Helm lint (helm-lint.yml).

Notable structural decisions#

  1. Single-package application in cmd/: Placing 453 Go files in one package named cmd is architecturally unusual. It maximises cross-file access (no import cycles, no visibility barriers) at the cost of cohesion signalling. The team prioritises rapid development within the package over enforced module boundaries — a deliberate trade-off for a team that knows the codebase intimately.

  2. internal/ as a surrounding utility ring: Rather than layering application logic into internal sub-packages, MinIO uses internal/ purely for extracted libraries (locking, networking, config parsing, s3select). The application-level code in cmd/ is not “on top of” internal/ in a clean dependency hierarchy — cmd/ imports liberally from internal/ but the logic boundary is blurry.

  3. No pkg/ — separate modules instead: Code intended for external use (e.g., credential types, CLI helpers, pkg utilities) is published as independent Go modules (github.com/minio/pkg, github.com/minio/cli, github.com/minio/madmin-go). This enforces a cleaner public API boundary than a pkg/ directory would.

  4. internal/init for guaranteed global state: A dedicated package solely to set TZ=UTC via init() is an unusual but robust pattern — it guarantees the side-effect fires before any other code, even across test binaries, as long as the import is present and first.

  5. Docs directory doubles as integration test runner: The docs/ directory is not just documentation — it contains shell scripts (e.g., docs/distributed/decom.sh, docs/bucket/replication/setup_3site_replication.sh) that are the integration test suite, invoked by make test-* targets. This blurs the line between documentation and testing infrastructure.

  6. Build tags for platform and dev modes: The test and build commands use -tags kqueue,dev, indicating that certain code paths (kqueue I/O event notification on BSD/macOS, and a dev build mode with additional instrumentation) are gated behind build tags — a structured way to handle platform variation and development-vs-production behavior differences.