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. 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.
Fleans.Workeris a class library hosted byFleans.Api, not a separate executable. There is nofleans-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.
OIDC for the admin UI
Section titled “OIDC for the admin UI”auth: authority: https://idp.example.com/realms/fleans clientId: fleans-web clientSecretExistingSecret: fleans-oidc # Secret with key `client-secret`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.1.0-beta # 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.
Plugin packages on NuGet
Section titled “Plugin packages on NuGet”Fleans publishes four plugin-author packages to nuget.org on every tagged GitHub Release. They are the supported way to write a custom-task plugin or stand up your own plugin host without depending on the Fleans repo as a Git submodule. The packages are layered strictly so plugin authors get only what they need:
Fleans.Worker → Fleans.Application.Abstractions → Fleans.Domain.Abstractions| Package | When to use it |
|---|---|
Fleans.Domain.Abstractions | True leaf — depends only on Microsoft.Orleans.Sdk. Holds IDomainEvent, ExecuteCustomTaskEvent, InputMapping/OutputMapping, CustomTaskFailedActivityException + the exception hierarchy. Pulled in transitively by Fleans.Application.Abstractions; rarely referenced directly. |
Fleans.Application.Abstractions | Grain interfaces (script/condition/custom-task/narrow IWorkflowInstanceCallback), schema records, mapping resolver, stream constants. Pulled in transitively by Fleans.Worker; rarely referenced directly. |
Fleans.Worker | Worker-side primitives: CustomTaskHandlerBase, [WorkerPlacement], the placement directors. Reference this from any project that defines a custom-task plugin. |
Fleans.Plugins.RestCaller | Worked-example plugin — the <serviceTask type="rest-call"> HTTP caller. Useful as a copy-template for new plugins, or to register the REST caller directly in your own worker host. |
Starter template
Section titled “Starter template”The fleans-custom-worker-example GitHub template is the supported scaffolding for “host your own custom-task plugins”. Click Use this template to start your own plugin host repo — see the custom-worker-host guide for details.
All three packages share the engine’s <VersionPrefix> track — every Fleans release bumps every plugin’s NuGet version even when the plugin source is bit-identical (same precedent as Aspire.Hosting.* and Microsoft.Orleans.*). Pin to the same version across the three when you upgrade.
Why we publish to NuGet
Section titled “Why we publish to NuGet”A plugin is just a class that derives from Fleans.Worker.CustomTasks.CustomTaskHandlerBase. Pre-NuGet, plugin authors had to either fork the engine repo or vendor Fleans.Worker.csproj as a submodule — both forced a coupling between plugin code and the engine repo’s branch layout. Publishing the three plugin packages to nuget.org makes the plugin host a normal .NET project: dotnet new console, dotnet add package Fleans.Worker, write your handler, ship.
The packages include SourceLink + .snupkg symbols, so debugging into the engine’s worker primitives Just Works in Visual Studio / Rider with “Enable Source Link” + “Enable source server support” turned on.
How to consume them
Section titled “How to consume them”The minimum plugin-host project file:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <!-- Pin all three to the same Fleans engine version. --> <PackageReference Include="Fleans.Worker" Version="0.1.0-beta" /> <PackageReference Include="Fleans.Plugins.RestCaller" Version="0.1.0-beta" /> </ItemGroup></Project>A minimal Program.cs for a custom worker host:
using Fleans.Plugins.RestCaller; // ships the rest-call handler
var builder = Host.CreateApplicationBuilder(args);
builder.UseOrleans(silo =>{ silo.UseRedisClustering(opts => opts.ConfigurationOptions = ...); silo.AddRedisGrainStorageAsDefault(opts => opts.ConfigurationOptions = ...); silo.Configure<SiloOptions>(o => o.SiloName = $"plugin-host-{Environment.MachineName}");});
// Register every plugin you ship in this host.builder.Services.AddRestCallerPlugin();
await builder.Build().RunAsync();When the silo joins the Orleans cluster, <bpmn:serviceTask type="rest-call"> activities are automatically claimed by your plugin handler. See Custom Tasks for the handler authoring guide and the fleans-custom-worker-example GitHub template for a complete worked example.
Release cadence
Section titled “Release cadence”The packages are published by .github/workflows/nuget-publish.yml on release.published. The workflow can also be invoked manually with workflow_dispatch and version=0.0.0-ci-test to dry-run pack + upload-artifact without touching nuget.org. Re-runs against an already-published version are no-ops via dotnet nuget push --skip-duplicate.
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