Summary
Add a first-class Helm chart for deploying the spur-cloud control plane (API + frontend + optional in-cluster Postgres + RBAC + ingress).
Motivation
Today deploy/k8s/ ships raw manifests applied with kubectl apply -f. This works for the first install but has several real gaps:
:latest image refs everywhere, no semver / pull-policy story.
- Secrets are created imperatively (
kubectl create secret) and the jwt_secret lives in a ConfigMap with a CHANGEME placeholder — fine for demos, a footgun for prod.
- Per-environment tweaks (ingress host, replica counts, resource limits, auth provider toggles, external Postgres) require hand-editing YAML.
- No upgrade/rollback semantics —
kubectl apply doesn't track removals.
- Hard to declare as a dependency from upstream tooling (Argo CD, Flux, parent charts that want to install spur-cloud as a subchart).
A Helm chart removes all of the above and is the standard way to package a Kubernetes workload that other teams or environments will consume.
Proposed scope (MVP)
- Templated Deployment + Service for
spur-cloud-api and spur-cloud-frontend.
- Secret-based render of the full
spur-cloud.toml (no more ConfigMap leak of jwt_secret).
secrets.existingSecret escape hatch for ExternalSecrets / sealed-secrets workflows.
- ServiceAccount + ClusterRole/Binding (pods, pods/exec, services,
spur.ai/spurjobs).
- Ingress (host, optional TLS).
- Optional in-cluster Postgres StatefulSet (
postgres.enabled), with database.url override for managed Postgres (RDS / CloudSQL / Crunchy).
- Session namespace toggle (
createSessionNamespace) with helm.sh/resource-policy: keep so helm uninstall does not nuke running user pods.
checksum/secret annotation on the API pod so config changes trigger a rollout.
Out of scope (follow-ups)
- HPA / PodDisruptionBudget / pod anti-affinity / TopologySpreadConstraints.
- Publishing the chart to an OCI registry (
oci://ghcr.io/rocm/charts/spur-cloud) + CI to package on tag.
dependencies: on a future spur chart in ROCm/spur, so --set spur.enabled=true brings up the whole stack.
- Decision on whether to delete the raw manifests in
deploy/k8s/ or regenerate them from helm template in CI.
PR
See the linked PR for the implementation.
Summary
Add a first-class Helm chart for deploying the spur-cloud control plane (API + frontend + optional in-cluster Postgres + RBAC + ingress).
Motivation
Today
deploy/k8s/ships raw manifests applied withkubectl apply -f. This works for the first install but has several real gaps::latestimage refs everywhere, no semver / pull-policy story.kubectl create secret) and thejwt_secretlives in a ConfigMap with aCHANGEMEplaceholder — fine for demos, a footgun for prod.kubectl applydoesn't track removals.A Helm chart removes all of the above and is the standard way to package a Kubernetes workload that other teams or environments will consume.
Proposed scope (MVP)
spur-cloud-apiandspur-cloud-frontend.spur-cloud.toml(no more ConfigMap leak ofjwt_secret).secrets.existingSecretescape hatch for ExternalSecrets / sealed-secrets workflows.spur.ai/spurjobs).postgres.enabled), withdatabase.urloverride for managed Postgres (RDS / CloudSQL / Crunchy).createSessionNamespace) withhelm.sh/resource-policy: keepsohelm uninstalldoes not nuke running user pods.checksum/secretannotation on the API pod so config changes trigger a rollout.Out of scope (follow-ups)
oci://ghcr.io/rocm/charts/spur-cloud) + CI to package on tag.dependencies:on a futurespurchart in ROCm/spur, so--set spur.enabled=truebrings up the whole stack.deploy/k8s/or regenerate them fromhelm templatein CI.PR
See the linked PR for the implementation.