Skip to main content

Deploy with Helm

info

For alternative deployment approaches, see Deploy with the ToolHive Operator or Deploy manually.

Prerequisites​

  • A Kubernetes cluster (current and two previous minor versions are supported)
  • Permissions to create resources in the cluster
  • kubectl configured to communicate with your cluster
  • Helm v3.10 or later (v3.14+ is recommended)
  • PostgreSQL 14 or later

Overview​

The official Helm chart in the toolhive-registry-server 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:

helm upgrade --install registry-server \
oci://ghcr.io/stacklok/toolhive-registry-server \
-n toolhive-system --create-namespace \
-f values.yaml

Configure the Registry Server​

The chart's config block maps directly to the Registry Server's configuration file. Any valid configuration field can be set under config in your values file:

values.yaml
config:
sources:
- name: toolhive
git:
repository: https://github.com/stacklok/toolhive-catalog.git
branch: main
path: pkg/catalog/toolhive/data/registry-upstream.json
syncPolicy:
interval: '30m'
registries:
- name: default
sources: ['toolhive']
auth:
mode: anonymous
database:
host: postgres
port: 5432
user: registry
database: registry
sslMode: require

Provide database credentials​

Database credentials use the pgpass file pattern. Create a Kubernetes Secret with a pgpass-formatted 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.

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 (libpq rejects pgpass files with wider permissions):

values.yaml (excerpt)
extraEnv:
- name: PGPASSFILE
value: /pgpass-prepared/.pgpass

extraVolumes:
- name: pgpass-secret
secret:
secretName: registry-pgpass
defaultMode: 0600
- name: pgpass-prepared
emptyDir: {}

extraVolumeMounts:
- name: pgpass-prepared
mountPath: /pgpass-prepared
readOnly: true

initContainers:
- name: pgpass-init
image: cgr.dev/chainguard/busybox:latest
command:
- sh
- -c
- cp /pgpass-secret/.pgpass /pgpass-prepared/.pgpass && chmod 600
/pgpass-prepared/.pgpass
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65535
volumeMounts:
- name: pgpass-secret
mountPath: /pgpass-secret
readOnly: true
- name: pgpass-prepared
mountPath: /pgpass-prepared

The init container runs as the same UID as the main container, so the copied file is already owned by 65535 and the Registry Server can read it without a separate chown step. This matches the commented pgpass example in the chart's own values.yaml.

Next steps​