How I Reduced the Size of a Docker Image by 99.82%
When deploying an application, reducing the size of Docker images can be crucial for improving download times, network performance, and storage efficiency. In this article, we will explore several techniques to optimize a Docker image, using a simple “Hello World” program in Golang as an example.
Base Code
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello World")
}
Step 1: Basic Dockerfile — 276MB
Let’s start with a simple Docker image using Golang to compile and run the program.
FROM golang:1.23.1-alpine
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN go build -o main .
CMD ["./main"]
Step 2: Multi-stage Image — 247MB
By using multi-stage images, we can separate the compilation and execution phases, which helps exclude unnecessary files (like Go build tools) from the final image.
FROM golang:1.23.1-alpine AS builder
WORKDIR /app
COPY go.* ./
RUN go mod download && go mod verify
COPY . .
RUN go build -o main .
FROM golang:1.23.1-alpine
COPY --from=builder /app /
CMD ["./main"]
Step 3: Build Optimization — 1.38MB
Now, we optimize the build phase to generate a lighter binary. Here’s what we’re doing:
- CGO_ENABLED=0: Disables dependencies on C libraries, allowing us to create a static binary.
- GOARCH=amd64 and GOOS=linux: Specify that we are building for a Linux amd64 architecture.
- scratch: Using the scratch image, an empty image that contains nothing (no OS, no libraries), which is possible because of the static binary.
FROM golang:1.23.1-alpine AS builder
WORKDIR /app
COPY go.* ./
RUN go mod download && go mod verify
COPY . .
RUN CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -o main -a --trimpath --ldflags="-s -w" --installsuffix cgo
FROM scratch
COPY --from=builder /app /
CMD ["./main"]
Step 4: Binary Compression with UPX — 497KB
To go even further, we can use UPX, a utility for compressing executable files. This slightly increases build time but reduces the binary size even more.
FROM golang:1.23.1-alpine AS builder
RUN apk add --no-cache upx
WORKDIR /app
COPY go.* ./
RUN go mod download && go mod verify
COPY . .
RUN CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -o main -a --trimpath --ldflags="-s -w" --installsuffix cgo
RUN upx --ultra-brute -qq main && upx --t main
FROM scratch
COPY --from=builder /app /
CMD ["./main"]
By following these steps, we have reduced the image size from 276MB to just 497KB. These optimizations are particularly useful for deploying microservices or applications in resource-constrained environments (such as Kubernetes or lightweight containers).