Deploy with Docker
Deploy with Docker
Section titled “Deploy with Docker”This guide shows how to containerize a Plumego service, configure it through environment variables, and connect its health endpoints to a Kubernetes-style readiness probe. The pattern applies whether you deploy on Kubernetes, Docker Compose, or any OCI-compatible runtime.
For the boundary rationale behind health endpoints, see Health and Readiness. For the graceful shutdown pattern the container runtime depends on, see Graceful Shutdown.
What this guide covers
Section titled “What this guide covers”- A minimal multi-stage Dockerfile for a Plumego service
- Environment variable conventions from
env.example - Wiring
/healthzand/readyzfor liveness and readiness probes - Docker Compose configuration for local validation
Step 1 — Write a minimal multi-stage Dockerfile
Section titled “Step 1 — Write a minimal multi-stage Dockerfile”Build the Go binary in a builder stage, then copy only the binary into a minimal runtime image.
# syntax=docker/dockerfile:1FROM golang:1.26-alpine AS builderWORKDIR /srcCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -o /out/service ./reference/standard-service
FROM gcr.io/distroless/static-debian12:nonrootCOPY --from=builder /out/service /serviceEXPOSE 8080ENTRYPOINT ["/service"]CGO_ENABLED=0 and distroless/static together produce an image with no shell, no libc, and no package manager. The binary is the only writable surface. If your service uses x/data with SQLite or any cgo-dependent driver, switch to gcr.io/distroless/base-debian12 and remove CGO_ENABLED=0.
Step 2 — Configure via environment variables
Section titled “Step 2 — Configure via environment variables”Plumego services read configuration from environment variables. The canonical variable names come from env.example at the repository root. The minimum set for a production container:
# NetworkAPP_ADDR=:8080
# Timeouts (milliseconds)APP_READ_TIMEOUT_MS=30000APP_WRITE_TIMEOUT_MS=30000APP_IDLE_TIMEOUT_MS=60000APP_SHUTDOWN_TIMEOUT_MS=5000
# ConcurrencyAPP_MAX_CONCURRENCY=256APP_QUEUE_DEPTH=512
# SecurityAPP_DEBUG=falseAUTH_TOKEN=<set-from-secrets-manager>Pass these via Docker --env-file, Kubernetes ConfigMap / Secret, or your platform’s secret management. Never bake secrets into the image.
Step 3 — Wire the health probes
Section titled “Step 3 — Wire the health probes”The reference service already exposes /healthz (liveness) and /readyz (readiness). Verify they respond before configuring the orchestrator:
docker run --rm -p 8080:8080 myapp:latest &curl http://localhost:8080/healthz # → {"status":"ok", ...}curl http://localhost:8080/readyz # → {"ready":true, ...}Both return JSON with a 200 on success and the readiness endpoint returns 503 when any registered ComponentChecker fails.
Step 4 — Configure Kubernetes probes
Section titled “Step 4 — Configure Kubernetes probes”Map /healthz to the liveness probe and /readyz to the readiness probe. Keep the liveness period generous — a liveness failure triggers a pod restart:
livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 30 failureThreshold: 3
readinessProbe: httpGet: path: /readyz port: 8080 initialDelaySeconds: 3 periodSeconds: 10 failureThreshold: 3During a rolling deployment, Kubernetes removes a pod from the load-balancer rotation as soon as its readiness probe fails. The graceful shutdown sequence — signal → drain → Shutdown — ensures in-flight requests complete before the pod exits.
Step 5 — Validate with Docker Compose
Section titled “Step 5 — Validate with Docker Compose”Use Docker Compose to test the full container configuration locally before pushing to a registry:
services: api: build: . ports: - "8080:8080" environment: APP_ADDR: ":8080" APP_DEBUG: "false" APP_SHUTDOWN_TIMEOUT_MS: "5000" healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:8080/readyz"] interval: 10s timeout: 5s retries: 3 start_period: 5sdocker compose up --builddocker compose ps # confirm health: healthydocker compose downWhat this pattern gives you
Section titled “What this pattern gives you”- The multi-stage build keeps the final image under 10 MB for a typical Plumego service.
- Environment variable configuration means the same image runs in every environment with no rebuild.
- Separate liveness and readiness probes match the Plumego health model: a dependency failure drains traffic without restarting the process.
APP_SHUTDOWN_TIMEOUT_MScontrols how long the process waits for in-flight requests to finish after receivingSIGTERM, which aligns with Kubernetes’terminationGracePeriodSeconds.
Complete example in the reference app
Section titled “Complete example in the reference app”The reference service ships a ready-to-use Dockerfile and docker-compose.yml:
reference/standard-service/Dockerfile— multi-stage build, distroless final image, non-root userreference/standard-service/docker-compose.yml— local compose setup with environment variable injection
Run it locally to verify the full build:
git clone https://github.com/spcent/plumegodocker build -t plumego-ref ./reference/standard-servicedocker run -p 8080:8080 plumego-refcurl http://localhost:8080/healthz