ALPHA RELEASE (v0.1) — Aixgo is in active development. Not all features are complete. Production release planned for late 2025. Learn more →
← Back to Guides

Building Docker Images from Scratch

Create minimal 8MB production containers leveraging Aixgo's single binary advantage.

One of Aixgo’s most compelling advantages is deployment size. While Python AI frameworks produce 1GB+ containers, Aixgo agents compile to 8MB binaries. This guide shows how to build minimal production containers.

The Container Size Problem

Python-based AI frameworks create massive containers:

# Python AI service - typical Dockerfile
FROM python:3.11
COPY requirements.txt .
RUN pip install -r requirements.txt

# Result: 1.2GB+ container
# - Python runtime: 900MB
# - Dependencies: 300MB+
# - Your code: <1MB

Problems:

  • Slow deployments (minutes to pull 1GB)
  • High storage costs
  • Large attack surface
  • Slow cold starts (30-45 seconds)

Aixgo’s Solution: FROM scratch

Go compiles to static binaries with zero runtime dependencies:

# Aixgo service - minimal Dockerfile
FROM scratch
COPY aixgo-agent /
CMD ["/aixgo-agent"]

# Result: 8MB total container
# - Go binary: 8MB
# - No runtime needed
# - No dependencies

Benefits:

  • Fast deployments (seconds to pull 8MB)
  • Minimal storage costs
  • Tiny attack surface
  • Instant cold starts (<100ms)

Basic Dockerfile: Single Binary

The simplest production Dockerfile:

FROM golang:1.21 AS builder

WORKDIR /app

# Copy go mod files
COPY go.mod go.sum ./
RUN go mod download

# Copy source code
COPY . .

# Build static binary
RUN CGO_ENABLED=0 GOOS=linux go build -o agent main.go

# Runtime stage
FROM scratch

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

# Copy config (optional)
COPY --from=builder /app/config/ /config/

CMD ["/agent"]

Build and run:

docker build -t aixgo-agent:latest .
docker run aixgo-agent:latest

Result: ~8MB container

Optimized Dockerfile: Smallest Possible Image

Further optimization with build flags:

FROM golang:1.21-alpine AS builder

WORKDIR /app

# Copy dependencies
COPY go.mod go.sum ./
RUN go mod download

# Copy source
COPY . .

# Build with optimizations
RUN CGO_ENABLED=0 GOOS=linux go build \
    -ldflags="-w -s" \
    -trimpath \
    -o agent \
    main.go

# Runtime: scratch (0MB base)
FROM scratch

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

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

# Copy config
COPY --from=builder /app/config/ /config/

CMD ["/agent"]

Build flags explained:

  • CGO_ENABLED=0 - Disable C dependencies (pure Go)
  • -ldflags="-w -s" - Strip debug info and symbol table
  • -trimpath - Remove file system paths from binary
  • Result: ~6-8MB container

Multi-Stage Build Best Practices

Stage 1: Builder (golang:alpine)

Use Alpine for smaller build stage:

FROM golang:1.21-alpine AS builder

# Install build dependencies (if needed)
RUN apk add --no-cache git

WORKDIR /app

# Layer caching: dependencies first
COPY go.mod go.sum ./
RUN go mod download

# Then copy source (changes more frequently)
COPY . .

# Build
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o agent main.go

Stage 2: Runtime (scratch)

Minimal runtime with only essentials:

FROM scratch

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

# Copy timezone data (if needed)
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo

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

# Copy config
COPY config/ /config/

# Non-root user (security best practice)
USER 65534:65534

CMD ["/agent"]

Handling External Dependencies

CA Certificates (for HTTPS)

If your agent calls external APIs:

FROM scratch

# Required for HTTPS calls to LLM providers
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

COPY --from=builder /app/agent /agent
CMD ["/agent"]

Timezone Data

If your agent uses timezone-aware date/time:

FROM scratch

# For time.LoadLocation()
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
ENV TZ=UTC

COPY --from=builder /app/agent /agent
CMD ["/agent"]

Configuration Files

Mount configs as volumes or copy at build time:

# Option 1: Copy at build time
COPY config/agents.yaml /config/agents.yaml

# Option 2: Mount as volume (more flexible)
# docker run -v ./config:/config aixgo-agent

Size Comparison: Python vs Aixgo

Real-world example: data analysis agent

Python (LangChain)

FROM python:3.11

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

CMD ["python", "main.py"]

requirements.txt:

langchain==0.1.0
openai==1.0.0
pandas==2.1.0
numpy==1.24.0
# ... 50+ more dependencies

Result:

  • Base image: 900MB
  • Dependencies: 400MB
  • Total: 1.3GB

Aixgo

FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o agent main.go

FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/agent /agent
COPY --from=builder /app/config/ /config/
CMD ["/agent"]

Result:

  • Base image: 0MB (scratch)
  • Binary: 8MB
  • Total: 8MB

Improvement: 150x smaller

Security Hardening

Run as Non-Root User

FROM scratch

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

