Hosting Your Own Custom-Task Plugins
When you ship a custom-task plugin (e.g. an in-house “send-slack-notification” plugin), you typically want it to run in your own deployable — not inside the Fleans engine image.
The fleans-custom-worker-example
repository is a GitHub template that shows the minimum-viable shape for that deployable:
a Web-SDK Exe that boots an Orleans silo with Fleans:Role=Worker, references only
Fleans.Worker and the chosen plugin assemblies via NuGet, and joins the same Orleans
cluster as the engine via Redis clustering.
Quick start
Section titled “Quick start”Click Use this template on the example repo, or via gh CLI:
gh repo create my-plugin-host --template nightBaker/fleans-custom-worker-example --publiccd my-plugin-hostConnectionStrings__orleans-redis="localhost:6379" dotnet run --project src/Fleans.CustomWorkerHostThat gives you a working silo claiming the bundled Fleans.Plugins.RestCaller handler.
Swap in your own plugin (see Writing Custom-Task Plugins)
and ship the resulting container image alongside the engine.
Why a separate host?
Section titled “Why a separate host?”- Operational isolation — you can deploy, scale, and roll back your plugin worker independently from the engine. Engine releases don’t force you to rebuild the plugin worker; plugin source changes don’t force you to redeploy the engine.
- Reference hygiene — the example template depends only on
Fleans.Worker(a leaf NuGet) and the plugin packages. NoFleans.Application/Fleans.Domain/Fleans.Infrastructure/ persistence references — structurally guaranteed by a CI leaf-package guard in the template repo. This means your plugin host cannot accidentally execute engine grains. - Independent release cadence — your plugin host repo lives outside the engine repo, so you bump it on your own schedule and contributors don’t need engine commit access.
Configure the shared reminder service
Section titled “Configure the shared reminder service”Your plugin host joins the engine’s Orleans cluster, so it owns part of the cluster’s
reminder ring and serves some of the engine’s BPMN-timer reminders. Configure it with the
same reminder provider as the engine — by default the Redis reminder table on the
orleans-redis connection (Microsoft.Orleans.Reminders.Redis,
siloBuilder.UseRedisReminderService(...)). Without a reminder service, timers whose range
lands on your host fail to register and their activities fail; with an in-memory one
(UseInMemoryReminderService()), those timers are lost whenever your host restarts.
Plugin-author NuGet stack
Section titled “Plugin-author NuGet stack”Three NuGet packages compose the plugin-author surface, layered strictly:
Fleans.Worker (which pulls in the abstraction packages); a host built from the template references it plus your plugins and joins the engine's Orleans cluster. Open interactive diagram ↗Fleans.Worker → Fleans.Application.Abstractions → Fleans.Domain.Abstractions| Package | Holds |
|---|---|
Fleans.Domain.Abstractions | IDomainEvent, ExecuteCustomTaskEvent, InputMapping/OutputMapping, CustomTaskFailedActivityException + exception hierarchy. True leaf — depends only on Microsoft.Orleans.Sdk. |
Fleans.Application.Abstractions | Grain interfaces (IScriptExecutorGrain, IConditionExpressionEvaluatorGrain, ICustomTaskCatalogGrain, narrow IWorkflowInstanceCallback), CustomTaskParameterSchema, MappingResolver, WorkflowEventStreams, WorkflowLoggingContext. |
Fleans.Worker | CustomTaskHandlerBase, [WorkerPlacement], placement directors. |
Your plugin host references Fleans.Worker + each plugin package; the rest comes in
transitively. Verify the closure with:
dotnet list package --include-transitive --project src/Fleans.CustomWorkerHost | grep -i fleansYou should see only Fleans.*.Abstractions, Fleans.Worker, and your plugin packages — no
Fleans.Application or Fleans.Domain (without the .Abstractions suffix).
Adding a new plugin
Section titled “Adding a new plugin”- Author your plugin grain by deriving from
CustomTaskHandlerBase(see Writing Custom-Task Plugins). - Publish your plugin as a NuGet package, or
<ProjectReference>if it lives in your monorepo. - Add the reference to your
CustomWorkerHostcsproj. - Add
services.AddYourPlugin();to the registration block inProgram.cs. - Rebuild and redeploy the worker container.
Plugin SemVer caveat
Section titled “Plugin SemVer caveat”Plugin 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 asAspire.Hosting.*/Microsoft.Orleans.*). Pin to a major+minor that matches the engine you’re deploying alongside.Upgrade the host together with the engine. A plugin host reads a share of the engine’s stream queues and must deserialize every event on them, not just its own custom-task events. If an engine release adds a stream event type, a host on an older
Fleans.Workercannot read it. On Kafka that event is then dropped (or dead-lettered) and the workflow instance waiting for it hangs.
Cross-references
Section titled “Cross-references”fleans-custom-worker-example— the GitHub template repo.- Custom Tasks concept page — the BPMN-side picture.
- Writing Custom-Task Plugins — handler authoring.