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:
- Clients connect to Azure SignalR Service, not your server.
- Your server connects to Azure SignalR Service as a "hub server."
- When a client invokes a hub method, Azure routes it to your server.
- 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:
dotnet add package Microsoft.Azure.SignalR
The code change is minimal — replace AddSignalR() with AddSignalR().AddAzureSignalR():
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:
{
"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:
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.
// 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:
- Free tier: 20 concurrent connections, 20,000 messages/day. Good for development.
- Standard tier: Scales to 100,000+ concurrent connections per unit. Add units for more capacity.
- Premium tier: Adds availability zones, larger message sizes, and higher SLAs.
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:
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:
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:
- Connection count — how many clients are connected.
- Message count — throughput.
- Connection open/close errors — connectivity issues.
- Server connection count — connections from your app server to Azure.
Enable diagnostic logs for detailed connection and messaging traces:
builder.Services.AddSignalR()
.AddAzureSignalR(options =>
{
options.ConnectionString = connectionString;
options.DiagnosticClientFilter = connection =>
connection.GetHttpContext()?.Request.Query["debug"] == "true";
});
Key Takeaways
- Azure SignalR Service offloads WebSocket management — your server only handles business logic.
- Migration requires one NuGet package and one configuration change; hub code stays the same.
- Use managed identity in production instead of connection string keys.
- Choose default mode for interactive hubs, serverless mode for broadcast-only.
- Scale your application servers independently from connection capacity.
- Use multiple endpoints for high availability across regions.