How Much RAM Does Your Blazor Server App Actually Need?

"How much RAM does my Blazor Server app need?" is one of the most-asked questions when developers first move to production. Unlike traditional web apps where a single worker can handle thousands of stateless requests per second on modest memory, Blazor Server holds a stateful circuit in memory for every connected user. That changes the sizing math fundamentally — concurrent users become the dominant capacity metric, not requests per second. This is the practical sizing guide grounded in real production numbers.

The short answer

For typical Blazor Server applications — CRUD UIs, admin dashboards, internal tools — budget ~250 KB per concurrent circuit plus baseline runtime overhead. Translated to plan sizes:

Plan tierApp pool RAMComfortable concurrent circuits

Developer ($9.49)~1 GB dedicated~75-100

Business ($17.49)~2 GB dedicated~150-200

Professional ($27.49)~4 GB dedicated~300-400+

"Concurrent" means circuits that are actively connected at the same moment. Most apps see much lower concurrency than total daily users — a Blazor Server app with 5,000 monthly users typically has 50-200 concurrent at peak.

Why Blazor Server is memory-bound

Traditional ASP.NET Core MVC or Razor Pages apps serve requests statelessly — a request arrives, runs through the pipeline, returns a response, and frees the request's memory. The worker process holds caches and the runtime baseline; per-request memory is transient.

Blazor Server is different. Each connected user opens a SignalR connection that the server holds open for the entire session. Per circuit, the server retains:

The current component tree — every Razor component currently rendered, with its parameters, state, and event handlers

Scoped dependency-injected services — AddScoped-registered services live for the circuit's lifetime, not just one HTTP request

Render-tree diff state — the framework's internal cache of what was last sent to the client, used to compute the next UI delta

SignalR connection state — the hub context, transport buffers, ping timers

Event handler closures — every @onclick handler captured in a component's lifetime

For a moderate-complexity component tree (a typical dashboard page with a sidebar, a data table, a few form components), the total is in the 200-300 KB range. For deeply nested component trees (a kanban board with many cards, each with sub-components), it can climb to 500-700 KB per circuit.

Real numbers from production Blazor Server apps

Approximate per-circuit memory observed across a sample of production Blazor Server applications on Windows Server 2022 + IIS 10 with .NET 10 LTS:

App shapeAvg circuit memoryNotes

Simple CRUD admin (login + 3-4 list/edit pages)180-220 KBMost internal tools land here

Mid-complexity dashboard (charts, data tables, filters)240-320 KBTypical SaaS admin panel

Rich interactive app (kanban, drag-drop, real-time chat)400-600 KBComplex stateful UIs

Heavy client-state app (in-memory data grids, large forms)700 KB - 1.2 MBEdge cases — consider WASM

Budget the 250 KB rule of thumb for capacity planning, then measure your specific app's per-circuit memory in staging (we'll get to how below) before committing to a plan tier.

Total memory budget on a hosting plan

On Adaptive Web Hosting plans, the application pool gets dedicated RAM that isn't shared with neighbour tenants. The total budget breaks down approximately:

App pool RAM = baseline runtime + framework caches + circuit memory + spare headroom

Baseline: ~150-200 MB (CLR + ASP.NET Core + Kestrel)

Framework caches: ~50-100 MB (Razor compiled components, JSON serializer caches)

Circuit memory: (N circuits × ~250 KB) MB

Spare headroom: ~200 MB (recommended to avoid GC pressure)

On a 2 GB Business plan: 2,048 MB total − 200 MB baseline − 100 MB framework caches − 200 MB headroom = ~1,548 MB available for circuits = ~6,000 circuits at 250 KB each in theory.

In practice, the limit comes from GC pressure long before raw memory exhaustion. As circuit count climbs into the thousands, garbage collection pauses get noticeable — the framework spends more time managing memory than serving UI updates. The 150-200 figure we recommend for Business is conservative; it's where typical apps stay smooth, not where the runtime hard-fails.

How to measure your app's actual per-circuit memory

Theoretical numbers are useful for initial planning; production numbers should drive your actual plan decision. Two measurement approaches:

Method 1: dotnet-counters during load test

Run your Blazor Server app locally or in staging. From a separate terminal, watch live process metrics:

dotnet-counters monitor -n YourApp

Look at GC Heap Size (working memory). Note the baseline with zero circuits. Open the app in 10 browser tabs (each is one circuit). Wait 30 seconds for circuits to stabilize. Note the new heap size. The difference divided by 10 is your per-circuit memory.

