smithery/laurigates

go-containers

Go container optimization — scratch/distroless images, static binaries, CGO, ldflags, trimpath (846MB to 2.5MB). Use when working with Go containers or optimizing Go image sizes.

Installation

$ npx skills add smithery/laurigates --skill go-containers

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/laurigates · top by installs.

npx skills add smithery/laurigates

Browse all from smithery/laurigates

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Allowed toolsBash, Read, Grep, Glob, Edit, Write, TodoWrite, WebSearch, WebFetch

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 10,951 B
  • docs SUMMARY.md 402 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Go Container Optimization

Expert knowledge for building minimal, secure Go container images using static compilation, scratch/distroless base images, and Go-specific build optimizations.

When to Use This Skill

Use this skill when... Use container-development instead when...
Building or auditing a Dockerfile for a Go application Writing language-agnostic multi-stage build patterns
Choosing between scratch, distroless, and Alpine for a Go binary Hardening any container against non-root / least-privilege standards
Driving down a Go image from hundreds of MB to single-digit MB Composing services with Docker Compose
Resolving CGO / static-link issues specific to Go Optimizing a Node.js (nodejs-containers) or Python (python-containers) image

Core Expertise

Go's Unique Advantages:

  • Compiles to single static binary (no runtime dependencies)
  • Can run on scratch base (literally empty image)
  • No interpreter, VM, or system libraries needed at runtime
  • Enables smallest possible container images (2-5MB)

Key Capabilities:

  • Static binary compilation with CGO_ENABLED=0
  • Binary stripping with ldflags (-w, -s)
  • Scratch and distroless base images
  • CA certificate handling for HTTPS
  • CGO considerations when C libraries are required

The Optimization Journey: 846MB → 2.5MB

This demonstrates systematic optimization achieving 99.7% size reduction:

Step 1: The Problem - Full Debian Base (846MB)

# ❌ BAD: Includes full Go toolchain, Debian system, unnecessary tools
FROM golang:1.23
WORKDIR /app
COPY . .
RUN go build -o main .
EXPOSE 8080
CMD ["./main"]

Issues:

  • Full Debian base (~116MB)
  • Complete Go compiler and toolchain (~730MB)
  • System libraries, package managers, shells
  • None of this is needed at runtime

Image size: 846MB

Step 2: Switch to Alpine (312MB)

# ✅ BETTER: Alpine reduces OS overhead
FROM golang:1.23-alpine
WORKDIR /app
COPY . .
RUN go build -o main .
EXPOSE 8080
CMD ["./main"]

Improvements:

  • Alpine Linux (~5MB base vs ~116MB Debian)
  • Still includes full Go toolchain (unnecessary at runtime)

Image size: 312MB (63% reduction)

Step 3: Multi-Stage Build (15MB)

# ✅ GOOD: Separate build from runtime
# Build stage
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o main .

# Runtime stage
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]

Improvements:

  • Build artifacts excluded from final image
  • Only Alpine + binary in final image
  • Faster deployments and pulls

Image size: 15MB (95% reduction from 312MB)

Step 4: Strip Binary & Optimize Build Flags (8MB)

# ✅ BETTER: Optimized build flags
# Build stage
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .

# Optimized build with stripping
RUN CGO_ENABLED=0 GOOS=linux go build \
    -a \
    -installsuffix cgo \
    -ldflags="-w -s" \
    -trimpath \
    -o main .

# Runtime stage with CA certificates
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /app
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]

Build Flag Explanations:

Flag Purpose Impact
CGO_ENABLED=0 Disable CGO, create static binary Removes dynamic library dependencies
-a Force rebuild of all packages Ensures clean build
-installsuffix cgo Separate output directory Prevents cache conflicts
-ldflags="-w -s" Strip debug info and symbol table Reduces binary size significantly
-w Omit DWARF debug information ~30% size reduction
-s Omit symbol table and debug info Additional ~10% reduction
-trimpath Remove file system paths Security: no local path leakage

Additions:

  • CA certificates for HTTPS requests
  • Fully static binary (no dynamic dependencies)

Image size: 8MB (47% reduction from 15MB)

Step 5: Scratch or Distroless (2.5MB)

Option A: Scratch (Absolute Minimum)

# Build stage
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .

# Optimized static build
RUN CGO_ENABLED=0 GOOS=linux go build \
    -a \
    -installsuffix cgo \
    -ldflags="-w -s" \
    -trimpath \
    -o main .

# Runtime stage - scratch (empty image)
FROM scratch

# Copy CA certificates from builder
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

# Copy binary
COPY --from=builder /app/main /main

EXPOSE 8080
CMD ["/main"]

Image size: 2.5MB (68% reduction from 8MB)

Option B: Distroless (Slightly Larger, Easier Debugging)

# Build stage (same as above)
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
    -a \
    -ldflags="-w -s" \
    -trimpath \
    -o main .

# Runtime stage - distroless
FROM gcr.io/distroless/static-debian12

COPY --from=builder /app/main /main
EXPOSE 8080
CMD ["/main"]

