Install the hub
Choose embedded or external kcp, configure a production hub, and verify the console.
Choose a deployment
| Mode | Hub workload | Constraint |
|---|---|---|
| Embedded kcp | StatefulSet | One replica; embedded etcd uses its PVC |
| External kcp | Deployment | Hub replicas can scale; operate kcp and its storage separately (guide ) |
A hub installation does not install every provider. Install the providers your users need after verifying the hub.
Prerequisites
Use a Kubernetes cluster you administer, Helm, kubectl, and a reviewed Railgrid chart version. For public access, configure DNS, TLS, and ingress . Use development credentials only in an isolated test installation.
The chart is published as oci://ghcr.io/railgrid/charts/railgrid-hub. Its version is the Railgrid release without the v prefix (release vX.Y.Z is chart X.Y.Z), and its appVersion names the matching hub image. Resolve the version once and reuse it in every command below. Helm reports the newest published chart when no version is given:
HUB_CHART_VERSION=$(helm show chart oci://ghcr.io/railgrid/charts/railgrid-hub \
| awk '/^version:/ {print $2}')
echo "$HUB_CHART_VERSION"To install a release you have reviewed rather than the newest one, set HUB_CHART_VERSION to that release’s chart version instead. The releases page
lists them.
Local hub
For a laptop or a throwaway evaluation, do not install the chart by hand. railgrid dev init creates one kind cluster running the hub on embedded kcp, the default providers, and an agent that joins that same cluster as an edge, then signs you in with a static development token at https://console.127.0.0.1.sslip.io:9443:
railgrid dev initrailgrid dev delete removes it again. Run Railgrid locally
covers prerequisites, options, and troubleshooting, and the railgrid dev init reference
lists every flag, including --chart-path for a hub chart you are changing. That environment is not a production installation; the rest of this page configures a hub other people use.
Configure a production hub
For a shared cluster, prepare DNS, ingress and TLS
, and OIDC
first. The values below are what a production hub against external kcp
looks like. Substitute your hostnames, identity provider, administrators, and proxy ranges. For embedded kcp, drop the kcp block and keep replicaCount: 1.
Save this as hub-values.yaml:
replicaCount: 2 # more than 1 requires external kcp
kcp:
external:
enabled: true
existingSecret: railgrid-kcp # key admin.kubeconfig; see External kcp
hub:
hubExternalURL: https://console.example.com
# In-cluster address baked into minted provider kubeconfigs, so
# provider -> hub traffic never leaves the cluster. The serving
# certificate below must cover this name.
internalURL: https://railgrid-hub.railgrid-system.svc.cluster.local:9443
adminUsers:
- admin@example.com
# Reverse proxies in front of the hub (ingress pods, load balancer).
# Required behind any proxy; see Ingress.
trustedProxyCIDRs:
- 10.244.0.0/16
# Hosts the console may embed in an iframe: App Studio previews
# under the infrastructure provider's apps domain.
portalFrameSources:
- https://*.apps.example.com
security:
providerDelegatedTokens: platform
tls:
# A certificate you issue (below). Setting this turns the chart's
# selfSigned and certManager modes off.
existingSecret: railgrid-hub-serving-tls
# CA bundle embedded in every minted provider kubeconfig.
providerCASecretName: railgrid-provider-ca
idp:
issuerURL: https://auth.example.com
clientID: railgrid
# Keep accounts when you add a Dex connector or change issuer.
linkVerifiedEmailIssuers:
- https://auth.example.comEverything the example leaves out keeps the chart default: development mode is off, the chart creates no Ingress or HTTPRoute, and the hub image is the chart’s appVersion. Route traffic with your own Ingress or HTTPRoute (see Ingress
), or enable ingress or httpRoute when the chart’s objects fit your controller.
Set adminUsers to identities as resolved by your authentication setup: the email for OIDC users. An empty list disables the admin surface, including provider onboarding.
Before installing, create the Secrets the values reference: the kcp kubeconfig (external kcp only), the serving certificate, and the provider CA bundle. The next section covers the certificates. Then render and install:
helm template railgrid-hub oci://ghcr.io/railgrid/charts/railgrid-hub \
--version "$HUB_CHART_VERSION" --namespace railgrid-system --values hub-values.yaml
helm upgrade --install railgrid-hub oci://ghcr.io/railgrid/charts/railgrid-hub \
--version "$HUB_CHART_VERSION" --kube-context HOSTING_CONTEXT \
--namespace railgrid-system --create-namespace \
--values hub-values.yaml --wait --timeout 15mName the release railgrid-hub. The chart’s full name then collapses to railgrid-hub, which is the Service name the provider charts default to in hub.url.
Replace HOSTING_CONTEXT with the hosting cluster’s kubeconfig context. The hub reads the OIDC discovery document at start and restarts until the issuer is reachable, so install Dex first. A successful Helm rollout verifies workload readiness; complete the authentication and workspace checks below before giving users access.
Serving certificate and provider trust
Providers verify the hub’s certificate. A minted provider kubeconfig carries no insecure-skip-tls-verify unless the hub runs in development mode; it embeds the CA bundle from hub.providerCASecretName (Secret key ca.crt) and trusts nothing else. Two consequences:
- The hub’s serving certificate must be issued by a CA you control and keep stable. The chart’s
tls.selfSignedmode generates a fresh CA on every upgrade, which invalidates every minted kubeconfig on the next release. Issue the certificate yourself. - With external kcp, providers also follow APIExport endpoints to the shards and verify the shards’ certificates with the same bundle. Append kcp’s
root-server-cacertificate (Secretroot-server-cain the kcp namespace) to the bundle, or providers start but reportx509: certificate signed by unknown authorityagainst the shard hostnames and stay unready.
With cert-manager installed, this creates a ten-year CA, an Issuer from it, and a one-year serving certificate. The hub reads its certificate once at start; the long duration and early renewal mean any hub rollout inside the renewal window picks the new certificate up. Browsers never see this certificate when TLS terminates at your edge; providers do.
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned
namespace: railgrid-system
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: railgrid-hub-ca
namespace: railgrid-system
spec:
isCA: true
commonName: railgrid-hub-ca
secretName: railgrid-hub-ca
duration: 87600h # 10y; providers embed it, so rotating it means re-minting them
renewBefore: 8760h
privateKey:
algorithm: ECDSA
size: 256
rotationPolicy: Never
issuerRef:
name: selfsigned
kind: Issuer
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: railgrid-hub-ca
namespace: railgrid-system
spec:
ca:
secretName: railgrid-hub-ca
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: railgrid-hub-serving
namespace: railgrid-system
spec:
secretName: railgrid-hub-serving-tls
duration: 8760h # 1y
renewBefore: 2160h # 90d
dnsNames:
- console.example.com
- railgrid-hub
- railgrid-hub.railgrid-system.svc
- railgrid-hub.railgrid-system.svc.cluster.local
- localhost
ipAddresses:
- 127.0.0.1
privateKey:
algorithm: ECDSA
size: 256
rotationPolicy: Always
issuerRef:
name: railgrid-hub-ca
kind: IssuerBuild the provider CA bundle from the CA certificate, plus kcp’s server CA when kcp is external:
kubectl --context HOSTING_CONTEXT -n railgrid-system get secret railgrid-hub-ca \
-o jsonpath='{.data.ca\.crt}' | base64 -d > provider-ca.pem
# External kcp only:
kubectl --context HOSTING_CONTEXT -n kcp-system get secret root-server-ca \
-o jsonpath='{.data.tls\.crt}' | base64 -d >> provider-ca.pem
kubectl --context HOSTING_CONTEXT -n railgrid-system create secret generic railgrid-provider-ca \
--from-file=ca.crt=provider-ca.pemThe bundle contains only public certificates and can live in version control. If you change it later, restart the hub and re-copy every provider’s kubeconfig Secret: the hub rewrites the minted Secrets on reconcile, but the copies in provider namespaces do not update themselves. See Provider hosting .
Use external kcp
For multiple hub replicas, or for a control plane with its own lifecycle, run kcp separately and point the hub at it with a front-proxy kubeconfig. External kcp with the kcp-operator
covers etcd, shards, the front proxy, and minting that kubeconfig. Put it in a Secret in the hub namespace with the key admin.kubeconfig:
kubectl --context HOSTING_CONTEXT -n railgrid-system create secret generic railgrid-kcp \
--from-file=admin.kubeconfig=/PATH/TO/FRONT-PROXY.kubeconfigCreate the namespace first if it does not exist. Protect this kubeconfig as an administrative credential; it is system:kcp:admin. The server address and certificate inside it must be reachable and trusted from the hub pods.
Changing kcp.embedded/kcp.external on an existing embedded installation does not migrate its data; treat that as a separate storage and control-plane migration.
Settings to review
| Setting | Why it matters |
|---|---|
hub.hubExternalURL | Client-facing origin used by hub flows; match DNS, TLS, and the OIDC redirect URI <hubExternalURL>/auth/callback. |
hub.internalURL | In-cluster origin baked into provider kubeconfigs. Must be a SAN of the serving certificate. Leave empty when providers run outside the cluster. |
hub.adminUsers | Platform administrators; empty disables the admin surface and provider onboarding. |
hub.trustedProxyCIDRs | Required behind any proxy or load balancer, or every visitor shares one rate-limit bucket. |
hub.portalFrameSources | Every apps domain whose previews the console embeds, as https://*.<domain>. |
hub.tls.existingSecret | The serving certificate, issued from a CA you keep stable. |
hub.providerCASecretName | The trust bundle every minted provider kubeconfig carries: hub CA, plus the kcp server CA for external kcp. |
hub.security.providerDelegatedTokens | platform hands providers short-lived workspace-scoped tokens instead of the caller’s bearer. Recommended for production. |
idp.issuerURL, idp.clientID | Must agree character for character with the issuer’s discovery document and the public client registered there. |
idp.linkVerifiedEmailIssuers | Lets returning users keep their account across a connector or issuer change. |
kcp.external.existingSecret | The front-proxy kubeconfig for external kcp, key admin.kubeconfig. |
replicaCount | Greater than one requires external kcp. |
image.hub.tag | Defaults to the chart’s appVersion; pin only when you need a different image. |
For defaults and additional settings, use the chart values file
. Flags the chart does not model go in hub.extraArgs; the chart refuses an entry that repeats a flag it already renders.
Installation sources
The product repo includes executable installation recipes for both modes, run in CI against a fresh kind cluster. Use the recipe and chart from the same revision:
Verify the installation from the terminal
Use the explicit hosting-cluster context to inspect the discovered workload and serving container. Follow hosting-cluster diagnostics for pod, rollout, and event commands.
curl -s https://console.example.com/healthz # {"status":"ok","oidc":true,...}
kubectl railgrid login --hub-url https://console.example.com
kubectl railgrid use
kubectl api-resourcesFor static-token deployments, follow login authentication
instead of expecting a browser flow. Success means authentication works, the intended workspace is selectable, and its APIs are discoverable. Then sign in to the console as one of adminUsers and confirm the admin area is available; provider onboarding happens there.
Next steps
Install providers , then review operations and recovery . For the complete configuration, use the chart values .