Back to Blog
Tutorial10 min read2026-06-30

How to Deploy a Blazor Server App

Deploy a Blazor Server application to production with the SignalR circuit in mind: sticky sessions, WebSocket transport, container configuration, health checks, and connecting a managed database via EF Core.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Blazor Server is unusual among web app architectures: the UI lives on the server, and the browser maintains a persistent SignalR connection (a "circuit") that streams DOM diffs back and forth. That model has real deployment implications. You can't treat it like a stateless API, because each user's UI state is held in server memory and tied to a live WebSocket. Get the transport and session affinity right and Blazor Server is a joy to run; get them wrong and you'll see circuits dropping and full-page reloads.

Understand the circuit

When a user loads a Blazor Server page, the server establishes a SignalR circuit. Every click, input, and render happens over that connection. Two things follow:

  1. 1WebSockets are strongly preferred. SignalR can fall back to long polling, but that's far less efficient and adds latency to every interaction. Your ingress must allow WebSocket upgrades.
  2. 2Affinity matters at scale. If you run multiple replicas, a reconnecting client should land on the same instance or it loses its circuit state. For a single replica this is moot; for scaled-out deployments, you either configure sticky sessions or use a backplane.

Prepare the app

Make your app read the port from the environment and listen on all interfaces. In Program.cs this is usually handled by the ASPNETCORE_URLS environment variable:

ASPNETCORE_URLS=http://0.0.0.0:8080

Add EF Core and read the connection string from configuration, which ASP.NET maps from environment variables automatically:

builder.Services.AddDbContext<AppDbContext>(opt =>
    opt.UseNpgsql(builder.Configuration.GetConnectionString("Default")
        ?? Environment.GetEnvironmentVariable("DATABASE_URL")));

Note that Npgsql expects a key-value connection string (Host=...;Username=...;Password=...;Database=...) rather than a URI. If your platform injects a postgresql:// URL, parse it into the Npgsql format at startup, or set the discrete connection-string keys as environment variables.

Add a health check endpoint:

builder.Services.AddHealthChecks().AddNpgSql(connStr);
app.MapHealthChecks("/health");

Dockerfile

Use the official .NET multi-stage pattern. Publish a trimmed, framework-dependent build for fast deploys:

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o /app --no-restore

FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app .
ENV ASPNETCORE_URLS=http://0.0.0.0:8080
EXPOSE 8080
ENTRYPOINT ["dotnet", "YourApp.dll"]

The aspnet runtime image is smaller than the SDK and contains exactly what a published app needs.

Deploying on PandaStack

With the Dockerfile in your repo:

  1. 1Connect the GitHub repo as a container app.
  2. 2PandaStack builds with rootless BuildKit in an ephemeral Job pod, pushes to Artifact Registry, and deploys with Helm. Kong ingress handles routing.
  3. 3Provision a managed PostgreSQL database; DATABASE_URL is injected. Add discrete Npgsql connection-string keys if you prefer the explicit format.
  4. 4Run EF Core migrations. The cleanest approach is to apply migrations at startup for small apps, or run them as a one-off:
using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    db.Database.Migrate();
}

WebSocket and circuit considerations

Kong ingress supports WebSocket upgrades, which is what the SignalR circuit needs. Configure the SignalR circuit timeouts to be tolerant of brief network blips so a user who switches networks can reconnect:

builder.Services.AddServerSideBlazor().AddHubOptions(o =>
{
    o.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
    o.KeepAliveInterval = TimeSpan.FromSeconds(15);
});
ConsiderationBlazor Server guidance
TransportWebSocket required for good UX
ReplicasSingle replica avoids affinity issues; scale-out needs sticky sessions
MemoryEach circuit holds UI state; size memory tier accordingly
Cold startAvoid scale-to-zero for latency-sensitive interactive apps

A caution about the free tier: scale-to-zero on spot nodes means a cold start after idle, and any active circuits are lost when the app scales down. For a low-traffic internal tool that's acceptable. For a customer-facing interactive app, run on a paid tier with a warm instance and ideally a single replica or sticky sessions, since Blazor Server's stateful nature doesn't play well with cold starts.

Verifying

Load the app over its automatic-SSL domain and open the browser dev tools network tab; you should see a WebSocket connection to the SignalR hub stay open. Interactions should feel instant with no full-page reloads. Use the server-side metrics view to watch memory, since circuit state accumulates with concurrent users.

Common pitfalls

  • Connection string format: Npgsql wants key-value, not a URI.
  • WebSockets blocked: if the circuit can't use WebSockets it falls back to polling and feels sluggish.
  • Scaling without affinity: reconnecting users lose their circuit and get a reload.
  • Memory growth: many concurrent circuits consume server memory; choose a memory-optimized tier for heavy interactive use.

References

  • Blazor Server hosting and deployment: https://learn.microsoft.com/aspnet/core/blazor/host-and-deploy/server
  • SignalR configuration: https://learn.microsoft.com/aspnet/core/signalr/configuration
  • EF Core migrations: https://learn.microsoft.com/ef/core/managing-schemas/migrations/
  • Npgsql connection string parameters: https://www.npgsql.org/doc/connection-string-parameters.html
  • .NET container images: https://learn.microsoft.com/dotnet/architecture/microservices/net-core-net-framework-containers/official-net-docker-images

Blazor Server's stateful circuit makes it different from a typical web deploy, but with WebSockets and the right replica strategy it runs cleanly in containers. Spin up a container app plus a managed PostgreSQL database on PandaStack's free tier to try it: https://dashboard.pandastack.io

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also