Azure SignalR Service: Managed Real-Time at Scale

Self-hosted SignalR works well, but managing WebSocket connections is resource-intensive. Each connection consumes memory, a socket, and thread pool attention. Azure SignalR Service offloads all of that — your server handles business logic while Azure manages the connections.

How It Works

Without Azure SignalR Service, your application server manages all WebSocket connections directly. With it, the architecture changes:

  1. Clients connect to Azure SignalR Service, not your server.
  2. Your server connects to Azure SignalR Service as a "hub server."
  3. When a client invokes a hub method, Azure routes it to your server.
  4. When your server sends a message, Azure routes it to the right clients.

Your server never holds a WebSocket connection to a client. It maintains a small number of persistent connections to the Azure service, which fans out to potentially thousands of clients.

Setup

Install the NuGet package:

Example.cs
dotnet add package Microsoft.Azure.SignalR

The code change is minimal — replace AddSignalR() with AddSignalR().AddAzureSignalR():

Program.cs
builder.Services.AddSignalR()
    .AddAzureSignalR(options =>
    {
        options.ConnectionString = builder.Configuration
            .GetConnectionString("AzureSignalR")!;
    });

Your hubs, groups, user targeting — everything stays exactly the same. The abstraction is remarkably clean.

Connection String

The connection string comes from the Azure portal when you create a SignalR Service resource:

Endpoint=https://myapp.service.signalr.net;AccessKey=<key>;Version=1.0;

Store it in your configuration:

appsettings.json
{
  "ConnectionStrings": {
    "AzureSignalR": "Endpoint=https://myapp.service.signalr.net;AccessKey=...;Version=1.0;"
  }
}

In production, use Azure Key Vault or managed identity instead of embedding keys.

Using Managed Identity

For production deployments, avoid connection string keys entirely:

Example.cs
builder.Services.AddSignalR()
    .AddAzureSignalR(options =>
    {
        options.Endpoints = new ServiceEndpoint[]
        {
            new ServiceEndpoint(
                new Uri("https://myapp.service.signalr.net"),
                credential: new DefaultAzureCredential())
        };
    });

This uses Azure Managed Identity — no secrets to rotate or leak.

Service Modes

Azure SignalR Service supports two modes:

Default mode. Your server has hubs with methods that clients can invoke. Azure routes invocations to your server and responses back. This is the standard model.

Serverless mode. No hub server at all. You use the REST API or Azure Functions bindings to send messages. Clients can't invoke hub methods — they can only receive. Good for broadcast-only scenarios.

Example.cs
// Serverless: sending via REST API from an Azure Function
[Function("SendNotification")]
public async Task Run(
    [HttpTrigger(AuthorizationLevel.Function)] HttpRequest req,
    [SignalROutput(HubName = "notifications")] IAsyncCollector<SignalRMessage> messages)
{
    await messages.AddAsync(new SignalRMessage
    {
        Target = "ReceiveNotification",
        Arguments = new object[] { "System update complete." }
    });
}

Scaling Considerations

Azure SignalR Service tiers determine your limits:

Your application server scales independently. Since it doesn't hold WebSocket connections, you can scale it based on CPU and memory for your business logic, not connection count.

Multiple Endpoints for High Availability

Configure multiple Azure SignalR Service endpoints for redundancy:

Example.cs
builder.Services.AddSignalR()
    .AddAzureSignalR(options =>
    {
        options.Endpoints = new ServiceEndpoint[]
        {
            new ServiceEndpoint("connection-string-primary",
                EndpointType.Primary, "east"),
            new ServiceEndpoint("connection-string-secondary",
                EndpointType.Secondary, "west")
        };
    });

Azure routes traffic to the primary endpoint and fails over to the secondary if the primary is unavailable.

Local Development

You don't need an Azure subscription for development. Use the Azure SignalR emulator or simply don't call AddAzureSignalR() in development:

Example.cs
if (builder.Environment.IsDevelopment())
{
    builder.Services.AddSignalR();
}
else
{
    builder.Services.AddSignalR()
        .AddAzureSignalR();
}

This keeps local development fast and free while using the managed service in staging and production.

Monitoring

Azure SignalR Service integrates with Azure Monitor. Key metrics to watch:

Enable diagnostic logs for detailed connection and messaging traces:

Example.cs
builder.Services.AddSignalR()
    .AddAzureSignalR(options =>
    {
        options.ConnectionString = connectionString;
        options.DiagnosticClientFilter = connection =>
            connection.GetHttpContext()?.Request.Query["debug"] == "true";
    });

Key Takeaways