# How to Structure Go Projects with Docker and Kubernetes

> Structure Go projects effectively with Docker and Kubernetes. Learn to organize your code using golang-standards project layout for cleaner deployments and orchestration.

- Repository: [golang-standards/project-layout](https://github.com/golang-standards/project-layout)
- Tags: how-to-guide
- Published: 2026-03-06

---

**Structure Go projects using the `golang-standards/project-layout` by placing entry points in `cmd/`, private logic in `internal/app/`, public libraries in `pkg/`, Docker assets in `build/package/`, and Kubernetes manifests in `deployments/` to maintain clean separation between source code, containerization, and orchestration.**

The `golang-standards/project-layout` repository provides an idiomatic blueprint for organizing Go code that scales naturally into containerized environments. By following these directory conventions, you create a predictable pipeline from local `go build` to Docker images to Kubernetes deployments without cluttering your application logic with infrastructure concerns.

## Core Directory Layout for Containerized Applications

The foundation of a Docker- and Kubernetes-ready Go project rests on four key directories that separate binaries, implementation details, public APIs, and container assets.

### Command Entry Points (`cmd/`)

Each application binary lives in its own subdirectory under `cmd/`. The `golang-standards/project-layout` specifies that `cmd/<app>/main.go` serves as the entry point that wires together packages from `internal/` and `pkg/`.

```go
// cmd/api/main.go
package main

import (
    "log"
    "myproject/internal/app"
)

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

```

This pattern allows multiple binaries (e.g., `cmd/api/`, `cmd/worker/`) to coexist in one repository, each building into a separate container image.

### Private Application Code (`internal/app/`)

Place your business logic and service implementations in `internal/app/<your_app>/`. The `internal/` prefix enforces the Go compiler's visibility rules, preventing external projects from importing these packages. This directory contains the HTTP handlers, database repositories, and domain logic that your `cmd/` binaries invoke.

### Public Libraries (`pkg/`)

Reusable packages intended for consumption by other projects belong in `pkg/<your_public_lib>/`. Unlike `internal/`, these packages are importable by external modules and should maintain stable APIs. Use this directory for utility libraries, middleware, or domain packages shared across multiple microservices.

## Containerization with Docker (`build/package/`)

According to the repository guidelines, Docker-related assets reside in `build/package/` (or alternatively at the repository root). This keeps build scripts, Dockerfiles, and container configurations isolated from source code.

### Multi-Stage Dockerfile

Create a `Dockerfile` in `build/package/` that compiles your Go binary in a builder stage and copies it into a minimal runtime image:

```dockerfile

# build/package/Dockerfile

FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /bin/api ./cmd/api

FROM gcr.io/distroless/static-debian12
COPY --from=builder /bin/api /api
EXPOSE 8080
ENTRYPOINT ["/api"]

```

Build the image from the repository root with:

```bash
docker build -f build/package/Dockerfile -t myorg/api:latest .

```

### Docker Compose for Local Stacks

The `deployments/` directory (detailed below) also accommodates local orchestration files. Place a [`docker-compose.yml`](https://github.com/golang-standards/project-layout/blob/main/docker-compose.yml) there to spin up dependencies alongside your application:

```yaml

# deployments/docker-compose.yml

version: "3.8"
services:
  api:
    build:
      context: ..
      dockerfile: build/package/Dockerfile
    ports:
      - "8080:8080"
    volumes:
      - ../configs/config.yaml:/etc/api/config.yaml:ro
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: devpassword

```

## Kubernetes Orchestration (`deployments/`)

The `deployments/` directory houses all infrastructure-as-code templates, including Helm charts, raw Kubernetes manifests, Kustomize overlays, and Terraform modules. This centralizes deployment logic and allows CI/CD pipelines to target a single directory for all environments.

### Helm Chart Structure

Organize Kubernetes resources using Helm under `deployments/helm/<chart>/`:

```yaml

# deployments/helm/api/Chart.yaml

apiVersion: v2
name: api
description: Go API service
version: 0.1.0
appVersion: "1.0"

```

```yaml

# deployments/helm/api/values.yaml

replicaCount: 3
image:
  repository: myorg/api
  tag: latest
  pullPolicy: IfNotPresent
configPath: /etc/api/config.yaml

```

```yaml

# deployments/helm/api/templates/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Chart.Name }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Chart.Name }}
  template:
    metadata:
      labels:
        app: {{ .Chart.Name }}
    spec:
      containers:
      - name: api
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: config
          mountPath: /etc/api
      volumes:
      - name: config
        configMap:
          name: {{ .Chart.Name }}-config

```

### Configuration Management (`configs/`)

External configuration files belong in `configs/`. Mount these into Kubernetes pods as ConfigMaps or Secrets, keeping sensitive data out of your container images:

```bash
kubectl create configmap api-config --from-file=configs/config.yaml

```

## Connecting the Layers: From Source to Cluster

The complete workflow follows this path:

1. **Develop** business logic in `internal/app/` and utilities in `pkg/`
2. **Expose** functionality through [`cmd/api/main.go`](https://github.com/golang-standards/project-layout/blob/main/cmd/api/main.go) which imports your internal packages
3. **Build** the container using `build/package/Dockerfile`, which compiles the `cmd/api` binary
4. **Deploy** using manifests in `deployments/helm/` or raw YAML that reference the image built in step 3
5. **Configure** via files in `configs/` mounted as ConfigMaps in Kubernetes

This structure ensures that changing your HTTP router requires no modifications to Docker or Kubernetes files, and scaling replicas in Kubernetes requires no changes to your Go source.

## Summary

- Place **entry points** in `cmd/<app>/main.go` to produce specific binaries for containerization
- Isolate **private logic** in `internal/app/` and **public libraries** in `pkg/` to control API boundaries
- Store **Docker configurations** in `build/package/` to keep container build logic separate from source code
- Centralize **deployment manifests** in `deployments/` for Helm charts, Kubernetes YAML, and Terraform
- Manage **configuration files** in `configs/` for easy mounting as ConfigMaps or Secrets in Kubernetes
- Use **multi-stage Docker builds** to compile Go binaries in a full toolchain image and run them in distroless containers

## Frequently Asked Questions

### Where should I put the Dockerfile in a Go project?

According to the `golang-standards/project-layout` repository, Docker-related files belong in `build/package/` (as documented in [[`build/README.md`](https://github.com/golang-standards/project-layout/blob/main/build/README.md)](https://github.com/golang-standards/project-layout/blob/master/build/README.md)). This directory may contain Dockerfiles, build scripts, and packaging metadata. Alternatively, you may place a `Dockerfile` at the repository root, but `build/package/` remains the preferred location for complex projects with multiple container images.

### What is the difference between `internal/` and `pkg/` directories?

The `internal/` directory contains packages that are implementation details specific to your application; the Go compiler enforces that code outside your module's tree cannot import these packages. Use `internal/app/` for your service layer and business logic. The `pkg/` directory contains packages intended for external consumption, such as client libraries or utility packages that other projects might import via Go modules. This distinction protects your internal APIs while advertising stable public interfaces.

### How do I handle environment-specific configuration in Kubernetes deployments?

Store template configuration files in the `configs/` directory (as described in [[`configs/README.md`](https://github.com/golang-standards/project-layout/blob/main/configs/README.md)](https://github.com/golang-standards/project-layout/blob/master/configs/README.md)). During deployment, load these files into Kubernetes as ConfigMaps or Secrets using `kubectl create configmap` or Helm's `Files.Get` function. Mount them into your containers at paths defined in your `deployments/` manifests, allowing the same container image to run in development, staging, and production by changing only the mounted configuration.

### Can I use docker-compose for local development with this layout?

Yes. Place [`docker-compose.yml`](https://github.com/golang-standards/project-layout/blob/main/docker-compose.yml) files in the `deployments/` directory alongside your Kubernetes templates, as the `deployments/` folder is intended for "container orchestration and deployment configurations" including Docker Compose, Helm, and Terraform. Reference the Dockerfile in `build/package/` using the `dockerfile` build context option to ensure local builds use the same multi-stage pipeline as your CI/CD system.