Go Project Layout Patterns Used by Popular Go Projects: Standard Structure Explained

The Standard Go Project Layout organizes code into /cmd for executables, /internal for private packages, and /pkg for public libraries, a pattern adopted by Kubernetes, Prometheus, and Terraform to maintain scalable, compiler-enforced architectures.

The golang-standards/project-layout repository documents Go project layout patterns observed across the Go ecosystem to help developers structure applications as they grow. While not an official Go language standard, these conventions are implemented in major open-source repositories to separate concerns, enforce privacy boundaries through compiler rules, and create predictable codebases that scale from microservices to cloud-native platforms.

Core Directory Structure

The layout documented in the repository's README.md organizes projects into logical groups that separate executable entry points from reusable libraries and infrastructure configuration.

/cmd: Executable Entry Points

The /cmd directory contains the main package entry points for applications built by the project. According to cmd/README.md, this pattern keeps main functions minimal—typically just wiring dependency injection and calling into internal packages—while supporting multiple binaries from a single repository.

Major projects using this pattern include Velero, Moby, Prometheus, InfluxDB, Kubernetes, Dapr, and go-ethereum. Each subdirectory under /cmd represents a specific binary, such as /cmd/myapp/main.go.

/internal: Compiler-Enforced Privacy

The /internal directory stores private application code and library packages that external projects cannot import. As documented in internal/README.md, the Go compiler enforces this privacy: any package under an internal/ path can only be imported by code within the same module subtree.

Projects leveraging this enforcement include Terraform, InfluxDB, Perkeep, Jaeger, Moby, Satellity, and MinIO. This prevents accidental API leakage and clarifies which packages constitute the public surface area versus implementation details.

/pkg: Public Library APIs

The /pkg directory signals that contained code is safe for external consumption, though its use is optional and debated within the community. As noted in pkg/README.md, some teams place public libraries here while others use top-level directories directly.

Notable adopters include Containerd, Slim, Telepresence, Jaeger, Istio, Kaniko, and Gvisor. When present, /pkg provides a clear contract: packages here maintain backward compatibility, while code outside it (especially in /internal) does not.

Supporting Infrastructure Directories

Beyond code organization, the standard layout defines directories for non-Go assets and operational concerns:

  • /api – API protocol definitions (OpenAPI/Swagger, Protocol Buffers)
  • /web – Static web assets and frontend templates
  • /configs – Default configuration templates and schema files
  • /deployments – Kubernetes manifests, Helm charts, and orchestration configs
  • /scripts – Build and installation helper scripts (documented in scripts/README.md)
  • /build – CI/CD configurations and packaging scripts (documented in build/README.md)
  • /tools – Helper programs and development utilities
  • /examples – Sample applications demonstrating API usage
  • /vendor – Vendored third-party dependencies for reproducible builds

Real-World Implementation Example

A typical project following these Go project layout patterns structures code to maximize reusability while enforcing boundaries. The following examples demonstrate the relationship between /cmd, /internal, and /pkg.

Minimal Main Package

In /cmd/myapp/main.go, the entry point remains minimal, delegating to internal packages:

package main

import (
    "log"

    "github.com/yourorg/yourproj/internal/app"
)

func main() {
    if err := app.Run(); err != nil {
        log.Fatalf("application error: %v", err)
    }
}

This pattern ensures the main function only handles process lifecycle concerns, while business logic resides in importable packages.

Private Application Logic

The /internal/app/app.go file contains implementation details that cannot be imported by external modules:

package app

import (
    "fmt"
    "github.com/yourorg/yourproj/internal/pkg/util"
)

func Run() error {
    fmt.Println("starting app")
    return util.DoWork()
}

Because this lives under /internal, the Go compiler prevents other repositories from importing github.com/yourorg/yourproj/internal/app.

Public Utility Libraries

Reusable code intended for external consumers resides in /pkg/util/util.go:

package util

import "fmt"

func DoWork() error {
    fmt.Println("doing work in a public package")
    return nil
}

External projects can safely import github.com/yourorg/yourproj/pkg/util, distinguishing public APIs from private implementation details.

Configuration Templates

Default configurations are stored in /configs/config.yaml for discovery at runtime:

port: 8080
log_level: info

This separation allows operators to mount configuration without modifying compiled binaries.

Module Configuration

The repository includes a minimal go.mod file demonstrating that these layout patterns work seamlessly with Go modules. The structure supports both library and application development without requiring special build tags or complex replace directives, making it compatible with standard go build and go install workflows.

Summary

  • The /cmd directory houses executable entry points with minimal main functions, adopted by Kubernetes and Prometheus to separate binary concerns from library code.
  • The /internal directory provides compiler-enforced privacy for implementation details, preventing external import and used by Terraform and Jaeger.
  • The /pkg directory optionally exposes stable public APIs for external consumption, implemented by Containerd and Istio.
  • Supporting directories like /configs, /deployments, and /scripts organize operational assets separately from source code.
  • This layout is not an official Go standard but represents consensus patterns from high-scale open-source projects.

Frequently Asked Questions

Is the Standard Go Project Layout an official Go standard?

No, the Standard Go Project Layout documented in golang-standards/project-layout is not an official Go language specification. It is a community-curated collection of patterns observed in successful open-source projects. The Go team does not mandate any specific directory structure beyond what the go tool requires for modules.

What is the difference between /internal and /pkg in Go projects?

The /internal directory uses Go's compiler-level enforcement to prevent external packages from importing its contents, making it ideal for application-specific logic you want to keep private. The /pkg directory, by convention, signals that packages are intended for public use and should maintain backward compatibility, though its use is optional and some projects place public code at the root instead.

Major infrastructure projects using the /cmd pattern include Kubernetes, Prometheus, InfluxDB, Velero, Moby (Docker), Dapr, and go-ethereum. These repositories place individual binary entry points in subdirectories like /cmd/kubelet or /cmd/server, keeping main packages isolated and small.

Should I use /pkg in my Go project?

Using /pkg depends on your project's scope. If you are building a library intended for external consumption, /pkg clearly signals stable public APIs, as seen in Containerd and Istio. However, for internal applications or when the entire repository is private, omitting /pkg and placing packages at the root may reduce unnecessary nesting. The pkg/README.md notes this convention is controversial and not universally accepted.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →