Deploying Colden to a cluster

What a Kubernetes cluster needs before the first Colden release, how to prepare a namespace, the first deployment and the checks that prove it works.

One Helm release per environment runs the whole kit (infra/helm/colden). This guide takes an empty cluster to a working release; the pipeline repeats steps 2 and 3 on every deployment (docs/guides/ci.md).

1. What the cluster needs

The cluster's owner installs these once; the chart expects them and creates none of them.

PrerequisiteWhyHow the chart uses it
Kubernetes 1.29 or laterThe chart's API versions (autoscaling/v2, policy/v1, networking.k8s.io/v1)kubeVersion in Chart.yaml
An ingress controller (ingress-nginx by default, Traefik or any other)The web and api hostsingress.className
cert-manager with an issuerTLS certificates for the hostseither ingress.certificate.issuer (the chart requests a Certificate per host) or ingress.annotations (cert-manager.io/cluster-issuer), and ingress.tls.*SecretName
DNS records pointing at the ingress: the web's host, and the api's when it has its ownThe public addresseshosts.web, hosts.api (empty: the api's /v1 and /webhooks on the web's host)
A default storage classThe in-cluster PostgreSQL's volume (dev and staging)postgresql.persistence.*
metrics-serverThe autoscalers, where they are on<host>.autoscaling
The OTLP endpoint reachable from the podsTraces, metrics and logsconfig.Telemetry__OtlpEndpoint, the Secret's Telemetry__OtlpHeaders
A GitLab deploy token with read_registryPulling the kit's imagesthe pull secret in global.imagePullSecrets (gitlab-registry unless COLDEN_PULL_SECRET_NAME names another)
An external PostgreSQL and Redis (prod)The kit's and the identity server's data, the cachethe Secret's ConnectionStrings__Postgres, KRATOS_DSN, ConnectionStrings__Redis

For an external PostgreSQL, create the two databases first, each owned by its own login role, as infra/compose/postgres/init.sh does: colden for the kit and kratos for the identity server.

2. Prepare the namespace

Export the Secret's keys (infra/helm/colden/README.md lists them) and the registry credential in your shell, from your password manager, never from a file in the repository; then:

export COLDEN_REGISTRY_USER=<deploy token user> COLDEN_REGISTRY_PASSWORD=<deploy token>
export COLDEN_IN_CLUSTER_POSTGRESQL=true          # dev and staging, which run the chart's PostgreSQL
tools/bootstrap-namespace.sh colden-dev --dry-run  # checks the variables and lists the keys; needs no cluster
tools/bootstrap-namespace.sh colden-dev            # Windows: tools/bootstrap-namespace.ps1 colden-dev

The script applies the namespace, the pull secret gitlab-registry and the Secret colden-secrets with server-side apply. It stops before touching the cluster when a required key is missing or has the wrong length, and it never prints a value. Where the cluster's operator creates namespaces, COLDEN_CREATE_NAMESPACE=false makes the script check that the namespace exists instead. COLDEN_PULL_SECRET_NAME gives the pull secret another name, the one the environment's global.imagePullSecrets uses.

3. Install the release

helm upgrade --install colden infra/helm/colden -n colden-dev -f infra/helm/colden/values-dev.yaml \
  --set global.image.tag=sha-<commit> --wait --timeout 10m --atomic

Before the first install, set the environment's real hosts in its values file; an environment of your own keeps its values in infra/helm/environments/<owner>/<environment>.yaml, which tools/helm-check.sh validates. On a first install the in-cluster PostgreSQL starts first, then both migrations run (kubectl -n colden-dev logs job/colden-migrate and job/colden-kratos-migrate), then the api, the worker, the web, the identity server and its courier start. --atomic removes a failed first install and rolls back a failed upgrade.

4. Check the deployment

kubectl -n colden-dev get deploy,job,ingress                # every Deployment ready, both Jobs complete
curl -fsS https://<api host>/health/ready                    # the api and its dependencies (with an api host of its own)
curl -fsS https://<web host>/.ory/health/ready               # the identity server, through the web
curl -fsS https://<web host>/api/health                      # the web

Then sign up on the web host: the verification code arrives by email through the kit's sender, and the first organization opens its dashboard. A span from the api in the telemetry backend confirms the OTLP export.

Changing a secret

Update the variable, rerun tools/bootstrap-namespace.sh <namespace> (the pipeline does it on every deployment), then restart the pods, since nothing reloads at runtime:

kubectl -n colden-dev rollout restart deploy -l app.kubernetes.io/instance=colden

Removing an environment

helm -n colden-dev uninstall colden removes the release and keeps the migration Jobs' last runs, the in-cluster PostgreSQL and its volume. Delete the namespace to remove everything, including the data.