Deploy to Kubernetes
Kubernetes is optional. The services are ordinary containers that read the environment and answer / and /, so anything that runs containers can run them. For Kubernetes, the repository ships one Helm chart in deploy/.
The chart doesn’t create a cluster or a database. Use any cluster: a managed one, or kind or minikube on your machine. Use a managed MySQL 9.7 or MongoDB replica set, or one you already run. The memory store is there to try the chart: it keeps data in the pod, so the chart refuses more than one replica per service with it.
Images
Section titled “Images”Each release builds multi-arch images (linux/ and linux/) to the GitHub Container Registry, with provenance and an SBOM:
| Image | Tags |
|---|---|
ghcr.io/ |
<version>-memory, <version>-mysql, <version>-mongodb |
ghcr.io/ |
<version>-memory, <version>-mysql, <version>-mongodb |
ghcr.io/ |
<version> |
The store is part of the tag because it is compiled in. The chart’s store value picks the matching variant for both services, and image.tag defaults to the chart’s version. The Go images run as a numeric non-root user on a distroless base, with nothing in them but the binary.
Building your own? The release workflow names the images after the GitHub repository it runs in, so a copy of the template publishes under its own name. Point the chart at it with image.registry and image.repository. If the registry is private, list the pull secret in imagePullSecrets: see Pull an image from a private registry.
Install
Section titled “Install”-
Create the secrets. The chart refers to Secrets you create by name and never templates one from values.
Value Keys auth.existingSecretsigning-key.pem(required),mysql-passwordwithstore: mysql,mongodb-uriwithstore: mongodbuser.existingSecretsmtp-password(optional),google-client-secretwithuser.google.clientID, and the same database keyTerminal window openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out signing-key.pemkubectl create secret generic gab-auth --from-file=signing-key.pem --from-literal=mysql-password=…kubectl create secret generic gab-user --from-literal=smtp-password=… --from-literal=mysql-password=… -
Write your values. The chart refuses to render without
auth.existingSecret,trustedProxies,user.smtp.addranduser.smtp.from, and, withstore: mysql, each service’smysql.addr. SetpublicURLto the origin the browser sees: the chart derivesISSUER,WEB_URLand the Google callback from it.trustedProxiesmust cover the web pods, usually the cluster’s pod CIDR, because every request reaches the services through them. Other settings from Environment variables go inauth.envanduser.env.ci/in the chart is a complete example, andmysql-values.yaml values.yamldescribes every value. -
Install or upgrade:
Terminal window helm upgrade --install my-api deploy/helm/go-api-boilerplate -f values.yamlWith
store: mysqlormongodb, andmigrations.enabled(the default), a pre-install and pre-upgrade hook runs a Job per service with the same image’smigratecommand before the Deployments roll: the MySQL migrations, or the MongoDB indexes. -
Check it:
Terminal window helm status my-apikubectl get podsA pod is ready when
/answersreadyz 200.
Routing
Section titled “Routing”By default the chart creates a Gateway API HTTPRoute (httpRoute.parentRefs and httpRoute.hostnames) that sends traffic to the web pods, which serve the app and forward / and / to the services, so the browser sees one origin. For a cluster without a Gateway API controller, set httpRoute.enabled: false and ingress.enabled: true, with ingress.className, ingress.host and ingress.tls.
The user service’s Connect port, 8081, is on its Service for auth to call inside the cluster; neither route exposes it.
Rotating the signing key
Section titled “Rotating the signing key”Services keep the auth service’s public keys for up to an hour, so publish a new key before signing with it:
-
Add the new key’s public half to a Secret named in
auth.previousKeysSecret, upgrade, and restart auth: it reads its keys only at start, and a changed Secret alone doesn’t roll the pods. The JWKS now lists the new key.Terminal window openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out new-signing-key.pemopenssl pkey -in new-signing-key.pem -pubout -out new.pemkubectl create secret generic gab-auth-previous --from-file=new.pem --dry-run=client -o yaml | kubectl apply -f -helm upgrade my-api deploy/helm/go-api-boilerplate -f values.yaml --set auth.previousKeysSecret=gab-auth-previouskubectl rollout restart deployment/my-api-auth -
Wait more than an hour, so every service has fetched it.
-
Put the new private key in
signing-key.pem, add the old key’s public half to the previous-keys Secret, and restart auth again. A previous key that is also the signing key is published once, sonew.pemcan stay. -
After another hour, remove the old public key and restart auth. Its last tokens expired 15 minutes after the switch.
What the chart sets for you
Section titled “What the chart sets for you”- Pods that run as non-root with the runtime’s default seccomp profile and no service-account token, and containers with a read-only root filesystem, no privilege escalation and every capability dropped.
- Liveness and readiness probes on
/andlivez /, and resource requests and limits you can change per component.readyz
The drain on shutdown is the services’ own: on SIGTERM, readiness reports 503 for SHUTDOWN_DELAY, so the cluster stops routing to the pod, and then in-flight requests get SHUTDOWN_TIMEOUT to finish.