Go Project Structure for Microservices Architecture: The Complete golang-standards/project-layout Guide

The golang-standards/project-layout repository provides a battle-tested directory structure that separates concerns between service entry points (/cmd), private implementation details (/internal), shared libraries (/pkg), and deployment artifacts (/deployments) to build maintainable Go microservices.

A well-organized repository makes it easier to develop, test, and deploy microservices written in Go. The golang-standards/project-layout standard offers a generic, community-vetted directory scheme that enforces import boundaries and supports operational needs such as configuration management and containerization. This guide explains how to adapt this layout specifically for microservice architectures, with practical examples from the source code.

Why Directory Structure Matters for Go Microservices

Microservice architectures require strict boundaries between services while enabling code reuse for common concerns like logging and metrics. The project-layout standard addresses this by leveraging Go’s compiler-enforced visibility rules—particularly the internal/ directory—to prevent unauthorized cross-service imports. By keeping infrastructure-as-code next to the service source in deployments/, the layout also reduces drift between application and operational configurations.

According to the repository’s README, the structure is deliberately generic: you adopt only the directories you need while preserving the same import rules and conventions【/cache/repos/github.com/golang-standards/project-layout/master/README.md#L57-L84】.

Core Directories for Microservice Architecture

/cmd: Service Entry Points

The cmd/ directory holds the entry-point binaries for each microservice. Each service gets its own subdirectory (e.g., cmd/user-service/), keeping the main package tiny and focused.

This separation ensures that business logic lives in importable packages rather than being trapped in executable code. As noted in cmd/README.md, real-world projects like Kubernetes and Docker use this pattern to manage multiple binaries from a single repository.

// cmd/user-service/main.go
package main

import (
	"log"
	"net/http"

	"github.com/yourorg/yourrepo/internal/app/user"
	"github.com/yourorg/yourrepo/internal/config"
)

