From 4f6fdfa81f3efb9e2446b5e4db958cdd71c69a6e Mon Sep 17 00:00:00 2001 From: Dan Barr <6922515+danbarr@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:18:44 -0400 Subject: [PATCH 1/5] Reorder Registry Server Helm deploy guide The guide ran `helm upgrade -f values.yaml` before it introduced the contents of values.yaml, and database credentials appeared later still, so a first-time reader could not execute the steps in order. Reorder to prerequisites, create a values file, provide database credentials, install the chart, then optional umbrella-chart config. Add the kubectl commands to create the namespace and pgpass Secret, note that the pgpass entry must match the connection fields, and match the quickstart's `--wait` install and labeled pod check. Co-Authored-By: Claude Opus 5 (1M context) --- docs/toolhive/guides-registry/deploy-helm.mdx | 133 +++++++++++------- 1 file changed, 86 insertions(+), 47 deletions(-) diff --git a/docs/toolhive/guides-registry/deploy-helm.mdx b/docs/toolhive/guides-registry/deploy-helm.mdx index c547412b..61f9505f 100644 --- a/docs/toolhive/guides-registry/deploy-helm.mdx +++ b/docs/toolhive/guides-registry/deploy-helm.mdx @@ -23,7 +23,8 @@ currently using it, see with your cluster - [Helm](https://helm.sh/docs/intro/install/) v3.10 or later (v3.14+ is recommended) -- PostgreSQL 14 or later +- A PostgreSQL 14 or later instance reachable from the cluster, with a database + and user already created for the Registry Server ## Overview @@ -33,22 +34,15 @@ repository deploys a standalone Registry Server. Use this method when you want to manage the Registry Server like any other Helm release without installing the ToolHive Operator. -## Install the chart - -Install the chart from its OCI registry into the `toolhive-system` namespace: +The steps below build a values file, create a Secret holding the database +password, and install the chart. -```bash -helm upgrade --install registry-server \ - oci://ghcr.io/stacklok/toolhive-registry-server \ - -n toolhive-system --create-namespace \ - -f values.yaml -``` - -## Configure the Registry Server +## Create a values file The chart's `config` block maps directly to the Registry Server's -[configuration file](./configuration.mdx). Any valid configuration field can be -set under `config` in your values file: +[configuration file](./configuration.mdx), so any valid configuration field can +be set under `config`. Start with a minimal file that points the server at a +catalog source and your PostgreSQL instance: ```yaml title="values.yaml" config: @@ -73,48 +67,34 @@ config: sslMode: require ``` -### Share a database host across subcharts - -When the Registry Server chart runs as a subchart of an umbrella chart whose -bundled services share a single PostgreSQL instance, you can set the database -host once at the umbrella level under `global.postgres` instead of repeating it -per subchart. This is a standard Helm `global.*` mechanism, so any umbrella -chart can use it: - -```yaml title="values.yaml (umbrella excerpt)" -global: - postgres: - host: 'postgres.example.com' - port: 5432 - sslMode: 'require' -``` - -When `config.database.host` on the Registry Server subchart is empty, the chart -falls back to `global.postgres.{host,port,sslMode}` (port defaults to `5432`). A -locally set `config.database.host` always wins, so per-subchart overrides keep -working. Only `host`, `port`, and `sslMode` are inherited. The `user`, -`database`, and credentials still go in the subchart's own `config.database` -block. - -:::enterprise - -If you run the full Stacklok Enterprise platform, its canonical umbrella chart -already wires up `global.postgres`, so you set the shared database host once at -the umbrella level rather than per subchart. See the -[platform-wide PostgreSQL defaults](../../platform/enterprise-platform/deployment.mdx#global-postgresql-defaults) -for the credential Secrets and the other components that read `global.postgres`. +This configuration serves a single registry with anonymous access. Replace +`host`, `port`, `user`, and `database` with the values for your own PostgreSQL +instance. The pgpass entry in the next step must repeat those same four values, +because libpq matches them field by field when it looks up the password. -::: +See [Configure sources and registries](./configuration.mdx) for the full field +reference, and [Set up authentication](./authentication.mdx) to replace +`mode: anonymous` before exposing the server. ## Provide database credentials -Database credentials use the pgpass file pattern. Create a Kubernetes Secret -with a +Keep the database password out of your values file and supply it through a +pgpass file instead. Create a Kubernetes Secret with a [pgpass-formatted](https://www.postgresql.org/docs/current/libpq-pgpass.html) entry under the key `.pgpass`, then point the chart at it with an init container that prepares the file and a `PGPASSFILE` environment variable that tells libpq where to find it. +Create the release namespace and the Secret: + +```bash +kubectl create namespace toolhive-system + +kubectl create secret generic registry-pgpass \ + --namespace toolhive-system \ + --from-literal=.pgpass='postgres:5432:registry:registry:' +``` + The chart's main container runs with `readOnlyRootFilesystem: true` as the non-root UID `65535`, so the init container copies the Secret into an `emptyDir` volume and applies `0600` permissions there (libpq rejects pgpass files with @@ -123,6 +103,8 @@ Kubernetes mounts Secret files owned by root, so a restrictive `defaultMode` such as `0600` would make the file unreadable by UID `65535` and the init container would fail to copy it. +Add the following to your values file: + ```yaml title="values.yaml (excerpt)" extraEnv: - name: PGPASSFILE @@ -169,6 +151,63 @@ separate `chown` step. This matches the commented pgpass example in the chart's own [`values.yaml`](https://github.com/stacklok/toolhive-registry-server/blob/main/deploy/charts/toolhive-registry-server/values.yaml). +For separate migration and application users, TLS verification, and alternatives +to pgpass such as environment variables or AWS RDS IAM, see +[Database configuration](./database.mdx). + +## Install the chart + +With `values.yaml` complete, install the chart from its OCI registry into the +`toolhive-system` namespace: + +```bash +helm upgrade --install registry-server \ + oci://ghcr.io/stacklok/toolhive-registry-server \ + -n toolhive-system \ + -f values.yaml \ + --wait --timeout=3m +``` + +Confirm that the pod is ready: + +```bash +kubectl get pods -n toolhive-system \ + -l app.kubernetes.io/instance=registry-server +``` + +## Share a database host across subcharts + +When the Registry Server chart runs as a subchart of an umbrella chart whose +bundled services share a single PostgreSQL instance, you can set the database +host once at the umbrella level under `global.postgres` instead of repeating it +per subchart. This is a standard Helm `global.*` mechanism, so any umbrella +chart can use it: + +```yaml title="values.yaml (umbrella excerpt)" +global: + postgres: + host: 'postgres.example.com' + port: 5432 + sslMode: 'require' +``` + +When `config.database.host` on the Registry Server subchart is empty, the chart +falls back to `global.postgres.{host,port,sslMode}` (port defaults to `5432`). A +locally set `config.database.host` always wins, so per-subchart overrides keep +working. Only `host`, `port`, and `sslMode` are inherited. The `user`, +`database`, and credentials still go in the subchart's own `config.database` +block. + +:::enterprise + +If you run the full Stacklok Enterprise platform, its canonical umbrella chart +already wires up `global.postgres`, so you set the shared database host once at +the umbrella level rather than per subchart. See the +[platform-wide PostgreSQL defaults](../../platform/enterprise-platform/deployment.mdx#global-postgresql-defaults) +for the credential Secrets and the other components that read `global.postgres`. + +::: + ## Next steps - [Configure sources and registries](./configuration.mdx) to set up your data From b9d6ea683d268cd5aceae4fdfd5250f12d34b3cc Mon Sep 17 00:00:00 2001 From: Dan Barr <6922515+danbarr@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:19:14 -0400 Subject: [PATCH 2/5] Retarget stale database doc cross-references The Kubernetes Secrets tip sent readers to the deprecated operator guide, a bash block used a bare `deployment.mdx` filename in a comment, and the connection troubleshooting step named only the operator's `pgpassSecretRef` field. Point each at the Helm guide or quickstart instead, keeping the database page a deployment-agnostic reference. Co-Authored-By: Claude Opus 5 (1M context) --- docs/toolhive/guides-registry/database.mdx | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/toolhive/guides-registry/database.mdx b/docs/toolhive/guides-registry/database.mdx index bbed7fc8..e18d965e 100644 --- a/docs/toolhive/guides-registry/database.mdx +++ b/docs/toolhive/guides-registry/database.mdx @@ -94,8 +94,10 @@ server reuses `password` for migrations. :::tip[Kubernetes Secrets] In Kubernetes, reference a Secret via `envFrom` or `secretKeyRef` rather than -storing the password in a ConfigMap. See the -[operator deploy guide](./deploy-operator.mdx) for complete examples. +storing the password in a ConfigMap. The +[Registry Server quickstart](./quickstart.mdx) shows this pattern in a Helm +values file. For the pgpass alternative, see +[Provide database credentials](./deploy-helm.mdx#provide-database-credentials). ::: @@ -142,15 +144,15 @@ chmod 600 /etc/secrets/pgpassfile Set the `PGPASSFILE` environment variable when running the server: ```bash -# For standalone server export PGPASSFILE=/etc/secrets/pgpassfile thv-registry-api serve --config config.yaml - -# For Docker/Kubernetes -# Set the PGPASSFILE environment variable in your deployment configuration -# See deployment.mdx for examples ``` +In Docker or Kubernetes, set `PGPASSFILE` in your deployment configuration +instead. See +[Provide database credentials](./deploy-helm.mdx#provide-database-credentials) +for a worked Helm example. + :::tip The pgpass file format is: `hostname:port:database:username:password` @@ -339,8 +341,10 @@ errors. Work through these in order: 1. **Credentials match.** Confirm the username, password, and database name in your config (or pgpass file, or `dynamicAuth` block) match what PostgreSQL - was provisioned with. For Kubernetes, check that the Secret referenced by - `pgpassSecretRef` decodes to the right values. + was provisioned with. In Kubernetes, check that the Secret holding the + credentials decodes to the right values: the Secret mounted through + `extraVolumes` for Helm deployments, or `spec.pgpassSecretRef` for the + deprecated operator method. 2. **Network reachability.** From the Registry Server pod or host, confirm you can reach the database host and port. In Kubernetes, port-forward the PostgreSQL Service and try `psql` from your local machine to rule out From 69fabc1d97e00fa0e87d43194d037d1b98b7676b Mon Sep 17 00:00:00 2001 From: Dan Barr <6922515+danbarr@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:26:27 -0400 Subject: [PATCH 3/5] Document exposing the Registry Server Service The Helm guide ended at a ready pod with no way to reach the API. The chart ships no Ingress or HTTPRoute template, so readers have to bring their own routing and nothing said so. Describe the ClusterIP Service and its default `- toolhive-registry-server` name, point readers at their existing ingress controller, Gateway API, or cloud load balancer, and keep port-forward as a pre-routing smoke test only. Verified against chart v1.5.0 via `helm template`. Co-Authored-By: Claude Opus 5 (1M context) --- docs/toolhive/guides-registry/deploy-helm.mdx | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/docs/toolhive/guides-registry/deploy-helm.mdx b/docs/toolhive/guides-registry/deploy-helm.mdx index 61f9505f..f3ee889a 100644 --- a/docs/toolhive/guides-registry/deploy-helm.mdx +++ b/docs/toolhive/guides-registry/deploy-helm.mdx @@ -35,7 +35,7 @@ to manage the Registry Server like any other Helm release without installing the ToolHive Operator. The steps below build a values file, create a Secret holding the database -password, and install the chart. +password, install the chart, and route traffic to the resulting Service. ## Create a values file @@ -175,6 +175,55 @@ kubectl get pods -n toolhive-system \ -l app.kubernetes.io/instance=registry-server ``` +## Expose the Registry Server + +The chart creates a ClusterIP Service on port 8080, named after the release and +chart in the form `-toolhive-registry-server`. For the +`registry-server` release above, that's +`registry-server-toolhive-registry-server`. Confirm the name: + +```bash +kubectl get svc -n toolhive-system \ + -l app.kubernetes.io/instance=registry-server +``` + +Set `fullnameOverride` in your values file if you want a shorter, stable name to +reference from routing resources. + +The chart doesn't create an Ingress, HTTPRoute, or any other external entry +point, so route to the Service with whatever your cluster already uses: + +- **Ingress controller**: create an Ingress with the Service as its backend on + port 8080. +- **Gateway API**: create an HTTPRoute with the Service as its `backendRef`. +- **Cloud load balancer**: change the Service type in your values file and add + your provider's annotations under `service.annotations`. + +```yaml title="values.yaml (excerpt)" +service: + type: LoadBalancer + annotations: {} +``` + +Whichever you choose, [set up authentication](./authentication.mdx) before the +server becomes reachable outside the cluster. The values file above sets +`mode: anonymous`, which lets any caller read the registry. + +To check the API before you wire up routing, port-forward the Service. This +command blocks, so leave it running and open a separate terminal for the +request: + +```bash +kubectl port-forward -n toolhive-system \ + svc/registry-server-toolhive-registry-server 8080:8080 +``` + +In the separate terminal, query one of the registries you defined in `config`: + +```bash +curl -s http://localhost:8080/registry/default/v0.1/servers | jq . +``` + ## Share a database host across subcharts When the Registry Server chart runs as a subchart of an umbrella chart whose From eabc9503def4f30dfe303faa658c87b2c52b43c9 Mon Sep 17 00:00:00 2001 From: Dan Barr <6922515+danbarr@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:26:29 -0400 Subject: [PATCH 4/5] Drop bare filename reference from config example The YAML example pointed at `authentication.mdx` in a comment, which readers can't click and which duplicates the prose link further down the page. Co-Authored-By: Claude Opus 5 (1M context) --- docs/toolhive/guides-registry/configuration.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/toolhive/guides-registry/configuration.mdx b/docs/toolhive/guides-registry/configuration.mdx index 97991f0e..b15f2f90 100644 --- a/docs/toolhive/guides-registry/configuration.mdx +++ b/docs/toolhive/guides-registry/configuration.mdx @@ -45,7 +45,6 @@ registries: org: 'acme' # Authentication configuration (required) -# See authentication.mdx for detailed configuration options auth: mode: anonymous From 921144a20d075b780df41d572e5586864d66bdc1 Mon Sep 17 00:00:00 2001 From: Dan Barr <6922515+danbarr@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:40:28 -0400 Subject: [PATCH 5/5] Address Copilot review on credential examples Use placeholders for the whole pgpass entry instead of mixing sample values with . The positional format now doubles as documentation of the field order, and readers can't update values.yaml while leaving a stale hard-coded entry behind. Name both Helm credential mechanisms in the connection troubleshooting step, since the Secret lives in extraEnv or extraVolumes depending on which pattern was used. Co-Authored-By: Claude Opus 5 (1M context) --- docs/toolhive/guides-registry/database.mdx | 7 ++++--- docs/toolhive/guides-registry/deploy-helm.mdx | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/toolhive/guides-registry/database.mdx b/docs/toolhive/guides-registry/database.mdx index e18d965e..02851212 100644 --- a/docs/toolhive/guides-registry/database.mdx +++ b/docs/toolhive/guides-registry/database.mdx @@ -342,9 +342,10 @@ errors. Work through these in order: 1. **Credentials match.** Confirm the username, password, and database name in your config (or pgpass file, or `dynamicAuth` block) match what PostgreSQL was provisioned with. In Kubernetes, check that the Secret holding the - credentials decodes to the right values: the Secret mounted through - `extraVolumes` for Helm deployments, or `spec.pgpassSecretRef` for the - deprecated operator method. + credentials decodes to the right values. With Helm, that Secret is referenced + from `extraEnv` if you inject the password as an environment variable, or + mounted through `extraVolumes` if you use pgpass. The deprecated operator + method uses `spec.pgpassSecretRef`. 2. **Network reachability.** From the Registry Server pod or host, confirm you can reach the database host and port. In Kubernetes, port-forward the PostgreSQL Service and try `psql` from your local machine to rule out diff --git a/docs/toolhive/guides-registry/deploy-helm.mdx b/docs/toolhive/guides-registry/deploy-helm.mdx index f3ee889a..74cf24a4 100644 --- a/docs/toolhive/guides-registry/deploy-helm.mdx +++ b/docs/toolhive/guides-registry/deploy-helm.mdx @@ -68,7 +68,7 @@ config: ``` This configuration serves a single registry with anonymous access. Replace -`host`, `port`, `user`, and `database` with the values for your own PostgreSQL +`host`, `port`, `database`, and `user` with the values for your own PostgreSQL instance. The pgpass entry in the next step must repeat those same four values, because libpq matches them field by field when it looks up the password. @@ -85,14 +85,15 @@ entry under the key `.pgpass`, then point the chart at it with an init container that prepares the file and a `PGPASSFILE` environment variable that tells libpq where to find it. -Create the release namespace and the Secret: +Create the release namespace and the Secret. The pgpass format is positional, so +the first four fields must match `config.database` in your values file exactly: ```bash kubectl create namespace toolhive-system kubectl create secret generic registry-pgpass \ --namespace toolhive-system \ - --from-literal=.pgpass='postgres:5432:registry:registry:' + --from-literal=.pgpass='::::' ``` The chart's main container runs with `readOnlyRootFilesystem: true` as the