Self-host Infrastructure
Deployment prerequisites, verification, and recovery for Infrastructure.
The infrastructure provider turns templates into running workloads on a runtime cluster through kro
, and publishes applications and App Studio previews under a wildcard domain through a Gateway API Gateway. Railgrid runs it in operator mode: the chart installs a small operator and one InfrastructureProvider resource, and the operator installs kro, bootstraps the provider workspace, and manages the serving Deployment.
Prerequisites
- A Kubernetes runtime you administer, Helm, and permission to onboard this provider. Use the common provider flow to obtain the workspace kubeconfig.
- A Gateway API
Gatewaywhose HTTPS listener accepts routes from all namespaces; the provider creates oneHTTPRouteper published app in each tenant’s runtime namespace. See Gateway API on GKE or Cloudflare Tunnel . - An apps domain with a wildcard DNS record and a wildcard certificate at your edge, for example
*.apps.example.com. Each app gets<name>-<tenant hash>.apps.example.com. Below a Cloudflare zone apex, Universal SSL does not cover a second label; use a zone apex or an Advanced Certificate wildcard. hub.portalFrameSourceson the hub listinghttps://*.apps.example.com, or the console blocks preview iframes.- A CNI that enforces
NetworkPolicyif you enable tenant network isolation.
Install kro once, as cluster-admin
The operator installs kro with helm upgrade --install, but kro’s ClusterRole grants */*/*, and Kubernetes lets a subject create a role only with permissions it already holds. The operator’s enumerated role does not hold them, so on a cluster without kro the InfrastructureProvider sticks at KroReleased=False (HelmFailed) ... attempting to grant RBAC permissions not currently held, the serving Deployment is never created, and the provider never appears in the console.
Install kro once yourself with the operator’s exact arguments; the operator’s later identical upgrades then leave the role untouched and succeed. Prefer this to operator.clusterAdmin: true, which would also bind cluster-admin to the ServiceAccount that handles tenant requests.
helm upgrade --install kro oci://registry.k8s.io/kro/charts/kro \
--version 0.9.3 --namespace kro-system --create-namespace --reset-values \
--set image.repository=registry.k8s.io/kro/kro --waitKeep --version equal to operator.kro.version below, and repeat this when a new kro version changes its ClusterRole. If the operator is already retrying, another operation is in progress means you raced it; run the command again.
Install
kubectl create namespace railgrid-infrastructure-operator
kubectl --namespace railgrid-infrastructure-operator create secret generic infrastructure-provider-kubeconfig \
--from-file=kubeconfig=./infrastructure.kubeconfiginfrastructure-values.yaml:
hub:
url: https://railgrid-hub.railgrid-system.svc.cluster.local:9443
tokenSecretRef:
name: ""
operator:
enabled: true
providerKubeconfigSecret:
name: infrastructure-provider-kubeconfig
key: kubeconfig
kro:
version: 0.9.3
image:
repository: registry.k8s.io/kro/kro
application:
# Zone apps are served under. Required to enable app exposure.
baseDomain: apps.example.com
gateway:
name: railgrid
namespace: gateway
# GKE only: a TCP HealthCheckPolicy per route backend. Without it the
# load balancer's default "GET /" check never passes the access gate.
backendHealthCheck: gke-tcp
publishing:
# Pin the access gate image; it fronts every published app.
accessProxyImage: ghcr.io/railgrid/railgrid-access-proxy:v0.2.1
# Browser-reachable hub origin for sign-in redirects.
hubPublicURL: https://console.example.com
insecure: truehelm upgrade --install infrastructure oci://ghcr.io/railgrid/charts/railgrid-infrastructure-provider \
--version CHART_VERSION --namespace railgrid-infrastructure-operator --values infrastructure-values.yamlThe operator creates the serving Deployment in its own namespace, railgrid-infrastructure-provider, on the runtime cluster, and reports progress on the InfrastructureProvider resource.
Provider requirements
One Gateway, every namespace. Published apps are HTTPRoutes in tenant namespaces. The Gateway’s listener must allow routes from all namespaces, and its wildcard certificate must cover the apps domain.
The access gate. Every published app’s traffic enters through railgrid-access-proxy, which answers 421 to a request whose Host is not the app’s and redirects private apps to the hub for sign-in. Health checks that expect 200 on / therefore fail; on GKE set backendHealthCheck: gke-tcp (chart 0.2.34 and later). A freshly published app answers 404 for a few minutes while a cloud load balancer programs its route.
Runtime kubeconfig. In operator mode on the hub’s cluster, the operator uses its own ServiceAccount for the runtime; set operator.runtimeKubeconfigSecret only to target a different cluster. The non-operator chart form takes centralKro.kubeconfigSecretRef instead; prefer a Secret reference over inline kubeconfigs in production.
Tenant isolation. tenantLimitRange stamps default container resources into every tenant namespace (on by default). tenantNetworkPolicy.enabled adds an ingress NetworkPolicy per tenant namespace; list your Gateway proxies’ namespace and the kube-apiserver source range in allowedNamespaces / allowedCIDRs, or dev-sandbox sync and logs time out.
Development sandboxes run tenant code. Pin development.agentImage and any development.images.* by digest in production, and set sandbox.runtimeClassName (gvisor or kata) before exposing App Studio to untrusted users.
Verify
kubectl -n railgrid-infrastructure-operator get infrastructureprovider \
-o jsonpath='{range .items[*].status.conditions[*]}{.type}={.status}{"\n"}{end}' # all True
kubectl -n railgrid-infrastructure-provider get pods # the serving Deployment
kubectl -n kro-system get podsA fresh provider stays unready until a workspace enables it; see the common flow
. Enable Infrastructure in a test workspace, list the Template catalog, and create one disposable Instance using the Infrastructure quickstart
. Then publish one app and open its URL: a redirect to the console sign-in is the expected answer for a private app.
Recover
| Symptom | Check | Fix |
|---|---|---|
KroReleased=False, permissions not currently held | kro not pre-installed | Install kro as cluster-admin (above) |
No pods in railgrid-infrastructure-provider | InfrastructureProvider conditions | Fix the failing condition; the operator retries about every 12 seconds |
Published app: no healthy upstream | Load-balancer health check vs the access gate | backendHealthCheck: gke-tcp, or an equivalent TCP check |
| Published app: 404 with a valid certificate | Route not yet programmed, or the Gateway rejects routes from the tenant namespace | Wait a few minutes; check allowedRoutes.namespaces |
| Published app: TLS failure | No wildcard certificate for the apps domain | Add the wildcard at the edge |
| Preview iframe blocked in the console | Hub portalFrameSources | Add https://*.<apps domain> |
For a failed upgrade, preserve both hub control-plane resources and runtime objects. Inspect kro and provider events, restore the provider kubeconfig Secret, then verify the Template catalog and one disposable Instance before resuming user workloads.
Configuration reference
Chart values and the chart README are authoritative for the deployed version. Keep the deployed image, chart, kro, and kcp versions recorded together: the provider and kcp move in lockstep .