Repeat with 50 tabs, then 100. The per-circuit average should be roughly stable. If it climbs significantly with concurrency, you have a memory issue (probably scoped services holding too much state).

Method 2: Plesk IIS log + production observation

In production on a Plesk for Windows host, the IIS Worker Process counters surface in Plesk's resource monitor. Track Private Bytes for your w3wp.exe process during a typical-traffic period. Cross-reference with your application's circuit count (logged via app.Use(...) middleware counting SignalR connections) and calculate average memory per circuit.

On Adaptive Web Hosting's Blazor plans, the Plesk for Windows control panel includes resource monitoring per site that shows real-time memory usage of your dedicated app pool.

What inflates per-circuit memory beyond the baseline

Several patterns blow up circuit memory beyond the typical 250 KB:

  • Scoped services holding large state

// BAD — scoped service holds 10 MB of customer data per user

services.AddScoped<CustomerCache>();

public class CustomerCache

{

public List<Customer> AllCustomers { get; set; } // loaded at circuit start

}

If every circuit's CustomerCache holds 10 MB, 100 circuits = 1 GB. Use AddSingleton for shared data, or load on-demand inside scoped services without retaining the full result set.

  • Deeply nested component trees with many parameters

A page with 100 small components each holding a 5 KB [Parameter] object costs 500 KB just in component state. Consider flattening the tree or sharing data via cascading values rather than per-component parameters.

  • Long-lived event handler closures

If your @onclick handlers capture large objects from the enclosing scope, those objects live for the circuit's lifetime. Minimize what handlers capture — reference only the IDs you need, then load the full data on click.

  • Cached HTTP responses in scoped services

// BAD — every user's HttpClient retains response bodies

services.AddScoped<ApiClient>();

public class ApiClient

{

public Dictionary<string, string> CachedResponses { get; } = new();

}

Use shared memory caching (IMemoryCache or IDistributedCache) for cross-user caching. Don't cache in scoped services unless the data is genuinely per-user and small.

What if my circuit count grows beyond the plan limit?

Option A: Upgrade plan tier

Adaptive plan upgrades happen in-place — the new resource ceilings apply to your existing app pool. No re-deploy needed. Developer → Business roughly doubles circuit capacity; Business → Professional roughly doubles again.

Option B: Reduce per-circuit memory

Audit scoped services and component trees per the section above. Trimming per-circuit memory from 500 KB to 300 KB doubles your effective capacity at the same plan tier.

Option C: Multi-server scale-out via SignalR backplane

For truly large Blazor Server deployments (1,000+ concurrent), single-server vertical scaling hits limits. The standard pattern is multiple servers behind a load balancer with sticky sessions OR a SignalR backplane (Redis or Azure SignalR Service) that synchronizes connection state across servers. This is beyond shared-hosting territory — you'd typically move to Azure App Service or AWS Elastic Beanstalk for this scale. See our Azure vs Adaptive comparison for that trade-off.

Option D: Switch to Blazor WebAssembly

Blazor WebAssembly puts the .NET runtime in the user's browser — there are no server-side circuits. Server memory is no longer a concurrency bottleneck. The trade-off: slower initial page load (the runtime downloads to the browser), different threading model, requires WebAssembly-compatible browsers. See Blazor Server vs WebAssembly: Hosting Decision Guide for the full comparison.

Specific recommendations by app shape

App profileRecommended planWhy

Internal tool, 10-25 employees, occasional useDeveloper ($9.49)Concurrent peak well under 50 circuits; plenty of headroom

Small SaaS admin panel, 100-500 monthly usersDeveloper or Business~25-75 concurrent at peak; Developer covers most cases

Mid-size SaaS, 1k-5k monthly usersBusiness ($17.49)~100-300 concurrent at peak; Business comfortably handles

Larger SaaS, 5k-15k monthly usersProfessional ($27.49)~300-600 concurrent at peak; need the dedicated SQL allocation too

15k+ monthly users with concurrency > 600Professional + plan-scale auditApproaching single-server limits; audit per-circuit memory, consider Blazor WebAssembly for some routes

Highly bursty traffic (events, virality)Azure App ServiceNeed autoscale; fixed plan tiers don't fit elastic concurrency

These are conservative defaults. If you've measured your specific app's per-circuit memory at <200 KB, you can comfortably push the concurrency higher on any given tier.

Monitoring circuit count in production

Track concurrent SignalR connections with custom middleware:

// In Program.cs

builder.Services.AddSingleton<CircuitTracker>();

public class CircuitTracker

