Introduction
Imagine you have built a real-time chat application using ASP.NET Core SignalR. Everything works well while the application runs on a single server. Users can send messages, receive notifications, and see updates without refreshing the page.
As the number of users grows, you decide to run the application on multiple servers behind a load balancer. You expect this change to handle more connections, but you soon discover a problem: a message sent by a user connected to one server does not always reach users connected to another server.
Why does this happen?
Each SignalR server manages its own client connections. Without a suitable scale-out solution, one server does not automatically know how to deliver messages to clients connected to another server.
This is an important consideration when moving a real-time application into production.
In this article, we will learn how SignalR works across multiple servers, understand the role of Redis and Azure SignalR Service, and configure a scalable ASP.NET Core SignalR application.
Why Does SignalR Need a Scale-Out Solution?
SignalR maintains persistent connections between clients and the server. Depending on the available transport, these connections may use WebSockets, Server-Sent Events, or long polling.
Consider an application running on two servers behind a load balancer.
Clients
|
Load Balancer
/ \
/ \
Server A Server B
User 1 User 2User 1 is connected to Server A, while User 2 is connected to Server B.
When User 1 sends a message, Server A can deliver it to the clients it manages. However, it cannot automatically deliver the message to User 2 unless the application has a way to distribute messages across servers.
This is where SignalR scale-out comes in.
A scale-out solution helps multiple servers coordinate message delivery. It does not necessarily move client connections between servers or share every piece of application state.
What Are the Main Options for Scaling SignalR?
ASP.NET Core SignalR supports different approaches to scale-out. The right option depends on your hosting environment, connection volume, infrastructure, and operational requirements.
Approach | How it works | Best suited for |
|---|---|---|
Single server | One server manages all SignalR connections | Small applications and development |
Redis backplane | Servers distribute SignalR messages through Redis | Self-managed or cloud-hosted server farms |
Azure SignalR Service | A managed service handles client connections and distributes messages | Applications hosted on Azure or environments that can connect to the service |
Running multiple application servers without a scale-out mechanism is not enough to guarantee that messages reach clients connected to other servers.
Option 1: Use Redis as a SignalR Backplane
A Redis backplane allows multiple SignalR servers to exchange messages through a shared Redis deployment.
For example, when a message is published through Server A, the backplane distributes it so that Server B can deliver it to its connected clients.
Clients
|
Load Balancer
/ \
/ \
Server A Server B
\ /
\ /
Redis
BackplaneRedis does not need to manage every client connection itself. The SignalR application servers continue to handle their own connections, while Redis helps distribute messages between them.
Step 1: Install the Redis package
In your ASP.NET Core SignalR project, install the Redis integration package.
dotnet add package Microsoft.AspNetCore.SignalR.StackExchangeRedisUse a package version compatible with your target .NET framework.
Step 2: Configure SignalR with Redis
Add the Redis connection string to your application configuration.
{
"ConnectionStrings": {
"Redis": "localhost:6379"
}
}This example uses a local Redis instance for development. In production, use an appropriately secured Redis deployment and store credentials in a secure configuration provider.
Now configure SignalR in Program.cs.
var builder = WebApplication.CreateBuilder(args);
var redisConnection =
builder.Configuration.GetConnectionString("Redis")
?? throw new InvalidOperationException(
"Redis connection string is missing.");
builder.Services
.AddSignalR()
.AddStackExchangeRedis(redisConnection);
var app = builder.Build();
app.MapHub<ChatHub>("/chatHub");
app.Run();The important part is AddStackExchangeRedis(). It configures Redis as the SignalR backplane so that messages can be distributed across application servers using the same Redis deployment.
Each server must use compatible SignalR configuration and connect to the appropriate shared backplane.
Step 3: Create a SignalR hub
Create a simple hub that allows clients to send messages.
using Microsoft.AspNetCore.SignalR;
public class ChatHub : Hub
{
public async Task SendMessage(
string user,
string message)
{
await Clients.All.SendAsync(
"ReceiveMessage",
user,
message);
}
}The Clients.All expression targets all connected clients known to the SignalR application. With a correctly configured Redis backplane, the message can reach clients connected to different application servers.
In a production application, derive user identity from authenticated claims instead of trusting a user name supplied by the client.
An important point about sticky sessions
Using Redis does not automatically remove the need for sticky sessions.
In a server farm, SignalR generally requires a client to remain connected to the server that handles its connection. This is known as session affinity or sticky sessions.
There are documented exceptions: sticky sessions are not required when the application uses Azure SignalR Service, runs on a single server, or uses WebSockets exclusively with negotiation skipped.
For the WebSockets exception, the client and server configuration must actually satisfy those conditions. Simply enabling WebSockets is not enough if negotiation is still used.
Redis primarily distributes messages between servers; it is not a replacement for connection affinity.
Option 2: Use Azure SignalR Service
If your application runs in Azure, Azure SignalR Service is another option. It is a managed service that handles client connections and distributes messages between the application and connected clients.
Instead of making every application server manage all client connections directly, the service takes on much of the connection-management work.
This can simplify scaling when you need to support a growing number of simultaneous connections.
Step 1: Create an Azure SignalR Service resource
Create an Azure SignalR Service resource and choose a suitable service configuration for your expected workload.
Store its connection string securely. Avoid placing production connection strings in source control.
Step 2: Install the Azure SignalR package
dotnet add package Microsoft.Azure.SignalRStep 3: Configure the application
Add the service connection string to your application's configuration under the Azure:SignalR:ConnectionString key, using a secure configuration provider in production.
Then configure SignalR in Program.cs.
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddSignalR()
.AddAzureSignalR();
var app = builder.Build();
app.MapHub<ChatHub>("/chatHub");
app.Run();The Azure SignalR integration reads the configured connection string and connects the application to the managed service.
Your hub can continue using methods such as Clients.All.SendAsync() without needing to implement custom cross-server message distribution.
When should you choose Azure SignalR Service?
Consider this option when you want managed connection handling, need to scale out a real-time application, or prefer to reduce the operational work involved in maintaining a Redis backplane.
However, Azure SignalR Service introduces a dependency on an additional managed service. Its pricing, capacity limits, network requirements, and service configuration should be considered before deployment.
Redis Backplane vs. Azure SignalR Service
Feature | Redis Backplane | Azure SignalR Service |
|---|---|---|
Connection management | Application servers manage client connections | Managed service handles client connections |
Message distribution | Redis distributes messages between servers | Service distributes messages to connected clients |
Infrastructure | Requires Redis infrastructure | Requires an Azure SignalR resource |
Operations | Your team manages Redis availability and configuration | Azure manages much of the connection infrastructure |
Hosting flexibility | Suitable for self-managed environments and cloud deployments | Particularly convenient for Azure-hosted applications |
Session affinity | Generally still required, subject to documented exceptions | Not required for client connections through the service |
Neither option eliminates the need to design your application carefully. Both require appropriate monitoring, security, error handling, and capacity planning.
Best Practices for Production Applications
1. Use a shared scale-out configuration
All application servers should use the intended Redis backplane or Azure SignalR Service configuration. Inconsistent configuration can cause unexpected message delivery behavior.
2. Configure session affinity where required
If your deployment requires sticky sessions, configure the load balancer accordingly. Test the actual connection and negotiation behavior rather than assuming the load balancer will preserve connections automatically.
3. Monitor connection counts and message delivery
Monitor active connections, failed connection attempts, message-processing errors, Redis availability, and resource utilization.
A high number of connected clients does not necessarily mean the application is healthy. Monitor the behavior that matters to your users.
4. Protect the hub
Use authentication and authorization where appropriate. Apply authorization rules to sensitive hubs and groups, and avoid allowing clients to send arbitrary messages to all users without validation.
5. Plan for reconnects
Network interruptions and server restarts can disconnect clients. Implement appropriate client reconnection behavior and make sure clients can restore any required application state after reconnecting.
Do not assume that every missed message will automatically be replayed. If the application requires durable message history, store it separately.
6. Load-test the complete deployment
Test the application with realistic connection counts, message sizes, and message rates. Measure server resource usage, backplane performance, and end-to-end message delivery.
Do not rely only on a test with a few clients connected to a single server.
Common Mistakes and Troubleshooting
Problem | Possible cause | What to check |
|---|---|---|
Messages reach only some clients | Backplane is missing or incorrectly configured | Verify that all servers use the same scale-out infrastructure |
Connections fail behind the load balancer | Session affinity or transport configuration is incorrect | Check negotiation, routing, and sticky-session settings |
Redis-related errors appear | Redis is unavailable or connections are misconfigured | Inspect connectivity, credentials, and Redis health |
Clients disconnect frequently | Network issues, timeouts, or infrastructure limits | Review client logs, server logs, and service metrics |
Some users receive unauthorized messages | Hub or group authorization is incorrect | Review identity handling and access rules |
Messages are missing after reconnecting | The application assumes automatic message replay | Implement durable message storage when message history is required |
One common mistake is treating Redis as a permanent message store. A SignalR backplane distributes messages; it is not a substitute for a durable event log or chat history database.
Another mistake is assuming that adding more application servers will automatically improve performance. The backplane, network, database, and managed service can all become bottlenecks.
Advantages and Disadvantages
Advantages
Allows SignalR applications to distribute messages across multiple servers.
Supports growth in application capacity.
Redis provides a flexible scale-out option for self-managed infrastructure.
Azure SignalR Service reduces the operational burden of managing client connections.
Lets the application retain its existing hub programming model.
Disadvantages
Redis adds infrastructure that must be secured and monitored.
Azure SignalR Service introduces service costs and a dependency on Azure.
Session affinity may still be required when using a Redis backplane.
Scaling does not automatically provide durable message history or replay.
Poorly designed hubs can still create performance and authorization problems.
Summary
Scaling an ASP.NET Core SignalR application requires more than running multiple copies of the application. Each server manages its own connections, so the application needs a way to distribute messages across the deployment.
Redis backplane is a useful choice when you want to manage your own server infrastructure. Azure SignalR Service is an alternative when you prefer a managed service to handle client connections and message distribution.
Choose the option that fits your hosting environment, operational capacity, and workload. Then configure connection affinity where required, secure your hubs, monitor message delivery, and test the complete deployment under realistic conditions.
The goal is to ensure that clients continue receiving the messages they need as your application grows beyond a single server.


Join the conversation! Your thoughts help the community grow.