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.
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 |> ignoreRequires 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 |> ignorelet configureFn = StreamProviders.addAzureQueueStreams "QueueProvider" azureConnStr
configureFn siloBuilder |> ignoreBroadcast 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
}Then map the health check endpoints in your ASP.NET Core pipeline:
app.MapHealthChecks("/health") |> ignoreRun 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
}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)
}Opt a silo into the F# codecs. Functional definition registration already installs
FSharpBinaryCodec; declare these explicitly when other host code needs the codec before
functional registration, or when you want the JSON fallback as well:
siloConfig {
useLocalhostClustering
useFSharpBinarySerialization // F# records, unions, lists, maps, options
useJsonFallbackSerialization // System.Text.Json for the rest
}See Serialization for what each mode covers and how the two compose.
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)
}- Client Configuration -- configure Orleans clients
- Legacy API -- maintenance documentation for earlier authoring models
- Streaming -- publish and subscribe to events
- Security -- TLS, mTLS, and call filters in depth