Click To Jump to your Favorite Topic Bellow:
- Beginner: Why Docker + Go?
- Your First Go Container
- Intermediate: Optimizing Builds & Caching
- Multi-Stage Builds (The Right Way)
- Distroless & Scratch (Minimal Images)
- Advanced: Debugging, Profiling, & Signal Handling
- Expert: Go Modules, Vendoring, & CI/CD Pipelines
- Expert: Docker Compose for Go Microservices
- Expert to God: Kubernetes, Init Containers, & Sidecars
- Security & Best Practices Checklist
- Final Expert Wisdom
Beginner: Why Docker + Go?
Why Docker + Go?
- Tiny images (even 5–10 MB)
- Fast startup (microseconds)
- Predictable memory usage
- No "works on my machine" bugs
Install Docker
Skip if Docker is already installed.
Mac / Windows
Docker Desktop
Linux
sudo apt install docker.io
Verify installation:
docker --version
Your First Go Container
2.1 Simple Go Program
main.go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, Docker!")
})
http.ListenAndServe(":8080", nil)
}
2.2 Write a Dockerfile
Dockerfile
FROM golang:1.22
WORKDIR /app
COPY go.mod go.sum* ./
RUN go mod download
COPY . .
RUN go build -o myapp .
EXPOSE 8080
CMD ["./myapp"]
2.3 Build & Run
docker build -t go-web .
docker run -p 8080:8080 go-web
Visit:
http://localhost:8080
2.4 Basic Commands
docker ps
docker stop <container>
docker rm <container>
docker images
docker rmi go-web
Intermediate: Optimizing Builds & Caching
3.1 Leverage Docker Layer Caching
Dependencies change less frequently than application code.
✅ Good
COPY go.mod go.sum ./
RUN go mod download
COPY . .
❌ Bad
COPY . .
RUN go mod download
3.2 Use .dockerignore
.git
*.log
tmp/
*.test
coverage.out
Dockerfile
README.md
3.3 Build with Tags & Versions
docker build -t myapp:1.0.0 .
docker tag myapp:1.0.0 myapp:latest
Multi-Stage Builds (The Right Way)
Go binaries don't need the Go compiler at runtime.
4.1 Two-Stage Dockerfile
## Stage 1: Build
FROMgolang:1.22ASbuilder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux \
go build -ldflags="-s -w" \
-o myapp .
## Stage 2: Run
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /build/myapp .
EXPOSE 8080
CMD ["./myapp"]
Build Flags Explained
-
CGO_ENABLED=0→ Pure Go binary -
GOOS=linux→ Linux target -
-ldflags="-s -w"→ Strip debug symbols
Size Comparison
| Image | Approx Size |
|---|---|
| golang:1.22 | ~800 MB |
| Alpine + Binary | ~12 MB |
4.2 Even Smaller: Scratch
FROM scratch
COPY --from=builder /build/myapp /myapp
EXPOSE 8080
ENTRYPOINT ["/myapp"]
Works when using CGO_ENABLED=0.
Distroless & Scratch (Minimal Images)
5.1 Google Distroless
FROM gcr.io/distroless/static-debian12
COPY --from=builder /build/myapp /myapp
ENTRYPOINT ["/myapp"]
Benefits
- Includes CA certificates
- Includes timezone data
- No shell
- No package manager
- Reduced attack surface
5.2 Adding TLS to Scratch
FROM scratch
COPY --from=builder \
/etc/ssl/certs/ca-certificates.crt \
/etc/ssl/certs/
COPY --from=builder /build/myapp /myapp
ENTRYPOINT ["/myapp"]
Advanced: Debugging, Profiling, & Signal Handling
6.1 Graceful Shutdown (SIGTERM)
func main() {
srv := &http.Server{Addr: ":8080"}
go func() {
if err := srv.ListenAndServe(); err != nil &&
err != http.ErrServerClosed {
log.Fatal(err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(
quit,
syscall.SIGINT,
syscall.SIGTERM,
)
<-quit
ctx, cancel := context.WithTimeout(
context.Background(),
5*time.Second,
)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal(err)
}
}
6.2 Debugging with Delve
FROM golang:1.22
RUN go install github.com/go-delve/delve/cmd/dlv@latest
CMD [
"dlv",
"debug",
"--headless",
"--listen=:40000",
"--api-version=2",
"--accept-multiclient"
]
Run:
docker run \
-p 40000:40000 \
-p 8080:8080 \
debug-image
6.3 Profiling with pprof
import _ "net/http/pprof"
go tool pprof \
http://localhost:6060/debug/pprof/heap
Expert: Go Modules, Vendoring, & CI/CD Pipelines
7.1 Vendoring
go mod vendor
COPY vendor ./vendor
RUN go build -mod=vendor -o myapp .
7.2 GitHub Actions
name: Build and Push
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- uses: docker/build-push-action@v5
with:
push: true
tags: user/app:latest
cache-from: type=gha
cache-to: type=gha,mode=max
7.3 Build Args
ARG VERSION=dev
RUN go build \
-ldflags="-X main.version=$VERSION" \
-o myapp .
docker build \
--build-arg VERSION=1.2.3 \
-t myapp .
Expert: Docker Compose for Go Microservices
version: "3.8"
services:
api:
build: .
ports:
- "8080:8080"
environment:
- DB_HOST=postgres
- REDIS_ADDR=redis:6379
depends_on:
- postgres
- redis
restart: unless-stopped
postgres:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379"
volumes:
pgdata:
Run:
docker compose up -d
Expert to God: Kubernetes, Init Containers, & Sidecars
9.1 Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-app
spec:
replicas: 3
selector:
matchLabels:
app: go-app
template:
metadata:
labels:
app: go-app
spec:
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 8080
9.2 Init Containers
initContainers:
- name: migrate
image: migrate/migrate
9.3 Sidecar Pattern
containers:
- name: go-app
image: myapp
- name: log-shipper
image: fluent/fluent-bit
Security & Best Practices Checklist
| Area | Recommendation |
|---|---|
| Base Image | Use scratch, distroless, or alpine |
| User | USER 10001 |
| Read-only Root | readOnlyRootFilesystem: true |
| Secrets | Use env vars or secret managers |
| Scanning | trivy image myapp |
| Labels | OCI labels |
| Health Checks | Add HEALTHCHECK |
| Timeouts | Configure stop timeout |
| Resources | Limit CPU and memory |
Final Expert Wisdom
- Use
CGO_ENABLED=0unless you require native C libraries. - Prefer
scratchwhen possible. - Never store persistent data inside container filesystems.
- Drop unnecessary Linux capabilities.
- Use health checks everywhere.
- Keep images small.
- Treat containers as immutable.
You now know everything from docker run to orchestrating 10,000 Go containers on Kubernetes.
Go forth and containerize.
For further actions, you may consider blocking this person and/or reporting abuse
