Docker and Go: Containerizing Applications from A to Z

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

Introduction: Why Docker and Go are the Perfect Pair?



Go (Golang) and Docker are two tools that have changed the approach to developing and deploying backend services. Go compiles into a single static binary file, and Docker allows you to package this binary into an isolated environment. Together, they provide minimalistic, fast, and secure containers that can run anywhere: from a developer's laptop to a production Kubernetes cluster.



In this article, we will go from zero to a production-ready Go application packaged in Docker. You will learn how to write a Dockerfile, optimize image size, use multi-stage builds, and run containers with a Go service.



1. Basics: Go Application and a Simple Dockerfile



Let's start with a simple HTTP server in Go. Create a file main.go:



package main

import ( "fmt" "net/http")

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

fmt.Println("Server is running on :8080") http.ListenAndServe(":8080", nil)}


Now let's write a minimal Dockerfile:



FROM golang:1.22-alpine AS builder

WORKDIR /appCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN go build -o server .

FROM alpine:3.19RUN apk --no-cache add ca-certificatesWORKDIR /root/COPY --from=builder /app/server .EXPOSE 8080CMD ["./server"]


Build the image and run the container:



docker build -t go-server .docker run -p 8080:8080 go-server


Now open your browser and go to http://localhost:8080 — you will see a greeting from the Go application running inside the container.



Why Multi-Stage Build?

In the first stage, we use the official Go image with a full set of tools for compilation. In the second stage, we use a minimal Alpine Linux, where only the ready binary is copied. This allows us to get an image size of about 15-20 MB instead of 500+ MB if we kept the full Go image.



2. Image Optimization: Minimizing Size and Build Speed



A small image is not only about saving space but also about faster loading and a smaller attack surface. Let's look at key techniques for Go applications.



2.1. Use .dockerignore

Create a .dockerignore file in the project root:



.git*.mdDockerfile.dockerignorenode_modules/test/


This prevents copying unnecessary files into the build context and speeds up the process.



2.2. Caching Dependencies

In our Dockerfile, we first copy only go.mod and go.sum, then run go mod download. Docker caches each layer, and as long as these files don't change, dependencies won't be reloaded with every code change.



2.3. CGO and Static Compilation

Go by default uses CGO for some operations (e.g., network operations via system calls). To get a fully static binary without dependencies on libc, use compilation flags:



RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server .


This guarantees that the binary will work on any Linux distribution, including minimal images like scratch.



2.4. Scratch — The Lightest Base Image

If your application does not require a shell, ping, or other utilities, use the empty scratch image:



FROM scratchCOPY --from=builder /app/server /EXPOSE 8080CMD ["/server"]


The size of such an image will be equal to the size of the binary itself — often less than 10 MB.



3. Docker Compose: Multi-Container Go Application



Real applications rarely consist of o

Blogs

Book Recommendations