Subscribe to my channel for Engineering videos @bitsofmandal
Packaging applications with Docker is standard practice today β but for production-ready Go apps, image size, startup time, and security matter. In this post, weβll walk through how to containerize a Go application, then progressively optimize the image size from 45MB to just 4MB using tried-and-tested techniques.
π Why Containerize Golang Apps?
- Go produces statically linked binaries β ideal for minimal containers.
- Containerized apps run reliably across different environments.
- Optimized containers improve deployment speed, resource usage, and security.
π οΈ What Youβll Learn
- How to containerize a Golang app with Docker.
- Multi-stage builds and their benefits.
- Reducing image size with Go build flags.
- Using minimal base images (
scratch
). - Compressing binaries with UPX.
- Real-world deployment tips and optimizations.
π Step 1: Basic Dockerfile
Letβs start simple β a basic Dockerfile using the official Go image:
FROM golang:1.24.3
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o server ./cmd/main.go
EXPOSE 8080
CMD ["./server"]
Image size: π³ ~45MB
β
Works fine for development, but not production-friendly.
π Step 2: Multi-Stage Build
Now, letβs reduce size by separating build-time and runtime environments:
FROM golang:1.24.3 AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o server ./cmd/main.go
FROM golang:1.24.3
WORKDIR /app
COPY --from=build /app/server /server
EXPOSE 8080
CMD ["./server"]
Image size: π³ ~38MB
β
Keeps runtime image clean β no build tools.
π Step 3: Minimal Base Image (scratch)
Use a minimal base image like scratch for further optimization:
FROM golang:1.24.3 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o server ./cmd/main.go
FROM scratch
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]
Image size: π³ ~17MB
β
Only includes your Go binary β nothing else.
Note: Requires statically linked binaries (CGO_ENABLED=0).
π Step 4: Go Build Flags
Use Go build flags to strip debugging info:
go build -ldflags="-s -w" -o server ./cmd/main.go
Image size: π³ ~12MB
β
Smaller binaries, faster startup.
π Step 5: UPX Compression
Compress your binary with UPX:
upx --ultra-brute --quiet server
Image size: π³ ~4MB
β
Drastically reduced size, with negligible startup overhead.
π¦ Final Optimized Dockerfile
FROM golang:1.24.3-alpine AS build
WORKDIR /app
RUN apk add --no-cache upx
COPY go.mod go.sum ./
RUN go mod download && go mod verify
COPY . .
RUN CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -v \
-o server \
-ldflags="-s -w" \
-installsuffix cgo \
./cmd/main.go
RUN upx --ultra-brute --quiet server && upx --test server
FROM scratch
COPY --from=build /app/server /server
ENTRYPOINT ["/server"]
Final image size: π³ 4MB
π Image Size Summary
Optimization Level | Image Size |
---|---|
Basic Golang + Alpine | 45MB |
+ -ldflags="-s -w"
|
38MB |
+ scratch
|
17MB |
+ UPX | 4MB |
π‘ Bonus Tips
- Use layer caching for faster CI/CD builds.
- Include SSL certificates if using scratch.
- Gracefully handle signals for safe shutdowns.
- Define Docker health checks.
- Integrate security scanning tools.
- Measure container performance metrics post-deployment.
π Real-World Impact
- Lower cloud storage and registry costs
- Faster container startup times
- Better scaling performance
- Improved security with minimal attack surface
π― Recap: 4 Key Optimization Techniques
- β Multi-stage builds to separate build and runtime.
- β Go build flags (-ldflags="-s -w") for smaller binaries.
- β Using scratch for minimal base images.
- β UPX for ultra-light binary compression.
Top comments (0)