Silo Configuration
Section titled “Silo Configuration”Complete guide to the siloConfig { } computation expression.
Current API.
siloConfig { }configures hosting; functional definitions bind the named providers configured here.
What you’ll learn
Section titled “What you’ll learn”- How to configure clustering, storage, streaming, and reminders
- TLS/mTLS for secure silo communication
- Dashboard, health checks, and startup tasks
- Grain versioning, collection age, and endpoints
- Call filters and grain services
Overview
Section titled “Overview”The siloConfig { } CE builds a SiloConfig record. Apply it to a host with SiloConfig.applyToHost:
open Orleans.FSharp.Runtime
let config = siloConfig { useLocalhostClustering addMemoryStorage "Default"}
let builder = HostApplicationBuilder()SiloConfig.applyToHost config builderlet host = builder.Build()host.Run()Or apply directly to an ISiloBuilder with SiloConfig.applyToSiloBuilder.
Startup validation
Section titled “Startup validation”Functional definitions are checked at ServiceLifecycleStage.First, before Orleans reaches
RuntimeInitialize. The checks cover the local manifest, serializers, named providers, and
functional configuration limits. A rejected definition therefore fails before that runtime
stage starts background services; it does not leave them running after a failed startup.
Register the required providers and configure their options before building the host. Startup callbacks are not a place to finish these registrations.
Clustering
Section titled “Clustering”Localhost (development)
Section titled “Localhost (development)”siloConfig { useLocalhostClustering }Requires Microsoft.Orleans.Clustering.Redis.
let connStr = Environment.GetEnvironmentVariable("REDIS_CONNECTION")
siloConfig { addRedisClustering connStr addMemoryStorage "Default"}Azure Table
Section titled “Azure Table”Requires Microsoft.Orleans.Clustering.AzureStorage.
let connStr = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION")
siloConfig { addAzureTableClustering connStr addMemoryStorage "Default"}ADO.NET (PostgreSQL, SQL Server)
Section titled “ADO.NET (PostgreSQL, SQL Server)”Requires Microsoft.Orleans.Clustering.AdoNet.
let connStr = Environment.GetEnvironmentVariable("POSTGRES_CONNECTION")
siloConfig { addAdoNetClustering connStr "Npgsql" addMemoryStorage "Default"}The second argument is the ADO.NET provider invariant. Common values: "Npgsql" (PostgreSQL), "System.Data.SqlClient" (SQL Server), "MySql.Data.MySqlClient" (MySQL).
Kubernetes
Section titled “Kubernetes”Use the Kubernetes module for automatic discovery via the Kubernetes API:
open Orleans.FSharp.Kubernetes
siloConfig { // Use CustomClustering to wire Kubernetes hosting addCustomStorage "Default" (fun sb -> sb)}
// Apply Kubernetes clustering separately on the ISiloBuilderKubernetes.useKubernetesClustering siloBuilder |> ignoreRequires Microsoft.Orleans.Hosting.Kubernetes.
Storage Providers
Section titled “Storage Providers”In-memory
Section titled “In-memory”Data is lost on silo restart. Good for development and testing.
siloConfig { addMemoryStorage "Default" }Requires Microsoft.Orleans.Persistence.Redis.
let connStr = Environment.GetEnvironmentVariable("REDIS_CONNECTION")
siloConfig { addRedisStorage "Default" connStr }Azure Blob
Section titled “Azure Blob”Requires Microsoft.Orleans.Persistence.AzureStorage.
let connStr = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION")
siloConfig { addAzureBlobStorage "Default" connStr }Azure Table
Section titled “Azure Table”Requires Microsoft.Orleans.Persistence.AzureStorage.
siloConfig { addAzureTableStorage "Default" connStr }ADO.NET
Section titled “ADO.NET”Requires Microsoft.Orleans.Persistence.AdoNet.
siloConfig { addAdoNetStorage "Default" connStr "Npgsql" }Cosmos DB
Section titled “Cosmos DB”Requires Microsoft.Orleans.Persistence.Cosmos.
let endpoint = Environment.GetEnvironmentVariable("COSMOS_ENDPOINT")
siloConfig { addCosmosStorage "Default" endpoint "MyDatabase" }DynamoDB
Section titled “DynamoDB”Requires Microsoft.Orleans.Persistence.DynamoDB.
siloConfig { addDynamoDbStorage "Default" "us-east-1" }Custom
Section titled “Custom”For any storage provider not covered above:
siloConfig { addCustomStorage "MyStore" (fun siloBuilder -> siloBuilder.AddMyCustomStorage("MyStore") )}Multiple providers
Section titled “Multiple providers”You can register multiple storage providers with different names:
siloConfig { useLocalhostClustering addMemoryStorage "Default" addRedisStorage "Cache" redisConnStr addAzureBlobStorage "Archive" azureConnStr}Bind provider names to functional persistent-state descriptors, then attach a descriptor to a definition:
let cachedState = PersistentState.create<MyState> "state" "Cache"let archivedState = PersistentState.create<MyState> "state" "Archive"
let cachedDefinition = grainFor cachedContract { stateFrom cachedState // handlers call context.persistentState cachedState when they read or write}Streaming
Section titled “Streaming”In-memory streams
Section titled “In-memory streams”siloConfig { useLocalhostClustering addMemoryStorage "Default" addMemoryStreams "StreamProvider"}A PubSubStore memory storage is automatically added if not already configured.
Persistent streams
Section titled “Persistent streams”For durable streams backed by queues (Event Hubs, Redis, etc.):
siloConfig { addPersistentStreams "MyStreams" adapterFactory configurator}Event Hubs
Section titled “Event Hubs”Use the StreamProviders module:
open Orleans.FSharp.StreamProviders
let connStr = Environment.GetEnvironmentVariable("EVENTHUB_CONNECTION")let configureFn = StreamProviders.addEventHubStreams "EventHubProvider" connStr "my-hub"
// Apply to siloBuilder directlyconfigureFn siloBuilder |> ignoreAzure Queue
Section titled “Azure Queue”let configureFn = StreamProviders.addAzureQueueStreams "QueueProvider" azureConnStrconfigureFn siloBuilder |> ignoreBroadcast channels
Section titled “Broadcast channels”Broadcast channels deliver messages to ALL subscriber grains (fan-out):
siloConfig { addBroadcastChannel "Notifications"}Reminders
Section titled “Reminders”In-memory (development)
Section titled “In-memory (development)”siloConfig { addMemoryReminderService }Requires Microsoft.Orleans.Reminders.Redis.
siloConfig { addRedisReminderService connStr }Custom
Section titled “Custom”siloConfig { addCustomReminderService (fun siloBuilder -> siloBuilder.UseMyReminderService() )}TLS / mTLS
Section titled “TLS / mTLS”TLS by subject name
Section titled “TLS by subject name”Requires Microsoft.Orleans.Connections.Security.
siloConfig { useLocalhostClustering useTls "CN=my-silo-cert"}TLS with certificate instance
Section titled “TLS with certificate instance”let cert = new X509Certificate2("path/to/cert.pfx", "password")
siloConfig { useLocalhostClustering useTlsWithCertificate cert}Mutual TLS (mTLS)
Section titled “Mutual TLS (mTLS)”siloConfig { useLocalhostClustering useMutualTls "CN=my-silo-cert"}Mutual TLS with certificate instance
Section titled “Mutual TLS with certificate instance”siloConfig { useLocalhostClustering useMutualTlsWithCertificate cert}Warning: Always use valid certificates from a trusted CA in production. Never disable certificate validation in production environments.
Dashboard
Section titled “Dashboard”Requires Microsoft.Orleans.Dashboard.
Default options
Section titled “Default options”siloConfig { useLocalhostClustering addDashboard}Custom options
Section titled “Custom options”siloConfig { useLocalhostClustering addDashboardWithOptions 5000 // counter update interval (ms) 100 // history length false // hide trace}Map the dashboard endpoints in your ASP.NET Core pipeline with MapOrleansDashboard().
See Orleans Dashboard for the runnable functional-actor example, exact UI names, and production authorization guidance.
Health Checks
Section titled “Health Checks”siloConfig { useLocalhostClustering enableHealthChecks}enableHealthChecks registers separate Orleans liveness and readiness checks; it does not map
HTTP endpoints. Keep those probes separate in an ASP.NET Core host:
open Systemopen Microsoft.AspNetCore.Diagnostics.HealthChecksopen Microsoft.AspNetCore.Builderopen Microsoft.Extensions.Diagnostics.HealthChecksopen Orleans.FSharp.Runtime
let onlyTag tag = let options = HealthCheckOptions() options.Predicate <- Func<HealthCheckRegistration, bool>(fun registration -> registration.Tags |> Seq.contains tag) options
app.MapHealthChecks( "/health/live", onlyTag OrleansHealthChecks.LivenessTag)|> ignore
app.MapHealthChecks( "/health/ready", onlyTag OrleansHealthChecks.ReadinessTag)|> ignoreLiveness stays healthy throughout normal silo startup and shutdown and becomes unhealthy only when
Orleans reports the local silo as Dead. Readiness is healthy only while the silo is Active.
Do not use an unfiltered endpoint as a Kubernetes liveness probe: it would also execute readiness
and could restart a healthy process while it is joining or draining. Use
OrleansHealthChecks.addSiloChecks when configuring services without the siloConfig builder.
Startup Tasks
Section titled “Startup Tasks”Run code when the silo starts:
siloConfig { useLocalhostClustering addStartupTask (fun sp ct -> task { let logger = sp.GetRequiredService<ILogger<_>>() logger.LogInformation("Silo started!") } :> Task)}Multiple startup tasks accumulate and run in registration order.
Grain Versioning
Section titled “Grain Versioning”Control how grains of different versions communicate during rolling upgrades:
open Orleans.FSharp.Versioning
siloConfig { useLocalhostClustering useGrainVersioning BackwardCompatible AllCompatibleVersions}Functional contract version is published as this native Orleans interface version. During a
rolling update, Orleans’ compatibility/selector pair decides which silo can receive the call;
the selected functional host then applies its own acceptsVersions envelope policy. Configure
both layers consistently. StrictVersion plus the default functional Exact policy is exact at
both boundaries.
Compatibility strategies:
| Strategy | Description |
|---|---|
BackwardCompatible |
Older versions can call newer (default) |
StrictVersion |
Only exact version matches |
AllVersions |
All versions interoperate |
Version selector strategies:
| Strategy | Description |
|---|---|
AllCompatibleVersions |
Random among compatible (default) |
LatestVersion |
Always activate latest |
MinimumVersion |
Always activate oldest |
Grain Collection Age
Section titled “Grain Collection Age”Set the global idle timeout before grain deactivation:
siloConfig { useLocalhostClustering grainCollectionAge (TimeSpan.FromMinutes 30.)}grainCollectionAge is the global idle-deactivation timeout. A functional definition can override it with the collectionAge operation in grainFor { }.
Endpoints
Section titled “Endpoints”Cluster identity
Section titled “Cluster identity”siloConfig { useLocalhostClustering clusterId "my-cluster" serviceId "my-service" siloName "silo-1"}Ports and IP
Section titled “Ports and IP”siloConfig { useLocalhostClustering siloPort 11111 gatewayPort 30000 advertisedIpAddress "10.0.0.1"}Call Filters
Section titled “Call Filters”Incoming filters
Section titled “Incoming filters”Intercept calls arriving at a grain:
open Orleans.FSharp
let loggingFilter = Filter.incoming (fun ctx -> task { printfn "Incoming call: %s" (FilterContext.methodName ctx) do! ctx.Invoke() printfn "Call completed" })
siloConfig { useLocalhostClustering addIncomingFilter loggingFilter}Outgoing filters
Section titled “Outgoing filters”Intercept calls made from a grain to another grain:
let tracingFilter = Filter.outgoing (fun ctx -> task { // Add tracing headers do! ctx.Invoke() })
siloConfig { useLocalhostClustering addOutgoingFilter tracingFilter}Before/after filters
Section titled “Before/after filters”let timingFilter = Filter.incomingWithAround (fun ctx -> task { printfn "Before %s" (FilterContext.methodName ctx) }) (fun ctx -> task { printfn "After %s" (FilterContext.methodName ctx) })Grain Services
Section titled “Grain Services”Grain services run on every silo. Use them for background processing:
siloConfig { useLocalhostClustering addGrainService typeof<MyBackgroundService>}Custom DI Services
Section titled “Custom DI Services”Register custom services with the host’s DI container:
siloConfig { useLocalhostClustering configureServices (fun services -> services.AddSingleton<IMyService, MyService>() |> ignore services.AddHttpClient() |> ignore)}Serialization
Section titled “Serialization”Select one generalized-serialization policy. Functional definition registration installs the binary default; configure an explicit policy when this silo should choose JSON or use it only for CLR types unsupported by the binary codec:
let serialization = FSharpSerialization.Binary |> FSharpSerialization.forUnsupportedTypes FSharpSerialization.Json
siloConfig { useLocalhostClustering useFSharpSerialization serialization}Use useFSharpJsonSerialization when JSON should be the primary generalized codec. See
Serialization for the exact selection order.
Serilog
Section titled “Serilog”Wire Serilog as the logging provider:
siloConfig { useLocalhostClustering useSerilog}Complete Production Example
Section titled “Complete Production Example”open Systemopen Orleans.FSharpopen Orleans.FSharp.Runtimeopen Orleans.FSharp.Versioning
let redisConn = Environment.GetEnvironmentVariable("REDIS_CONNECTION")let azureConn = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION")
let config = siloConfig { // Clustering addRedisClustering redisConn clusterId "production" serviceId "my-app"
// Storage addRedisStorage "Default" redisConn addAzureBlobStorage "Archive" azureConn
// Streaming addMemoryStreams "Events" addBroadcastChannel "Notifications"
// Reminders addRedisReminderService redisConn
// Security useMutualTls "CN=orleans-silo"
// Observability enableHealthChecks addDashboard useSerilog
// Services configureServices (fun services -> services.AddHttpClient() |> ignore)
// Versioning useGrainVersioning BackwardCompatible LatestVersion
// Endpoints siloPort 11111 gatewayPort 30000
// Lifecycle grainCollectionAge (TimeSpan.FromHours 2.) addStartupTask (fun sp ct -> task { printfn "Silo started" } :> Threading.Tasks.Task)}Next steps
Section titled “Next steps”- Client Configuration – configure Orleans clients
- Legacy archive – unsupported migration reference for earlier authoring models
- Streaming – publish and subscribe to events
- Security – TLS, mTLS, and call filters in depth