Files
scadalink-design/AkkaDotNet/11-Hosting.md
Joseph Doherty de636b908b Add Akka.NET reference documentation
Notes and documentation covering actors, remoting, clustering, persistence,
streams, serialization, hosting, testing, and best practices for the Akka.NET
framework used throughout the ScadaLink system.
2026-03-16 09:08:17 -04:00

6.8 KiB

11 — Akka.Hosting

Overview

Akka.Hosting is the recommended integration layer between Akka.NET and the Microsoft.Extensions.* ecosystem (Hosting, DependencyInjection, Configuration, Logging). It replaces the traditional HOCON-only configuration approach with a code-first, type-safe API and provides an ActorRegistry for injecting actor references into non-actor services.

In our SCADA system, Akka.Hosting is the entry point for bootstrapping the entire ActorSystem — configuring Remoting, Cluster, Singleton, Persistence, and all other modules through a fluent C# API that integrates with the standard .NET Generic Host.

When to Use

  • Always — Akka.Hosting should be the primary way to configure and start Akka.NET in any new .NET 10 application
  • Configuring all Akka.NET modules (Remoting, Cluster, Persistence, etc.) in code rather than HOCON files
  • Registering top-level actors in the ActorRegistry for injection into ASP.NET controllers, Windows Services, or other DI consumers
  • Managing ActorSystem lifecycle tied to the .NET host lifecycle

When Not to Use

  • There are no scenarios where you should avoid Akka.Hosting in new projects on .NET 6+
  • Legacy projects on older .NET may need the traditional HOCON approach if they cannot adopt Microsoft.Extensions.Hosting

Design Decisions for the SCADA System

Windows Service Host

The SCADA application runs as a Windows Service using the .NET Generic Host:

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddAkka("scada-system", (akkaBuilder, sp) =>
{
    var siteConfig = sp.GetRequiredService<SiteConfiguration>();

    akkaBuilder
        .ConfigureLoggers(loggers =>
        {
            loggers.LogLevel = Akka.Event.LogLevel.InfoLevel;
            loggers.AddLoggerFactory();  // Bridge to Microsoft.Extensions.Logging
        })
        .WithRemoting(options =>
        {
            options.Hostname = "0.0.0.0";
            options.PublicHostname = siteConfig.NodeHostname;
            options.Port = 4053;
        })
        .WithClustering(new ClusterOptions
        {
            Roles = new[] { "scada-node" },
            SeedNodes = siteConfig.SeedNodes
        })
        .WithSingleton<DeviceManagerActor>("device-manager",
            (system, registry, resolver) => resolver.Props<DeviceManagerActor>(),
            new ClusterSingletonOptions { Role = "scada-node" })
        .WithSingletonProxy<DeviceManagerActor>("device-manager",
            new ClusterSingletonProxyOptions { Role = "scada-node" })
        .WithDistributedData()
        .WithActors((system, registry, resolver) =>
        {
            var healthActor = system.ActorOf(
                resolver.Props<ClusterHealthActor>(), "cluster-health");
            registry.Register<ClusterHealthActor>(healthActor);
        });
});

var host = builder.Build();
await host.RunAsync();

ActorRegistry for DI Integration

The ActorRegistry stores named IActorRef instances that can be injected into non-actor services:

// Registering actors
registry.Register<DeviceManagerActor>(deviceManagerRef);
registry.Register<ClusterHealthActor>(healthActorRef);

// Consuming in a Windows Service health check endpoint
public class HealthCheckService : IHostedService
{
    private readonly IRequiredActor<ClusterHealthActor> _healthActor;

    public HealthCheckService(IRequiredActor<ClusterHealthActor> healthActor)
    {
        _healthActor = healthActor;
    }

    public async Task<HealthStatus> CheckHealthAsync()
    {
        var actorRef = await _healthActor.GetAsync();
        var status = await actorRef.Ask<ClusterHealthStatus>(new GetHealth(), TimeSpan.FromSeconds(5));
        return status.IsHealthy ? HealthStatus.Healthy : HealthStatus.Unhealthy;
    }
}

Site-Specific Configuration

Use Microsoft.Extensions.Configuration (appsettings.json, environment variables) for site-specific values (hostnames, seed nodes, device lists). Inject these into the Akka configuration:

// appsettings.json
{
  "ScadaSite": {
    "NodeHostname": "nodeA.scada.local",
    "SeedNodes": [
      "akka.tcp://scada-system@nodeA.scada.local:4053",
      "akka.tcp://scada-system@nodeB.scada.local:4053"
    ],
    "DeviceConfigPath": "C:\\ProgramData\\SCADA\\devices.json"
  }
}
builder.Services.Configure<SiteConfiguration>(
    builder.Configuration.GetSection("ScadaSite"));

Common Patterns

Combining HOCON and Hosting

For module-specific settings not yet covered by Hosting APIs, you can mix HOCON with code-first configuration:

akkaBuilder.AddHocon(ConfigurationFactory.ParseString(@"
    akka.cluster.split-brain-resolver {
        active-strategy = keep-oldest
        keep-oldest.down-if-alone = on
        stable-after = 15s
    }
"), HoconAddMode.Prepend);

Logging Bridge

Bridge Akka.NET's internal logging to Microsoft.Extensions.Logging so all logs go through the same pipeline (Serilog, NLog, etc.):

akkaBuilder.ConfigureLoggers(loggers =>
{
    loggers.ClearLoggers();
    loggers.AddLoggerFactory();
});

Coordinated Shutdown Integration

Akka.Hosting automatically ties CoordinatedShutdown to the host's lifetime. When the Windows Service stops, the ActorSystem leaves the cluster gracefully and shuts down. No additional configuration is needed.

Anti-Patterns

Bypassing Hosting to Create ActorSystem Manually

Do not create ActorSystem.Create() alongside Akka.Hosting. This creates a second, unmanaged ActorSystem. Always use AddAkka() on the service collection.

Registering Actors That Don't Exist Yet

The ActorRegistry resolves asynchronously (GetAsync()), but registering an actor reference that is never created will cause consumers to hang indefinitely. Always register actors in the WithActors callback where they are created.

Hardcoding Configuration

Do not hardcode hostnames, ports, or seed nodes in the AddAkka configuration. Use Microsoft.Extensions.Configuration so that each site's deployment can override these values via appsettings.json or environment variables.

Configuration Guidance

NuGet Packages

Akka.Hosting
Akka.Remote.Hosting
Akka.Cluster.Hosting       (includes Sharding, Tools)
Akka.Persistence.Hosting

These Hosting packages replace the need to install the base packages separately — they include their dependencies.

Startup Order

Akka.Hosting initializes components in the order they are registered. Register in dependency order:

  1. Loggers
  2. Remoting
  3. Clustering (depends on Remoting)
  4. Persistence
  5. Distributed Data
  6. Singletons and Singleton Proxies (depend on Clustering)
  7. Application actors

References