Self-Hosting on Kubernetes
Fleans ships an official Helm chart at charts/fleans/ for self-hosting the engine on any Kubernetes cluster. Aspire is the recommended local-development experience; the Helm chart is the recommended path for staging, production, and any deployment where you control your own Kubernetes.
Why a Helm chart?
Section titled “Why a Helm chart?”Aspire orchestrates the full Fleans stack on a developer laptop, but it is not a deployment tool — it does not produce Kubernetes manifests for clusters you operate. The chart fills that gap with a single artifact that:
- Wires the Orleans silos (
Coreand optionalWorker), the Blazor admin UI, and the MCP server together with the right service discovery and ports. - Provisions Redis (required for Orleans clustering) and Postgres (default workflow persistence) inside the release, with the option to disable either and bring your own.
- Centralises all tunables — image tags, replica counts, OIDC settings, ingress, Kafka streaming wiring — in one
values.yaml.
What the chart deploys
Section titled “What the chart deploys”| Component | Image | Purpose |
|---|---|---|
core Deployment | ghcr.io/nightbaker/fleans-api:<tag> | Orleans silo (Fleans__Role=Core). Hosts coordinator grains. |
worker Deployment (off by default) | ghcr.io/nightbaker/fleans-api:<tag> | Orleans silo (Fleans__Role=Worker). Hosts [StatelessWorker] script/condition grains and the read-model projector. Same image as core — distinguished only by the Fleans__Role env var. |
web Deployment | ghcr.io/nightbaker/fleans-web:<tag> | Blazor Server admin UI on port 8080. |
mcp Deployment | ghcr.io/nightbaker/fleans-mcp:<tag> | MCP server on port 5200 for AI-agent integration. |
redis StatefulSet | redis:7-alpine | Orleans clustering + grain storage. Required. |
postgres StatefulSet (default on) | postgres:16 | Workflow persistence when persistence.provider=Postgres. |
Optional resources: an Ingress for the admin UI, a chart-managed OIDC Secret, and Kafka streaming environment variables on the silos.
Note on the Worker silo. The worker role runs engine grains, not custom tasks, which go to gRPC workers. There is no
fleans-workerimage — bothcoreandworkerDeployments useimage.api. Enablingworker.enabled=truelets Orleans place stateless script/condition grains on dedicated pods (and dedicated nodes viaworker.nodeSelector/worker.tolerations) without pulling a second image.
Prerequisites
Section titled “Prerequisites”- Kubernetes 1.25+
- Helm 3.12+
- A
StorageClassif you enable persistent volumes (default for Postgres; off for Redis). - Pull access to
ghcr.io/nightbaker/*(public for the Fleans repo’s published images; provideimagePullSecretsif you mirror to a private registry).
Quick install — kind (local laptop)
Section titled “Quick install — kind (local laptop)”This is the same smoke test the chart’s CI runs. It installs the engine into a single-node kind cluster, port-forwards the admin UI, and tears everything down at the end.
# 1. Create a single-node test cluster.kind create cluster --name fleans-test
# 2. From the repo root: lint and render before installing.helm lint charts/fleans/helm template fleans charts/fleans/ --debug | head -120
# 3. Install. Disable Postgres persistence + ingress for the smoke test.helm install fleans charts/fleans/ \ --set postgres.persistentVolume.enabled=false \ --set ingress.enabled=false
# 4. Wait for everything to come up.kubectl get pods -l app.kubernetes.io/instance=fleans -w# (Ctrl-C when all show 1/1 Running.)
# 5. Reach the admin UI.kubectl port-forward svc/fleans-web 8080:8080# open http://localhost:8080/
# 6. Tear down.helm uninstall fleanskind delete cluster --name fleans-testCommon configuration
Section titled “Common configuration”Pin to a specific image tag
Section titled “Pin to a specific image tag”values.yaml defaults image.tag to the chart’s appVersion. Override on the command line for nightlies and hotfixes:
helm install fleans charts/fleans/ --set image.tag=0.1.0-rc.2Bring your own Postgres
Section titled “Bring your own Postgres”Disable the in-cluster Postgres and supply a connection string via extraEnv:
postgres: enabled: falsepersistence: provider: PostgresextraEnv: - name: ConnectionStrings__fleans valueFrom: secretKeyRef: name: my-existing-pg-secret key: connection-stringSee the Persistence reference for the connection-string contract and read-replica wiring.
Authentication (OIDC for the admin UI, JWT for REST + MCP)
Section titled “Authentication (OIDC for the admin UI, JWT for REST + MCP)”auth: authority: https://idp.example.com/realms/fleans clientId: fleans-web clientSecretExistingSecret: fleans-oidc # Secret with key `client-secret`auth.authority also reaches the core, api and mcp pods, which then require JWT bearer tokens (audience fleans-api for REST, fleans-mcp for MCP). Leave auth.authority empty to ship without authentication (single-tenant or dev). See the Authentication reference for full configuration semantics.
Kafka streaming provider
Section titled “Kafka streaming provider”streaming: provider: KafkaextraEnv: - name: Fleans__Streaming__Kafka__Brokers value: kafka.kafka.svc.cluster.local:9092See the Streaming reference for provider details and at-least-once delivery semantics.
Worker silos on dedicated nodes
Section titled “Worker silos on dedicated nodes”worker: enabled: true replicas: 3 nodeSelector: node-role: fleans-worker tolerations: - key: spot-instance operator: Exists effect: NoScheduleA best-practice production install
Section titled “A best-practice production install”A starting point for a real cluster — pinned tag, external managed Postgres, OIDC on the admin UI, ingress with TLS, and a small worker pool isolated to dedicated nodes. Save as prod-values.yaml and install with helm install fleans charts/fleans/ -f prod-values.yaml.
image: tag: 0.11.0 # pin — never let `latest` drift in prod
# Use a managed Postgres (RDS, Cloud SQL, Neon, …) — drop the in-cluster one.postgres: enabled: falsepersistence: provider: PostgresextraEnv: - name: ConnectionStrings__fleans valueFrom: secretKeyRef: name: fleans-pg key: connection-string
# Three Worker silos on a dedicated node pool.worker: enabled: true replicas: 3 nodeSelector: workload: fleans-worker
# Admin UI behind your IdP and a TLS ingress.auth: authority: https://id.example.com/realms/fleans clientId: fleans-web clientSecretExistingSecret: fleans-oidcingress: enabled: true className: nginx hosts: - host: fleans.example.com paths: - path: / pathType: Prefix tls: - secretName: fleans-tls hosts: - fleans.example.comUpgrade and lifecycle notes
Section titled “Upgrade and lifecycle notes”- Postgres password is sticky. First install generates a random password into a
Secret(or usespostgres.passwordif you supply one). Subsequenthelm upgradecalls reuse the existing Secret via thelookupfunction so you don’t get locked out. - Persistent volumes are kept on uninstall. The Postgres
Secretcarrieshelm.sh/resource-policy: keep. Delete the PVC and Secret manually for a clean reinstall. - CI guardrail. Every PR that touches
charts/is gated byhelm-lint.yml, which runshelm lintandhelm templateagainst both default values and the full feature set.
Worker packages on NuGet
Section titled “Worker packages on NuGet”Fleans publishes Fleans.ExternalTasks.Sdk to nuget.org on every tagged GitHub Release. It is the supported way to write a custom-task worker in .NET: a plain generic host that connects to the API over gRPC, with no Orleans dependency and no engine source dependency. It is not needed to run the engine itself.
| Package | Holds |
|---|---|
Fleans.ExternalTasks.Sdk | ExternalTaskHandler, ExternalTaskFailedException, ExternalTaskParameterSchema, ExternalTaskVisibility, ExternalTaskVariables, AddFleansExternalTaskWorker, AddExternalTaskHandler<T>, and the protocol file under protos/. |
Fleans.Plugins.RestCaller | Not an engine package: released from fleans-plugins on the same version track. The <serviceTask type="rest-call"> handler. |
Fleans.Worker, Fleans.Application.Abstractions and Fleans.Domain.Abstractions are no longer published.
Starter template
Section titled “Starter template”The fleans-custom-worker-example GitHub template is the supported scaffolding. Click Use this template to start your own worker repo.
How to consume them
Section titled “How to consume them”<Project Sdk="Microsoft.NET.Sdk.Worker"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Fleans.ExternalTasks.Sdk" Version="0.11.0" /> <PackageReference Include="Fleans.Plugins.RestCaller" Version="0.11.0" /> </ItemGroup></Project>Program.cs:
using Fleans.ExternalTasks.Sdk;using Fleans.Plugins.RestCaller; // the rest-call handler (fleans-plugins)
var builder = Host.CreateApplicationBuilder(args);builder.Services.AddFleansExternalTaskWorker();builder.Services.AddRestCallerPlugin();builder.Build().Run();Run it with Fleans__ExternalTaskWorker__EngineUrl set to the API’s URL, and <bpmn:serviceTask type="rest-call"> activities reach it. See Custom Tasks for the handler authoring guide and Hosting workers for production.
Release cadence
Section titled “Release cadence”The package is published by .github/workflows/nuget-publish.yml on release.published (plugin packages such as Fleans.Plugins.RestCaller are published from fleans-plugins), on the engine’s version track.
Package integrity & signatures
Section titled “Package integrity & signatures”Every Fleans package you install from nuget.org carries nuget.org’s repository signature. nuget.org applies this signature server-side, when it accepts the upload — it is not produced by the Fleans build, and it is present on the package you restore, not on a locally-packed .nupkg. The repository signature chains to a root that .NET trusts by default, so you can verify any restored Fleans package:
# Resolve the package, then verify the signature on the restored .nupkg in your global packages cache.dotnet add package Fleans.ExternalTasks.Sdkdotnet nuget verify --all \ ~/.nuget/packages/fleans.externaltasks.sdk/<version>/fleans.externaltasks.sdk.<version>.nupkgverify --all exits 0 and reports a valid repository signature; it fails (NU3004) if the package is unsigned or has been tampered with after nuget.org signed it.
What this guarantees, and what it does not. The repository signature gives you integrity — the package is exactly what nuget.org accepted and has not been altered in transit or on the feed. It does not assert publisher identity: it proves nuget.org holds the package, not that the Fleans project specifically signed it. For a pre-1.0 project this is a deliberate trade-off — repository signing delivers the tamper-resistance most consumers need at zero setup, and publisher (author) signing is a planned follow-up (#728) for when publisher-authenticated signatures become worth the certificate onboarding. Until then, no project-managed signing certificate or per-consumer trust step is required.
Limitations
Section titled “Limitations”- SQLite is not supported in production. SQLite is the default
persistence.providerfor local Aspire-based dev only. The chart accepts the override but you would have to wire a writable per-pod volume yourself, and Orleans clustering across silos against a per-pod SQLite file is not a supported configuration. Use Postgres for any multi-replica deployment. - No cosign image signing yet. Signing of the published images is tracked in #410.
- Chart not yet published to an OCI registry. Install from the repo (
helm install fleans charts/fleans/) or from ahelm package-produced.tgzattached to the matching GitHub Release.
See also
Section titled “See also”- Observability — health checks, metrics, logging, tracing, dashboards, alerting