BoltMCP Installation Docs

Cluster Preparation

Create the boltmcp namespace, add the image pull secret, create three application Secrets, and authenticate Helm against the BoltMCP container registry.

BoltMCP's Helm chart and container images are both hosted in a private Google Artifact Registry. You should have received a key.json service account key file for access — the same key authenticates both Helm (to pull the chart) and your Kubernetes cluster (to pull images at runtime).

Before installing the chart you also need to pre-create three Kubernetes Secrets that BoltMCP reads at runtime (database passwords, OIDC client secrets, and auth tokens). The chart does not generate these — you control how they are populated.

Locate BoltMCP key file

Export the path to your key file as the shell variable HELM_REGISTRY_CONFIG:

export HELM_REGISTRY_CONFIG="$PWD/keys/boltmcp-key.json"

This variable is used explicitly by kubectl below, and implicitly by every helm command on the following pages.

Create Namespace

This namespace is where BoltMCP will be installed:

kubectl create namespace boltmcp

StorageClass

BoltMCP's bundled PostgreSQL stores its data on a PersistentVolumeClaim. The cluster fulfils that claim by dynamically provisioning a volume through a StorageClass, so it must have an appropriate StorageClass available before you install the chart — otherwise the database PVC stays Pending and the install stalls.

List what the cluster already provides:

kubectl get storageclass

Most managed clusters ship a suitable class out of the box: GKE and AKS both come with a usable default, so this command will list one and there is nothing more to do here.

An appropriate StorageClass for BoltMCP is one that:

  • provisions dynamically — it has a real CSI provisioner (not kubernetes.io/no-provisioner), so it can create a volume on demand without a pre-existing PersistentVolume;
  • supports ReadWriteOnce — the database mounts the volume read-write on a single node. Block storage (AWS EBS, GCE PD, Azure Disk) is the right fit; avoid network-file classes (EFS, Filestore, Azure Files), whose semantics don't suit PostgreSQL.

It is also worth having allowVolumeExpansion: true (so the volume can grow later) and volumeBindingMode: WaitForFirstConsumer (so the volume is provisioned in the same zone the database pod is scheduled to — important on multi-zone clusters), though neither is strictly required.

If no appropriate StorageClass exists, create one. The common case is EKS Auto Mode: a fresh cluster comes with only a non-default gp2 class backed by the deprecated in-tree provisioner, so add a CSI-backed gp3 class instead:

config/storageclass-gp3.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3
provisioner: ebs.csi.eks.amazonaws.com
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
parameters:
  type: gp3
kubectl apply -f ./config/storageclass-gp3.yaml

The ebs.csi.eks.amazonaws.com provisioner is the one built into EKS Auto Mode. On a classic managed-node-group EKS cluster, install the EBS CSI driver add-on instead and use its provisioner, ebs.csi.aws.com.

Image Pull Secret

Create the pull secret in the boltmcp namespace so the cluster can pull images at runtime:

kubectl create secret docker-registry \
  boltmcp-pull-secret \
  -n boltmcp \
  --from-file=.dockerconfigjson=$HELM_REGISTRY_CONFIG

The chart references this Secret by name via global.imagePullSecrets, which defaults to boltmcp-pull-secret. If you prefer a different Secret name, create it under that name here and override global.imagePullSecrets in your values file accordingly.

Application Secrets

BoltMCP reads passwords and tokens from three user-managed Kubernetes Secrets. The chart never creates them. Make sure all three Secrets exist with every required key populated before moving on to the deployment step.

The Secret names are derived from the Helm release name as:

  • <release>-database
  • <release>-oidc
  • <release>-auth

Here we assume the default release name boltmcp:

Required keys

boltmcp-database

KeyUsed for
superuser-passwordPostgreSQL superuser password
migrate-core-passwordDB password for the migration role
web-passwordDB password for the BoltMCP web app
rest-api-passwordDB password for the REST API
mcp-server-passwordDB password for the MCP server
keycloak-passwordDB password for Keycloak
vault-passwordDB password for Vault's storage backend (required when vault.enabled=true, the default)

boltmcp-oidc

KeyUsed for
web-client-secretOIDC client secret for the web app
mcp-server-client-secretOIDC client secret for the MCP server
rest-api-resource-server-client-secretOIDC client secret used by the REST API to verify tokens.

Other OIDC clients created by BoltMCP are either public or have their passwords automatically set by Keycloak.

boltmcp-auth

KeyUsed for
web-auth-secretSession signing key for the web app (≥ 32 chars)
keycloak-admin-passwordMaster-realm Keycloak operator password (break-glass admin-console login)
boltmcp-admin-passwordPassword for the first user in the BoltMCP Keycloak realm
mcp-inspector-proxy-auth-tokenProxy auth token

Manual with kubectl (simplest)

Generate random values inline and create all three Secrets in one shot. This is the fastest path for evaluation installs and any environment where you don't already have a secrets manager. Update RELEASE below if you plan to install the chart with a custom release name.

create-secrets.sh
RELEASE=boltmcp
rand() { openssl rand -base64 48 | tr -d '\n=+/' | cut -c1-32; }

kubectl create secret generic ${RELEASE}-database -n boltmcp \
  --from-literal=superuser-password="$(rand)" \
  --from-literal=migrate-core-password="$(rand)" \
  --from-literal=web-password="$(rand)" \
  --from-literal=rest-api-password="$(rand)" \
  --from-literal=mcp-server-password="$(rand)" \
  --from-literal=keycloak-password="$(rand)" \
  --from-literal=vault-password="$(rand)"

kubectl create secret generic ${RELEASE}-oidc -n boltmcp \
  --from-literal=web-client-secret="$(rand)" \
  --from-literal=mcp-server-client-secret="$(rand)" \
  --from-literal=rest-api-resource-server-client-secret="$(rand)"

kubectl create secret generic ${RELEASE}-auth -n boltmcp \
  --from-literal=web-auth-secret="$(rand)" \
  --from-literal=keycloak-admin-password="$(rand)" \
  --from-literal=boltmcp-admin-password="$(rand)" \
  --from-literal=mcp-inspector-proxy-auth-token="$(rand)"

To retrieve a value later (assuming release name boltmcp):

kubectl get secret boltmcp-auth -n boltmcp \
  -o jsonpath='{.data.boltmcp-admin-password}' | base64 -d; echo

Alternatives

The manual approach above is the simplest path, but if you already run a secrets-management workflow you can populate the same three Secrets from it instead. With the External Secrets Operator you sync the values from HashiCorp Vault or a cloud secrets manager (AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, 1Password) into Kubernetes Secrets; with Sealed Secrets or SOPS you keep the encrypted source material in Git and let an in-cluster controller (or your GitOps tool) materialise the plain Secrets.

On this page