Docker and Go: Containerizing Applications in Golang

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Docker и Go: контейнеризация приложений на Golang



In modern development, containerization has become the de facto standard for deploying and scaling applications. Docker and Go are an ideal pair for creating lightweight, fast, and secure microservices. Go compiles into a static binary, making it especially attractive for containerization: the final image can weigh just a few megabytes. In this article, we will cover how to properly containerize Go applications, optimize the Dockerfile, and avoid common mistakes.



Why Docker and Go are the perfect combination?



Go (Golang) was designed with modern cloud architectures in mind. Its key features are static compilation, built-in concurrency support, and no external runtime dependencies. Docker, in turn, provides an isolated environment for running applications. Together, they allow you to create images that:



  • Take up minimal space — a Go application image can be smaller than 10 MB.
  • Start quickly — a static binary does not require an interpreter or virtual machine.
  • Scale easily — ideal for orchestration in Kubernetes.
  • Are more secure — a minimal image reduces the attack surface.


Creating a simple Go application for containerization



To start, let's write a simple web application in Go that we will containerize. Create a file main.go:



package main

import ( "fmt" "log" "net/http" "os")

func main() { port := os.Getenv("PORT") if port == "" { port = "8080" }

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go in Docker! 🐳") })

log.Printf("Server starting on port %s", port) log.Fatal(http.ListenAndServe(":"+port, nil))}


This application listens for HTTP requests and returns a greeting message. The port can be set via the PORT environment variable.



Writing a Dockerfile for a Go application



Now let's create a Dockerfile. We'll start with a basic approach and then optimize it.



Basic Dockerfile (without optimization)



FROM golang:1.22-alpine AS builder

WORKDIR /app

COPY go.mod go.sum ./RUN go mod download

COPY . .RUN go build -o main .

FROM alpine:latest

WORKDIR /root/

COPY --from=builder /app/main .

EXPOSE 8080

CMD ["./main"]


This Dockerfile uses a multi-stage build — one of the best patterns for Go. The first stage (builder) compiles the application, the second (final image) contains only the built binary. This approach avoids including the Go compiler and tools in the final image.



Optimized Dockerfile (best practices)



To make the image even smaller and more secure, we use scratch — an empty image without a shell or utilities:



FROM golang:1.22-alpine AS builder

WORKDIR /app

COPY go.mod go.sum ./RUN go mod download

COPY . .

# Disable CGO, enable optimizationsRUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o main .

FROM scratch

WORKDIR /root/

COPY --from=builder /app/main .

# Add root certificates for HTTPSCOPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

EXPOSE 8080

CMD ["./main"]


Key optimizations:

  • CGO_ENABLED=0 — disables CGO, which reduces the binary size.
  • -ldflags="-s -w" — removes the symbol table and debug information.
  • scratch — a minimal image without unnecessary layers.
  • Copying ca-certificates.crt — necessary for working with HTTPS.

Blogs

Book Recommendations