Host Orleans on Kubernetes
Kubernetes can host Orleans when pods have direct network connectivity and the application uses a production clustering provider. Orleans doesn’t require a Kubernetes-specific hosting package. The recommended approach is to configure each silo explicitly from the pod name and pod IP supplied by the Kubernetes downward API.
Configure the silo
Section titled “Configure the silo”Reference Microsoft.Orleans.Server and a production clustering provider package. Configure the pod IP as the advertised address, listen on all pod interfaces, and use the pod name as the silo name:
using System.Net;var builder = WebApplication.CreateBuilder(args);
var podName = builder.Configuration["POD_NAME"] ?? throw new InvalidOperationException("POD_NAME isn't configured.");var podIp = IPAddress.Parse( builder.Configuration["POD_IP"] ?? throw new InvalidOperationException("POD_IP isn't configured."));
builder.Host.UseOrleans(siloBuilder =>{ siloBuilder // Configure one production clustering provider here. .Configure<ClusterOptions>(options => { options.ServiceId = builder.Configuration["ORLEANS_SERVICE_ID"] ?? throw new InvalidOperationException("ORLEANS_SERVICE_ID isn't configured."); options.ClusterId = builder.Configuration["ORLEANS_CLUSTER_ID"] ?? throw new InvalidOperationException("ORLEANS_CLUSTER_ID isn't configured."); }) .Configure<SiloOptions>(options => options.SiloName = podName) .ConfigureEndpoints( advertisedIP: podIp, siloPort: 11_111, gatewayPort: 30_000, listenOnAnyHostAddress: true);});
builder.Services.Configure<HostOptions>(options =>{ options.ShutdownTimeout = TimeSpan.FromSeconds(120);});
var app = builder.Build();
// Map application-owned startup, readiness, and liveness endpoints.
app.Run();All silos and clients must use the same service ID, cluster ID, and clustering provider. The example explicitly uses silo port 11111 and gateway port 30000.
Apply a production baseline
Section titled “Apply a production baseline”The following baseline intentionally omits the clustering provider credentials and application ingress. Supply those resources using workload identity and provider-specific configuration.
apiVersion: apps/v1kind: Deploymentmetadata: name: dictionary-appspec: replicas: 3 minReadySeconds: 30 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 0 maxSurge: 1 selector: matchLabels: app.kubernetes.io/name: dictionary-app template: metadata: labels: app.kubernetes.io/name: dictionary-app spec: automountServiceAccountToken: false terminationGracePeriodSeconds: 150 containers: - name: app image: registry.example.com/dictionary-app:10.0.0 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 8080 protocol: TCP - name: silo containerPort: 11111 protocol: TCP - name: gateway containerPort: 30000 protocol: TCP env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: ORLEANS_SERVICE_ID value: dictionary-app - name: ORLEANS_CLUSTER_ID value: production startupProbe: httpGet: path: /health/startup port: http periodSeconds: 5 timeoutSeconds: 2 failureThreshold: 60 readinessProbe: httpGet: path: /health/ready port: http periodSeconds: 5 timeoutSeconds: 2 failureThreshold: 2 livenessProbe: httpGet: path: /health/live port: http periodSeconds: 10 timeoutSeconds: 2 failureThreshold: 3 resources: requests: cpu: 500m memory: 512Mi limits: memory: 1Gi---apiVersion: policy/v1kind: PodDisruptionBudgetmetadata: name: dictionary-appspec: minAvailable: 2 selector: matchLabels: app.kubernetes.io/name: dictionary-appApply the manifest in a namespace dedicated or appropriately shared for the application:
kubectl apply --namespace <namespace> --filename orleans.yamlThe baseline disables service account token mounting because Orleans doesn’t need to call the Kubernetes API. Add a service account and permissions only for application features that require them.
Deploy with Aspire
Section titled “Deploy with Aspire”Aspire can model an Orleans application and its backing resources in an AppHost. The Aspire.Hosting.Kubernetes integration can publish that application model as a Helm chart or deploy it to the Kubernetes cluster selected by the current kubectl context.
Aspire doesn’t change the Orleans networking and lifecycle requirements described on this page. Before deploying generated resources:
- Configure a production clustering provider and durable state providers in the Orleans resource model.
- Run multiple silo replicas across failure domains.
- Ensure each silo receives its own pod name and pod IP and advertises that pod IP.
- Expose the silo and gateway container ports to the required pod and client networks.
- Add application-owned startup, readiness, and liveness probes.
- Set resource requests, disruption budgets, rollout policy, and termination grace periods from measured behavior.
Review the generated Helm chart before applying it. Use the Kubernetes integration’s resource customization APIs when the generated workload doesn’t include these Orleans-specific requirements.
For the supported APIs and deployment commands, see Orleans and Aspire integration, Aspire Kubernetes integration, and Deploy with Aspire.
Optional Kubernetes hosting package
Section titled “Optional Kubernetes hosting package”The Microsoft.Orleans.Hosting.Kubernetes package is optional and isn’t generally recommended. Consider it only for a simple topology where exactly one Kubernetes Deployment object owns exactly one Orleans cluster.
For the supported simple topology, UseKubernetesHosting configures the silo name, pod IP, listening endpoints, service ID, and cluster ID from pod metadata. It also calls the Kubernetes API to reconcile Orleans membership with matching pods. It supplements a production clustering provider; it doesn’t replace one.
To use it:
- Reference
Microsoft.Orleans.Hosting.Kubernetesand callUseKubernetesHosting(). - Add
orleans/serviceIdandorleans/clusterIdlabels to theDeploymentpod template. - Supply
POD_NAME,POD_NAMESPACE,POD_IP,ORLEANS_SERVICE_ID, andORLEANS_CLUSTER_IDthrough the downward API. - Mount a dedicated service account token and grant this namespace-scoped role:
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: orleans-hostingrules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch", "patch"]Bind the role to the workload’s dedicated service account. If you explicitly enable DeleteDefunctSiloPods, also grant delete. Keep that option disabled unless its operational consequences have been reviewed.
Network requirements
Section titled “Network requirements”Allow direct pod-IP TCP traffic:
- Every silo pod to port
11111on every silo pod. - Every Orleans client to port
30000on every silo pod. - Every silo and client to the clustering provider.
- Silo pods to the Kubernetes API only when the optional hosting package is enabled.
Don’t place a Kubernetes Service virtual IP in AdvertisedIPAddress. Orleans advertises each pod IP so peers can contact that specific silo. A Service can expose application HTTP ingress, but it doesn’t replace Orleans membership or direct silo connectivity.
If a service mesh intercepts TCP, validate long-lived connections, pod-address preservation, mutual TLS policy, shutdown ordering, and retries. Exclude Orleans ports from interception if the mesh can’t preserve the required semantics.
Health probes
Section titled “Health probes”The three paths in the manifest are startup, readiness, and liveness probes that the application must implement according to Health and observability:
- Startup succeeds after the silo joins the cluster and required initialization completes.
- Readiness becomes false before shutdown and whenever the application can’t safely accept new traffic.
- Liveness performs only a local forward-progress check and doesn’t call grains or dependencies.
The five-minute startup allowance is an example, not a universal default. Set it from measured startup and recovery time. Aggressive probes can turn provider latency or CPU pressure into a cluster-wide restart loop.
Resource requests and limits
Section titled “Resource requests and limits”Set container resource requests and limits from measurements under representative load, including a silo loss and rolling deployment. Kubernetes uses requests for scheduling and uses limits to constrain a running container; they aren’t interchangeable capacity settings.
- CPU requests reserve scheduling capacity. If requests are too low, Kubernetes can place too many silos on one node and create contention during bursts or failover.
- CPU limits can throttle the .NET process even when the node has spare CPU. Tail latency, membership probes, garbage collection, and activation recovery can all suffer under throttling. The baseline intentionally specifies a CPU request without a CPU limit.
- Memory requests should cover a representative working set, including activations, caches, serialization buffers, and expected failover growth.
- Memory limits are enforced by terminating the container when it exceeds the cgroup limit. Leave headroom for bursts and failover; don’t rely on a managed OutOfMemoryException or graceful shutdown.
Monitor CPU throttling, working set, allocation rate, garbage collection, scheduler delay, activation count, and pod restarts. Revisit resource settings when grain state, placement, traffic, runtime, or node sizes change.
Namespace ResourceQuota and LimitRange policies can reject pods or inject defaults. Verify the effective pod specification after admission rather than assuming the submitted manifest is what runs.
For workload sizing, overload protection, and scaling signals, see Capacity planning and scaling.
Shutdown, rollouts, and scaling
Section titled “Shutdown, rollouts, and scaling”Kubernetes sends SIGTERM and begins the pod termination grace period. The .NET host must observe the signal, report not ready, and receive enough time to stop Orleans gracefully. In the example, the 150-second pod grace period is longer than the 120-second host shutdown timeout.
Keep maxUnavailable: 0 and some surge capacity when the cluster can’t tolerate losing a ready silo during rollout. The PodDisruptionBudget protects against voluntary disruptions, not node failure.
Scale in gradually. Verify that membership stabilizes and remaining silos have capacity before removing more pods. See Graceful shutdown and upgrades.
Troubleshoot Kubernetes hosting
Section titled “Troubleshoot Kubernetes hosting”For explicit endpoint configuration, compare POD_NAME, POD_IP, service ID, cluster ID, and the advertised membership endpoint.
If the optional hosting package reports missing KUBERNETES_SERVICE_HOST or KUBERNETES_SERVICE_PORT, verify that the process is running in a pod and that service environment links haven’t been disabled. If the API returns 403 Forbidden, check the pod’s service account, role binding namespace, and required pod verbs.
For broader triage, see Troubleshoot deployments.