func main() {
	// Load configuration (from configs/ folder, env vars, etc.)
	if err := config.Load(); err != nil {
		log.Fatalf("config error: %v", err)
	}

	// Initialise the user service (business logic lives in internal/app/user)
	svc := user.NewService()

	// Register HTTP handlers (could be generated from /api spec)
	http.HandleFunc("/users", svc.HandleUsers)

	// Start HTTP server
	log.Println("User service listening on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

/internal: Encapsulated Business Logic

The internal/ directory contains private implementation details—models, repository interfaces, and internal utilities—that must not be imported by other services. The Go compiler blocks imports from outside the module, guaranteeing encapsulation at the language level.

Store your service-specific domain logic here to prevent tight coupling between microservices. The internal/README.md explains that any package under this tree is automatically protected by the compiler, making it the safest place for proprietary business rules.

// internal/app/user/service.go
package user

import (
	"encoding/json"
	"net/http"
)

type Service struct{ /* fields like DB, logger */ }

func NewService() *Service { return &Service{} }

func (s *Service) HandleUsers(w http.ResponseWriter, r *http.Request) {
	// Business logic, e.g., fetch users from DB
	users := []string{"alice", "bob"}
	_ = json.NewEncoder(w).Encode(users)
}

/pkg: Reusable Public Libraries

The pkg/ directory hosts reusable libraries (e.g., pkg/logger, pkg/metrics) that other services can depend on. Unlike internal/, code here signals an intentional public API with versioning and documentation expectations.

When building a microservice ecosystem, place cross-cutting concerns like structured logging or distributed tracing here so sibling services can import them via standard module paths like github.com/yourorg/yourrepo/pkg/logger.

// pkg/logger/logger.go
package logger

import "log"

func Init() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
}

/api: Contract-First API Definitions

The api/ directory stores OpenAPI/Swagger specs, protobuf definitions, or any contract files that describe the service’s external interface. As documented in the Service Application Directories section【/cache/repos/github.com/golang-standards/project-layout/master/README.md#L99-L106】, this enables contract-first development and automatic client generation.


# api/user-service/openapi.yaml

openapi: "3.0.0"
info:
  title: User Service API
  version: "1.0"
paths:
  /users:
    get:
      summary: List users
      responses:
        '200':
          description: A JSON array of usernames
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string

/configs and /deployments: Operational Configuration

The configs/ directory houses default configuration files and templating for tools like consul-template or viper, centralizing config management across environments (dev, staging, prod).

The deployments/ directory contains Kubernetes manifests, Helm charts, Dockerfiles, and Terraform modules. Keeping infrastructure as code next to the service it deploys reduces configuration drift and simplifies CI/CD pipelines.


# deployments/Dockerfile

FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o /bin/user-service ./cmd/user-service

FROM alpine:latest
COPY --from=builder /bin/user-service /usr/local/bin/user-service
ENTRYPOINT ["user-service"]

Optional Supporting Directories

While a typical microservice requires at least cmd/, internal/, pkg/, api/, configs/, and deployments/, the layout includes additional directories for specific needs:

  • /web: Static assets, HTML templates, or SPA bundles if the microservice also serves a UI. Isolates web concerns from pure Go logic.
  • /scripts: Build, lint, CI helpers, and custom automation scripts that encourage reproducible builds.
  • /build: CI/CD pipelines, packaging scripts, and container build definitions that separate build logic from source.
  • /test: Integration-test helpers, test data, and example clients that provide a sandbox for end-to-end verification.
  • /docs: Design documents, architecture diagrams, and API documentation for improved onboarding.
  • /tools: Helper binaries that may depend on internal packages (e.g., code generators), keeping auxiliary tooling versioned with the service.

Key Reference Files in the Repository

When implementing this Go project structure for microservices architecture, consult these specific files in the golang-standards/project-layout repository:

  • README.md: High-level overview of the layout and rationale for all directories.
  • cmd/README.md: Guidelines for the cmd directory and examples from real-world projects.
  • internal/README.md: Explanation of the internal import enforcement and recommended sub-structures.
  • pkg/README.md: Advice on when to expose code as a public package versus keeping it internal.
  • api/README.md: Guidance on storing API contracts (OpenAPI, protobuf, etc.) and versioning strategies.
  • deployments/README.md: Examples of deployment manifests and CI/CD integration patterns.

Summary

The golang-standards/project-layout provides a proven foundation for Go microservices:

  • /cmd keeps service entry points minimal and focused
  • /internal enforces encapsulation via compiler-level import restrictions
  • /pkg exposes versioned, reusable libraries to other services
  • /api enables contract-first development with OpenAPI and protobuf definitions
  • /configs and /deployments co-locate operational code with application logic

Adopt directories selectively, evolve the structure as your system grows, and maintain strict boundaries between services to build scalable, maintainable microservice architectures.

Frequently Asked Questions

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

The /internal directory contains code that the Go compiler prevents from being imported by other modules, making it ideal for proprietary business logic that should not leak between microservices. The /pkg directory contains intentionally public code that other services and external projects can import as libraries. Use internal/ for service-specific implementation details and pkg/ for cross-cutting concerns like logging or middleware.

How does the golang-standards/project-layout handle multiple microservices in one repository?

Place each microservice’s entry point in its own subdirectory under /cmd (e.g., cmd/user-service/, cmd/order-service/). This pattern, used by large projects like Kubernetes, allows a monorepo structure where services share common libraries in /pkg while maintaining private business logic in /internal. Each subdirectory under cmd/ represents a separate binary with its own main package.

Where should I put Docker and Kubernetes configurations for Go microservices?

Store container definitions in /deployments, which is specifically designed for Kubernetes manifests, Helm charts, Dockerfiles, and Terraform modules. The deployments/README.md file in the repository provides examples of keeping infrastructure-as-code next to the service it deploys. This co-location reduces drift and simplifies CI/CD pipelines by ensuring build artifacts and deployment configurations version together.

Can I use only parts of the golang-standards/project-layout for my microservice?

Yes. The layout is deliberately generic, and the repository’s documentation explicitly recommends adopting only the directories you need. A minimal microservice might only require cmd/, internal/, pkg/, and api/, while adding deployments/ and configs/ as operational requirements grow. The critical requirement is maintaining the import boundary rules: keep private code in internal/ and public libraries in pkg/.

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 →