Deployment¶
Kubernetes manifests live in infra/. The image ships no config file — in-cluster configuration comes from GCTD_* environment variables and built-in defaults.
| Manifest | Purpose |
|---|---|
service.yaml |
gct-control ClusterIP, port 80 → container 8080 |
deployment-control.yaml |
the serve control plane (1 replica) |
deployment-worker.yaml |
the worker replicas (2 by default), sharing the cache PVC |
ingress.yaml |
traefik ingress routing a host to gct-control:80 |
Before kubectl apply -f infra/¶
The cluster must provide three things:
- A Postgres DSN as a secret. The app reads one DSN, not separate host/user/password. Both deployments reference
postgres-secretwith keydsn.
kubectl -n gocrap-tracker create secret generic postgres-secret \
--from-literal=dsn='postgres://gct_app:passw0rd@postgres.infra.svc:5432/crap_tracker?sslmode=disable'
- A shared repo-cache PVC. Workers share one cache directory and mount it
ReadWriteManyat/var/lib/go-crap-tracker, so clones and coverage profiles survive pod restarts and are shared across workers.
kubectl -n gocrap-tracker create pvc gct-repo-cache \
--access-mode=ReadWriteMany --storage-class=<rwxc> --size=20Gi
- NATS and Postgres reachable at
nats.infra.svc:4222andpostgres.infra.svc:5432— or changeGCTD_NATS_URL/ the DSN to match your cluster.
Environment variables¶
| Var | Control | Worker | Notes |
|---|---|---|---|
GCTD_NATS_URL |
✓ | ✓ | JetStream URL |
GCTD_DSN |
✓ | ✓ | from postgres-secret/dsn |
GCTD_SERVER_PORT |
✓ | — | defaults to 8080 |
GCTD_WORKER_ENGINE |
✓ | ✓ | local |
GCTD_MAX_STALE_MIN |
✓ | — | reconciler stale threshold (serve only) |
GCTD_CACHE_DIRECTORY |
— | ✓ | must be the PVC mount path |
Image¶
The Dockerfile is a multi-stage build. The final stage is a golang:1.24-alpine base on purpose: workers run git (clone/fetch) and go test -coverprofile as subprocesses, so both the Go toolchain and git must be present at runtime. The go-crap binary is installed into the image during the build, pinned to GOCRAP_VERSION.
Bump the go-crap version by editing GOCRAP_VERSION in the Makefile — it is passed to the image build as --build-arg and consumed by the Dockerfile's install.sh -v:
FROM golang:1.24-alpine AS builder
ARG GOCRAP_VERSION=v0.5.1
RUN apk add --no-cache git ca-certificates curl
RUN curl -fsSL https://padiazg.github.io/go-crap/install.sh | sh -s -- -v "$GOCRAP_VERSION"
# ... build ...
FROM golang:1.24-alpine
COPY --from=builder /out/go-crap-tracker /usr/local/bin/
COPY --from=builder /go/bin/go-crap /usr/local/bin/
ENTRYPOINT ["go-crap-tracker"]
CMD ["serve"]
Build and push the multi-arch image (arm64 + amd64, attestations off — the cluster's containerd rejects attestation manifests), then point the deployments at your registry tag:
or manually:
docker buildx build --provenance=false --sbom=false \
--platform linux/arm64,linux/amd64 \
--build-arg GOCRAP_VERSION=v0.5.1 \
-t registry.local.patodiaz.dev/go-crap-tracker:latest --push .
Scheduling memory bursts¶
A scan's go test can burst 1–2 GB of RAM. The shipped worker manifest therefore pins workers to the worker nodes (node affinity on kubernetes.io/hostname) and carries a hard memory limit (3000Mi), so a burst never competes with the control-plane node or the control app. On memory-constrained clusters (SBCs, small VPSs) this is what keeps a large-repo scan from taking the cluster down — and per-repo go_flags tunes the burst itself. See Constrained Environments.
Scaling¶
- Workers — scale
deployment-worker.yamlreplicasup or down. They share thecrap-workersconsumer and the cache PVC, so each additional replica simply takes more of the job queue and reuses the same clones/coverage. - Control plane — keep
serveat 1 replica. It owns the reconciler, the SSE relay, and the worker registry; running more than one would duplicate those singletons.
Health & probes¶
serve exposes GET /livez (liveness), GET /readyz (readiness — Postgres reachable + NATS connected), and GET /health (liveness alias). The control-plane deployment probes /readyz (after 5s) and /livez (after 20s); the Docker image HEALTHCHECK uses /health.
→ Known Issues for operational gotchas.