Self-host an existing provider
Run a capability on your infrastructure and connect it to a hub.
A provider is a chart you install into a Kubernetes cluster you administer. It authenticates to the hub’s kcp with a kubeconfig the hub mints for it, registers itself in the hub’s catalog, and serves its API to the workspaces that enable it. The steps below are the same for every provider; the per-provider guides add the state, credentials, and settings each one needs.
Prerequisites
- An existing hub you can reach, and permission to onboard a provider: a platform administrator (listed in the hub’s
adminUsers), or an organization administrator for an organization-owned provider. - A Kubernetes runtime you administer, Helm, and kubectl.
- Network access from the provider pods to the hub and to every kcp shard’s advertised virtual-workspace endpoint. Connectivity to the public console alone is not enough.
- The provider’s own dependencies: a Postgres database, an S3 bucket, a Gateway. Each guide lists them.
Run the providers in the hub’s cluster when you can. They then reach the hub at its in-cluster Service, which is what the hub bakes into their kubeconfigs when hub.internalURL is set, and the kcp shards through the same split-horizon names the hub uses.
1. Get the provider’s kubeconfig
The hub mints one credential per provider: a ServiceAccount in a provider workspace of its own (root:railgrid:providers:<name>), cluster-admin there and nothing above it. Three ways to get it:
- Console, organization-owned provider. Open Providers → Self-Hosting, select the provider recipe, and follow the generated instructions. They include the chart version, the provider-specific values, and the command that creates the runtime Secret.
- Console, platform provider. A platform administrator onboards it in the admin area and downloads the kubeconfig there.
- API, scripted. Creating a
Providerobject in the hub’sroot:railgrid:system:providersworkspace does exactly what the admin area does: the hub creates the provider workspace, the ServiceAccount, and writes the kubeconfig to Secret<name>-kubeconfigin that workspace’sdefaultnamespace. Railgrid’s own environments mint all six public providers this way from one idempotent script.
# With an admin kubeconfig for the hub's kcp (front proxy), create the Provider...
WS=https://kcp.example.com/clusters/root:railgrid:system:providers
curl --cert admin.crt --key admin.key -X POST -H 'Content-Type: application/json' \
-d '{"apiVersion":"admin.railgrid.ai/v1alpha1","kind":"Provider","metadata":{"name":"code"},"spec":{"displayName":"code"}}' \
"$WS/apis/admin.railgrid.ai/v1alpha1/providers"
# ...then read the minted kubeconfig once status.secretRef appears.
curl --cert admin.crt --key admin.key "$WS/api/v1/namespaces/default/secrets/code-kubeconfig" \
| python3 -c 'import sys,json,base64; print(base64.b64decode(json.load(sys.stdin)["data"]["kubeconfig"]).decode())' \
> code.kubeconfigThe kubeconfig’s server is the hub (hub.internalURL when set, else hub.hubExternalURL), and its CA data is the hub’s provider CA bundle. It is a credential; keep it out of version control and never use it as a Helm --kube-context.
2. Create the runtime Secret
In the cluster where the chart will run:
kubectl create namespace railgrid-provider-code
kubectl --namespace railgrid-provider-code create secret generic railgrid-provider-kubeconfig \
--from-file=kubeconfig=./code.kubeconfigThe filename can differ, but the Secret data key must be kubeconfig; the charts mount that exact key. Every public provider defaults to Secret name railgrid-provider-kubeconfig (providerKubeconfig.secretName), except the infrastructure provider in operator mode, which reads operator.providerKubeconfigSecret.name. A Secret in the hub cluster is not visible from another cluster; create it where the pods run.
3. Set the hub values
Every public provider chart takes the same hub block:
hub:
# Where heartbeats go. In the hub's cluster, the hub Service.
url: https://railgrid-hub.railgrid-system.svc.cluster.local:9443
# Leave empty: heartbeats authenticate with the provider's own
# ServiceAccount token. Naming a Secret that does not exist wedges the
# pod in CreateContainerConfigError.
tokenSecretRef:
name: ""
catalogEntry:
enabled: true # the init container registers the provider with the hubThe provider verifies the hub’s serving certificate against the system roots plus the CA bundle inside its own kubeconfig, which the hub minted from hub.providerCASecretName. A hub on a private CA therefore needs nothing more than the Secret from step 2; hub.insecure stays false and is only for development hubs with throwaway certificates. To trust a different bundle, set RAILGRID_HUB_CA_FILE (or RAILGRID_HUB_CA_DATA) in the pod’s environment. Several providers also take a browser-facing hub origin (hub.publicURL, hub.externalURL, hub.actionsExternalURL) for redirects and agents; those must be the public console URL, not the Service.
4. Install the chart
The charts are published at oci://ghcr.io/railgrid/charts/railgrid-<provider>-provider. Each provider has its own version line; find the current one and pin it:
helm show chart oci://ghcr.io/railgrid/charts/railgrid-code-provider | grep -E '^(version|appVersion)'
helm upgrade --install code oci://ghcr.io/railgrid/charts/railgrid-code-provider \
--version CHART_VERSION --namespace railgrid-provider-code --values code-values.yamlRailgrid’s environments follow 0.2.x through Flux. Images for the public providers are public; a private provider needs an imagePullSecrets entry.
5. Verify
- The pod is
Running. The init container has applied the CatalogEntry, schemas, and APIExport into the provider workspace through the kubeconfig; if it fails, see the table below. - The provider appears in the console’s provider list for the hub.
- Enable the provider in a test workspace and complete its quickstart. Only this proves the whole path: hub, kcp routing, and the provider’s own backend.
A fresh provider is UNREADY until a workspace binds it
Right after install, every provider reports /healthz 200 but /readyz 503 with cannot reach the APIExport virtual workspace ... APIExportEndpointSlice <name>.providers.railgrid.ai publishes no endpoints yet, and its CatalogEntry carries BackendHealthy=False. This is kcp, not a fault: a shard publishes a virtual-workspace endpoint only once a workspace on it has bound the export. Enabling the provider in one workspace makes it Ready. A Helm install with --wait on such a provider times out for the same reason.
Diagnose an installation failure
| Symptom | Check | Fix |
|---|---|---|
Init container: x509: certificate signed by unknown authority against the hub | The kubeconfig was minted before the hub had providerCASecretName, or the hub’s serving certificate changed | Re-mint and refresh the kubeconfig |
Pod running but Ready=False, logs show x509 against a shard hostname | The provider CA bundle lacks the kcp server CA | Append root-server-ca to the bundle, restart the hub, refresh the kubeconfig |
CreateContainerConfigError | hub.tokenSecretRef.name or another *SecretRef names a Secret that does not exist | Create the Secret or leave the reference empty |
ImagePullBackOff | Image tag and registry access | Pin image.tag to a published version; add a pull Secret for private images |
| Pod ready, provider missing from the console | Heartbeat and CatalogEntry conditions in the provider workspace | Check hub.url is reachable from the pod and the init container succeeded |
/readyz 503, publishes no endpoints yet | Nothing has bound the export | Enable the provider in a workspace |
kubectl --namespace railgrid-provider-code get events --sort-by=.lastTimestamp
kubectl --namespace railgrid-provider-code logs -l app.kubernetes.io/instance=code --all-containers=true --tail=100Redact credentials before sharing diagnostics.
Refresh a provider kubeconfig
The hub rewrites each provider’s kubeconfig Secret in the provider workspace when it reconciles, for example after the provider CA bundle or hub.internalURL changes. The copy in your runtime namespace does not follow. Re-read the Secret from the hub (step 1), re-create the runtime Secret (step 2), and restart the provider pods. Railgrid’s environments keep this as one idempotent script run after every hub trust change.
Remove a provider
Disable the provider in every workspace that uses it and follow the provider’s cleanup instructions first, then helm uninstall the release. Deleting the Provider object on the hub tears down the provider workspace and strands tenant bindings; do not use it as a temporary disable. See Provider access and lifecycle
.