# Copy passwd file for non-root user
COPY --from=builder /etc/passwd /etc/passwd

# Run as nobody (UID 65534)
USER 65534:65534

CMD ["/agent"]

Read-Only Filesystem

# Dockerfile
FROM scratch
COPY --from=builder /app/agent /agent
USER 65534:65534
CMD ["/agent"]
# Run with read-only root filesystem
docker run --read-only aixgo-agent:latest

Minimal Attack Surface

FROM scratch has:

  • No shell
  • No package manager
  • No utilities
  • No OS files

Only your binary exists. Nothing else to exploit.

Cloud-Specific Optimizations

Google Cloud Run

FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o agent main.go

FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/agent /agent
COPY config/ /config/

# Cloud Run sets PORT env var
CMD ["/agent"]

Deploy:

gcloud run deploy aixgo-agent \
  --image gcr.io/my-project/aixgo-agent \
  --platform managed \
  --memory 512Mi \
  --max-instances 10

AWS Lambda

For Lambda, use AWS Lambda Go runtime:

FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-w -s" -o bootstrap main.go

FROM scratch
COPY --from=builder /app/bootstrap /bootstrap
COPY config/ /config/
ENTRYPOINT ["/bootstrap"]

Package and deploy:

zip function.zip bootstrap config/*
aws lambda create-function \
  --function-name aixgo-agent \
  --runtime provided.al2 \
  --handler bootstrap \
  --zip-file fileb://function.zip

Build Optimization Techniques

Layer Caching

Order Dockerfile commands from least to most frequently changed:

FROM golang:1.21-alpine AS builder
WORKDIR /app

# 1. Dependencies (cached unless go.mod changes)
COPY go.mod go.sum ./
RUN go mod download

# 2. Source code (changes frequently)
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o agent main.go

Multi-Platform Builds

Build for multiple architectures:

docker buildx create --use
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t my-registry/aixgo-agent:latest \
  --push \
  .

Build-Time Variables

Inject version info at build time:

ARG VERSION=dev
ARG GIT_COMMIT=unknown

RUN CGO_ENABLED=0 go build \
  -ldflags="-w -s -X main.Version=${VERSION} -X main.GitCommit=${GIT_COMMIT}" \
  -o agent \
  main.go
docker build \
  --build-arg VERSION=1.0.0 \
  --build-arg GIT_COMMIT=$(git rev-parse HEAD) \
  -t aixgo-agent:1.0.0 \
  .

Debugging Minimal Containers

Problem: No Shell in scratch

You can’t docker exec into a scratch container (no shell).

Solution 1: Debug build with shell

# Production
FROM scratch AS production
COPY --from=builder /app/agent /agent
CMD ["/agent"]

# Debug (with shell)
FROM alpine:latest AS debug
COPY --from=builder /app/agent /agent
CMD ["/bin/sh"]

Build debug version:

docker build --target debug -t aixgo-agent:debug .
docker run -it aixgo-agent:debug /bin/sh

Solution 2: Logging

Use structured logging to stdout:

import "log/slog"

func main() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
    slog.SetDefault(logger)

    slog.Info("Starting agent", "version", Version)
    // ...
}

View logs:

docker logs -f <container-id>

Performance Impact

Container size affects:

Metric1.2GB (Python)8MB (Aixgo)Impact
Pull time2-5 minutes5-10 seconds24-60x faster
Cold start30-45 seconds<100ms300-450x faster
Storage cost$0.10/GB/month$0.001/GB/month100x cheaper
Deploy frequencySlow (discouraged)Fast (encouraged)Higher velocity

Real-World Example: Minimal Production Dockerfile

Complete production-ready Dockerfile:

# Build stage
FROM golang:1.21-alpine AS builder

# Install certificates
RUN apk add --no-cache ca-certificates git

WORKDIR /app

# Dependencies
COPY go.mod go.sum ./
RUN go mod download

# Source
COPY . .

# Build with all optimizations
RUN CGO_ENABLED=0 GOOS=linux go build \
    -ldflags="-w -s -X main.Version=${VERSION:-dev}" \
    -trimpath \
    -o agent \
    main.go

# Runtime stage
FROM scratch

# Metadata
LABEL maintainer="your-team@company.com"
LABEL version="1.0.0"

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

# Copy timezone data
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo

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

# Copy config
COPY config/ /config/

# Non-root user
USER 65534:65534

# Health check (if HTTP server)
HEALTHCHECK --interval=30s --timeout=3s \
  CMD ["/agent", "--health-check"]

CMD ["/agent"]

Build:

docker build -t my-registry/aixgo-agent:1.0.0 .
docker push my-registry/aixgo-agent:1.0.0

Key Takeaways

  1. FROM scratch - Smallest possible base (0MB)
  2. Multi-stage builds - Keep builder separate from runtime
  3. Static binaries - CGO_ENABLED=0 for zero dependencies
  4. Strip symbols - -ldflags="-w -s" reduces size
  5. Layer caching - Dependencies before source code
  6. 150x smaller - 8MB vs 1.2GB Python containers

Next Steps