{

private int _activeCircuits;

public int ActiveCircuits => _activeCircuits;

public void Connected() => Interlocked.Increment(ref _activeCircuits);

public void Disconnected() => Interlocked.Decrement(ref _activeCircuits);

}

// Hook into Blazor's circuit lifecycle

public class TrackingCircuitHandler(CircuitTracker tracker) : CircuitHandler

{

public override Task OnCircuitOpenedAsync(Circuit circuit, CancellationToken ct)

{

tracker.Connected();

return Task.CompletedTask;

}

public override Task OnCircuitClosedAsync(Circuit circuit, CancellationToken ct)

{

tracker.Disconnected();

return Task.CompletedTask;

}

}

builder.Services.AddScoped<CircuitHandler, TrackingCircuitHandler>();

Expose the count via a health-check or admin endpoint. On Adaptive Web Hosting plans, the Plesk control panel's resource monitor lets you correlate this number against actual app pool memory in real time.

Frequently asked questions

How do I know if I've hit the plan's memory ceiling?

Symptoms: SignalR disconnects becoming frequent (the worker is recycling under memory pressure), app pool recycle events in Event Viewer (filter on source = WAS), slow garbage collection pauses (visible as UI freezes for active users). See our Blazor Server SignalR drops diagnostic guide for the full diagnostic flow.

Does scaling to Professional actually double concurrent capacity?

Roughly, yes — the dedicated app pool memory budget approximately doubles, and per-circuit memory is the dominant factor. The real-world increase depends on whether your app's bottleneck is memory or something else (CPU per UI update, SQL query throughput). For most apps, memory is the binding constraint, so the upgrade math is close to linear.

What's the practical limit on a single Adaptive plan?

Professional comfortably handles 300-400 concurrent circuits for typical apps; with careful per-circuit memory optimization, 600+ is feasible. Beyond that, single-server limits start showing — GC pressure, SignalR connection limits, IIS request queueing. Apps with sustained 1,000+ concurrent need horizontal scaling, which is beyond shared-hosting territory.

Does the WebSocket transport vs Long Polling matter for memory?

Marginally. WebSockets are slightly more memory-efficient per circuit than Long Polling because there's no per-poll buffer. For 100 concurrent circuits the difference is on the order of a few MB total — not a major capacity driver. WebSockets win for latency and bandwidth, not significantly for memory.

Should I disable circuit retention to save memory?

Blazor Server keeps disconnected circuits in memory for 3 minutes by default, so users with brief network blips can reconnect without losing state. Reducing DisconnectedCircuitRetentionPeriod shortens this window — saves memory but degrades the reconnect experience for legitimate users. Tune based on your user base: for mobile-heavy apps with flaky networks, keep the default; for stable corporate intranets, you can shorten safely.

What about the .NET 10 LTS improvements — do they change the math?

.NET 10 LTS includes refinements to Blazor Server's component re-rendering algorithm and SignalR transport efficiency. Per-circuit memory has trended down across .NET versions — an app that used 320 KB per circuit on .NET 6 typically uses 250-280 KB on .NET 10. Measure your specific numbers after upgrading.

Can I run multiple Blazor Server apps on one plan?

Yes — on Adaptive Web Hosting plans each domain gets its own dedicated IIS Application Pool, with its own memory budget. Two Blazor Server apps on the same plan don't share circuit memory; they share the underlying server's RAM but the pools are isolated. Plan capacity divides across the apps.

Does Native AOT help with Blazor Server memory?

Blazor Server has historically not been a strong AOT target because of reflection-heavy patterns in the Razor pipeline. .NET 10 LTS made progress but it's still an emerging scenario. For now, traditional JIT-compiled Blazor Server is the production-mature choice. See our Native AOT production guide for the broader AOT picture.

Bottom line

Blazor Server memory sizing comes down to one number: per-circuit memory, multiplied by your concurrent user peak. The 250 KB rule of thumb works for typical apps; measure your specific number before committing to a plan tier.

On Adaptive Web Hosting's Blazor plans, the three tiers map cleanly to concurrent-circuit budgets: Developer ($9.49) for ~75-100 circuits, Business ($17.49) for ~150-200, Professional ($27.49) for ~300-400+. The dedicated IIS Application Pools mean your memory budget is predictable; the real SQL Server 2022 inclusion means you're not also juggling database-tier limits. Every plan includes a 30-day money-back guarantee. View Blazor hosting plans, see our complete Blazor hosting guide, or talk to an ASP.NET expert.

Back to Blog