In Part 8 , I ripped out SQLite and switched to Postgres. The database was real now β but it still only ran on my laptop.
Three terminal tabs. One for go run , one for Redis, one for Postgres. If I closed any of them, the whole thing fell over.
It was time to Dockerize.
The First Dockerfile: 800MB of Regret
My first attempt was a single-stage Dockerfile. Copy everything, build, run:
FROM golang:1.25-alpine WORKDIR /app COPY . . RUN go build -o server ./cmd/server CMD ["/app/server"]
Enter fullscreen mode
Exit fullscreen mode
It worked. The image was 800MB. That's bigger than most operating systems. I was shipping the entire Go compiler, my source code, and every dependency just to run a 15MB binary.
Multi-Stage Builds: The Aha Moment
The fix is a pattern called multi-stage builds. Two FROM statements in one Dockerfile:
# STAGE 1: Build (heavy β has Go compiler) FROM golang:1.25-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . .β¦