.NET 10 · zero third-party dependencies

Load testing for .NET,
without the baggage.

Pacer is an open-source performance & load testing framework built strictly on first-party Microsoft packages. Define scenarios as a pipeline of steps, drive them under realistic load profiles, and get latency percentiles in console, CSV & HTML.

Install from NuGet โญ Star on GitHub
$ dotnet add package Pacer
Why Pacer

Everything you need, nothing you don't

A focused engine with a clean fluent API โ€” no plugins, no telemetry, no surprise transitive packages.

๐Ÿงฉ

First-party only

Built on Microsoft.Extensions.* and System.CommandLine. No third-party runtime dependencies โ€” ever.

๐Ÿชœ

Five load profiles

Load, Soak, Spike, Stress and Ramp out of the box, plus custom stage composition.

โ›“๏ธ

Fluent scenarios

Inline setup and a step pipeline per virtual user โ€” data flows step โ†’ step like a real user journey.

๐Ÿ“Š

Real percentiles

Bounded-memory HDR-style histograms give accurate p50โ€“p99.9, plus live dotnet-counters metrics.

๐Ÿ“„

Console, CSV & HTML

Polished reports out of the box, and a pluggable IReportWriter for your own formats.

๐ŸŽฏ

Deterministic core

Built on TimeProvider, so the engine is fully unit-tested with a fake clock โ€” no flaky sleeps.

Model real traffic

Built-in load profiles

Closed-model concurrency โ€” you control the number of virtual users, and the profile shapes it over time.

Load
constant users
Soak
endurance hold
Spike
sudden surge
Stress
staircase up
Ramp
bell curve
Quickstart

From zero to a load test in one file

Register a scenario, hand your args to Pacer, and run it.

Program.cs
return await PacerApplication.RunAsync(args, builder =>
{
    builder.Services.AddPacer()
        .AddScenario(Scenario.Create("checkout")
            .WithSetup(async ctx => new CheckoutData("token"))
            .AddStep("login",   async ctx => StepResult.Ok())
            .AddStep("browse",  async ctx => StepResult.Ok())
            .AddStep("purchase",async ctx => StepResult.Ok())
            .WithLoad(LoadProfiles.Ramp(
                peak: 200,
                rampUp:   TimeSpan.FromSeconds(30),
                hold:     TimeSpan.FromMinutes(1),
                rampDown: TimeSpan.FromSeconds(30))));
});

// dotnet run -- run --scenario checkout
console
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 Scenario: checkout  [group: storefront]
 Profile:  Ramp  ยท  200 peak users  ยท  2m 00s
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 step          ok   fail    rps   p95 ms   p99 ms
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 login     12,418     0  207.0    13.95    14.98
 browse    12,390     0  206.5    33.28    34.30
 purchase  12,144   246  206.4    43.52    44.54
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 checkout  12,144   246  207.4    80.90    87.04
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•