Skip to content
Orleans.FSharp

Complete guide to the siloConfig { } computation expression.

Current API. siloConfig { } configures hosting; functional definitions bind the named providers configured here.

  • 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

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 builder
let host = builder.Build()
host.Run()

Or apply directly to an ISiloBuilder with SiloConfig.applyToSiloBuilder.

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.


siloConfig { useLocalhostClustering }

Requires Microsoft.Orleans.Clustering.Redis.

let connStr = Environment.GetEnvironmentVariable("REDIS_CONNECTION")
siloConfig {
addRedisClustering connStr
addMemoryStorage "Default"
}

Requires Microsoft.Orleans.Clustering.AzureStorage.

let connStr = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION")
siloConfig {
addAzureTableClustering connStr
addMemoryStorage "Default"
}

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).

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 ISiloBuilder
Kubernetes.useKubernetesClustering siloBuilder |> ignore

Requires Microsoft.Orleans.Hosting.Kubernetes.


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 }

Requires Microsoft.Orleans.Persistence.AzureStorage.

let connStr = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION")
siloConfig { addAzureBlobStorage "Default" connStr }

Requires Microsoft.Orleans.Persistence.AzureStorage.

siloConfig { addAzureTableStorage "Default" connStr }

Requires Microsoft.Orleans.Persistence.AdoNet.

siloConfig { addAdoNetStorage "Default" connStr "Npgsql" }

Requires Microsoft.Orleans.Persistence.Cosmos.

let endpoint = Environment.GetEnvironmentVariable("COSMOS_ENDPOINT")
siloConfig { addCosmosStorage "Default" endpoint "MyDatabase" }

Requires Microsoft.Orleans.Persistence.DynamoDB.

siloConfig { addDynamoDbStorage "Default" "us-east-1" }

For any storage provider not covered above:

siloConfig {
addCustomStorage "MyStore" (fun siloBuilder ->
siloBuilder.AddMyCustomStorage("MyStore")
)
}

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
}

siloConfig {
useLocalhostClustering
addMemoryStorage "Default"
addMemoryStreams "StreamProvider"
}

A PubSubStore memory storage is automatically added if not already configured.

For durable streams backed by queues (Event Hubs, Redis, etc.):

siloConfig {
addPersistentStreams "MyStreams" adapterFactory configurator
}

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 directly
configureFn siloBuilder |> ignore
let configureFn = StreamProviders.addAzureQueueStreams "QueueProvider" azureConnStr
configureFn siloBuilder |> ignore

Broadcast channels deliver messages to ALL subscriber grains (fan-out):

siloConfig {
addBroadcastChannel "Notifications"
}

siloConfig { addMemoryReminderService }

Requires Microsoft.Orleans.Reminders.Redis.

siloConfig { addRedisReminderService connStr }
siloConfig {
addCustomReminderService (fun siloBuilder ->
siloBuilder.UseMyReminderService()
)
}

Requires Microsoft.Orleans.Connections.Security.

siloConfig {
useLocalhostClustering
useTls "CN=my-silo-cert"
}
let cert = new X509Certificate2("path/to/cert.pfx", "password")
siloConfig {
useLocalhostClustering
useTlsWithCertificate cert
}
siloConfig {
useLocalhostClustering
useMutualTls "CN=my-silo-cert"
}
siloConfig {
useLocalhostClustering
useMutualTlsWithCertificate cert
}

Warning: Always use valid certificates from a trusted CA in production. Never disable certificate validation in production environments.


Requires Microsoft.Orleans.Dashboard.

siloConfig {
useLocalhostClustering
addDashboard
}
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.


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 System
open Microsoft.AspNetCore.Diagnostics.HealthChecks
open Microsoft.AspNetCore.Builder
open Microsoft.Extensions.Diagnostics.HealthChecks
open 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)
|> ignore

Liveness 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.


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.


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

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 { }.


siloConfig {
useLocalhostClustering
clusterId "my-cluster"
serviceId "my-service"
siloName "silo-1"
}
siloConfig {
useLocalhostClustering
siloPort 11111
gatewayPort 30000
advertisedIpAddress "10.0.0.1"
}

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
}

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
}
let timingFilter =
Filter.incomingWithAround
(fun ctx -> task { printfn "Before %s" (FilterContext.methodName ctx) })
(fun ctx -> task { printfn "After %s" (FilterContext.methodName ctx) })

Grain services run on every silo. Use them for background processing:

siloConfig {
useLocalhostClustering
addGrainService typeof<MyBackgroundService>
}

Register custom services with the host’s DI container:

siloConfig {
useLocalhostClustering
configureServices (fun services ->
services.AddSingleton<IMyService, MyService>() |> ignore
services.AddHttpClient() |> ignore)
}

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.


Wire Serilog as the logging provider:

siloConfig {
useLocalhostClustering
useSerilog
}

open System
open Orleans.FSharp
open Orleans.FSharp.Runtime
open 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)
}