Distroless advantages:

  • Includes CA certificates automatically
  • Minimal OS files (passwd, nsswitch.conf)
  • No shell (security benefit)
  • Slightly larger (~2-3MB more) but includes useful metadata

Image size: ~4-5MB

Performance Impact

Metric Debian (846MB) Alpine (15MB) Scratch (2.5MB) Improvement
Image Size 846MB 15MB 2.5MB 99.7% reduction
Pull Time 52s 4s 1s 98% faster
Build Time 3m 20s 2m 15s 1m 45s 47% faster
Startup Time 2.1s 1.2s 0.8s 62% faster
Memory Usage 480MB 180MB 128MB 73% reduction
Storage Cost $0.48/mo $0.01/mo $0.001/mo 99.8% reduction

Security Impact

Image Type Vulnerabilities Attack Surface
Debian-based 63 CVEs Full OS, shell, package manager, utilities
Alpine-based 12 CVEs Minimal OS, shell, package manager
Scratch 0 CVEs Binary only, no OS
Distroless 0-2 CVEs Binary + minimal runtime, no shell

Security benefits of scratch/distroless:

  • No shell → no shell injection attacks
  • No package manager → no supply chain attacks
  • No OS utilities → minimal attack surface
  • No unnecessary libraries → fewer vulnerabilities

When to Use Each Approach

Use Case Recommended Base Reason
Production services Scratch or Distroless Minimal size, maximum security
Services with C dependencies Alpine (with CGO) Requires system libraries
Development/debugging Alpine Need shell access for troubleshooting
Legacy apps Debian slim Compatibility requirements

Go-Specific .dockerignore

# Version control
.git
.gitignore
.gitattributes

# Go artifacts
vendor/
*.exe
*.exe~
*.dll
*.so
*.dylib
*.test
*.out
go.work
go.work.sum

# Development files
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store

# Documentation
README.md
*.md
docs/
LICENSE

# CI/CD
.github/
.gitlab-ci.yml
.travis.yml
Jenkinsfile

# Environment files
.env
.env.*
*.env

# Build artifacts
dist/
build/
bin/
tmp/
temp/

# Logs
*.log
logs/

# Test files (if not needed in image)
*_test.go
testdata/

# Docker files
Dockerfile*
docker-compose*.yml
.dockerignore

Binary Size Analysis

# Build with different optimization levels
go build -o main-default .
go build -ldflags="-w" -o main-w .
go build -ldflags="-s" -o main-s .
go build -ldflags="-w -s" -o main-ws .

# Compare sizes
ls -lh main-*

# Typical results for a medium Go app:
# main-default: 12.5MB (with debug info + symbols)
# main-w:        8.7MB (no debug info)
# main-s:       10.2MB (no symbol table)
# main-ws:       7.8MB (both stripped)

# Further analyze binary
go tool nm main-default | wc -l  # Count symbols
file main-ws                      # Verify static linking
ldd main-ws                       # Should show "not a dynamic executable"

Advanced: CGO Considerations

When your Go application uses CGO (C libraries, database drivers like SQLite, etc.), you cannot use scratch base images.

# When CGO is required (database drivers, C libraries)
FROM golang:1.23-alpine AS builder

# Install C dependencies
RUN apk add --no-cache gcc musl-dev

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

# Build with CGO enabled
RUN CGO_ENABLED=1 GOOS=linux go build \
    -ldflags="-w -s -linkmode external -extldflags '-static'" \
    -o main .

# Runtime needs musl
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/main .
CMD ["./main"]

Note: With CGO, Alpine is the minimal option (~7-10MB final image).

Common Go Database Drivers

Driver CGO Required Minimum Base
github.com/lib/pq (PostgreSQL) No Scratch
github.com/go-sql-driver/mysql No Scratch
github.com/mattn/go-sqlite3 Yes Alpine
modernc.org/sqlite (pure Go) No Scratch

Agentic Optimizations

Go-specific container commands for fast development:

Context Command Purpose
Quick build go build -ldflags="-w -s" -o app . Build stripped binary
Check size ls -lh app Verify binary size
Test static ldd app Verify no dynamic deps
Container build DOCKER_BUILDKIT=1 docker build -t app . Fast build with cache
Size check docker images app --format "{{.Size}}" Check final image size
Layer analysis docker history app:latest --human See layer sizes

Best Practices

Always:

  • Use CGO_ENABLED=0 unless you need C libraries
  • Strip binaries with -ldflags="-w -s"
  • Use -trimpath to remove filesystem paths
  • Prefer scratch or distroless for production
  • Version pin Go and base images (never use latest)

Never:

  • Ship the Go compiler in production images
  • Use full Debian/Ubuntu bases for Go apps
  • Include debug symbols in production binaries
  • Run as root user (even in scratch, use USER 65534)

Related Skills

  • container-development - General container patterns, multi-stage builds, security
  • nodejs-containers - Node.js-specific container optimizations
  • python-containers - Python-specific container optimizations