Performance concept

Virtual Users vs Requests per Second

Updated October 27, 2026•8 min read•Workload•Test design

Written by

Reviewed by the LoadTester editorial team. Review process: see the editorial policy.

Published
2026-10-27
Last reviewed
2026-10-27
Author
Kristian Razum
Virtual Users vs Requests per Second: LoadTester illustration

Virtual users (VUs) are simulated clients that each run a script in a loop; requests per second (RPS) is the rate at which requests reach the server. They are linked by Little's Law: RPS = VUs ÷ (response time + think time), so 100 VUs with 200 ms responses and no think time produce about 500 RPS, while the same 100 VUs with a 5 s pause produce about 19 RPS. Specify VUs when you are modeling concurrent sessions, and specify RPS when you need to prove a throughput target.

Definitions

  • Virtual user (VU): one independent execution context in the load generator (a thread, goroutine, coroutine or event-loop task) that runs your scenario: request, wait for the response, optionally pause, repeat. A VU holds state like cookies and auth tokens, and usually its own connections.
  • Requests per second (RPS): completed (or sent) HTTP requests per second, summed across all VUs. Some tools report iterations per second instead, where one iteration may contain several requests. TPS (transactions per second) usually means business transactions, such as a checkout made up of 6 requests.
  • Concurrency: requests actually in flight at the server at a given moment. It is not the same as VUs: a VU that is sleeping holds no in-flight request.

Why "1,000 users" is ambiguous

When someone says "the site must handle 1,000 users", they usually mean one of at least four different loads:

What they meantLoad on the server (example numbers)
1,000 people on the site at once (analytics "active users"), one page view every 30 sAbout 33 page views/s, each fanning out into its own API and asset requests
1,000 VUs looping with no think time against a 100 ms endpointAbout 10,000 RPS
1,000 requests per second1,000 RPS, whatever VU count is needed
1,000 concurrent in-flight requests at 100 ms eachAbout 10,000 RPS (Little's Law: 1,000 / 0.1)

The difference between the first and second rows is a factor of 300 in server load. Before you agree to a target, convert it into RPS per endpoint and an expected response time. The page on how many users your website can handle walks through getting those numbers from analytics.

Converting between VUs and RPS

RPS = VUs × requests_per_iteration / iteration_time
iteration_time = sum(response_times) + sum(think_times)

VUs = RPS × iteration_time / requests_per_iteration

Worked example

A scenario logs in once, then loops over: GET /products (120 ms), 2 s pause, GET /product/:id (80 ms), 3 s pause, POST /cart (150 ms), 1 s pause. Ignore the one-off login.

iteration_time = 0.12 + 2 + 0.08 + 3 + 0.15 + 1 = 6.35 s
requests_per_iteration = 3

300 VUs → 300 × 3 / 6.35 ≈ 142 RPS
Target 600 RPS → 600 × 6.35 / 3 = 1,270 VUs

The concurrent users calculator and the RPS calculator do this conversion in both directions. The underlying math is covered in Little's Law for load testing.

How VU mode hides problems when the system slows down

In VU mode, each VU waits for a response before sending the next request. Load therefore depends on the system's own speed. When the server slows down, it receives fewer requests. That is a negative feedback loop, and it flatters the system under test.

Example, 100 VUs with no think time:

Server stateMean response timeRPS the server receives
Healthy100 ms1,000
DB starts to queue250 ms400
Badly degraded500 ms200
Stalled for 5 s5,000 ms20

The load drops by 80% precisely when the system is struggling, which gives it room to recover. Real users don't behave that way. On the internet, new visitors keep arriving at the same rate whether your server is fast or slow, so a slow server builds a queue and gets slower. A VU-mode test can report "survived 100 users, p95 500 ms" for a system that would collapse under the real arrival rate that 100 VUs produced when it was healthy.

There is a second effect: requests that would have been sent during a stall are never sent, so they never get a latency sample. That under-reports tail latency, a problem called coordinated omission.

Delivered RPS as response time rises: VU mode falls, RPS mode stays flat server response time (ms) RPS delivered 100 200 300 400 500 1000 500 0 RPS mode: fixed 1,000 RPS (VUs grow instead) VU mode: 100 VUs, RPS = 100 / response time
Same starting load, two models. In VU mode the server is "rewarded" for slowing down with less traffic. In RPS (arrival-rate) mode the load stays at the target and the queue becomes visible.

VU mode vs RPS mode: comparison

VU mode (closed model)RPS mode (open / arrival-rate model)
You controlNumber of concurrent VUsRequest or iteration arrival rate
System decidesRPS (falls as latency rises)Active VUs (rise as latency rises)
Behavior under slowdownLoad drops, system recoversLoad holds, queue grows, errors appear
Tail latency accuracyUnder-reports during stallsAccurate, if the generator has enough VUs
ModelsFixed pools: worker queues, batch jobs, a fixed set of API clients, call-center agentsPublic web and API traffic from many independent clients
Good forSession-heavy flows, connection-count limits, soak tests with stable concurrencyThroughput targets, SLO validation, capacity and stress tests
Tool examplesk6 constant-vus, Gatling injectClosed, JMeter Thread Group, Locust usersk6 constant-arrival-rate, Gatling injectOpen, JMeter Arrivals Thread Group (plugin), wrk2 -R, Vegeta -rate

The theory behind this table is covered in more depth in open vs closed workload models.

When to use VU mode and when to use RPS mode

Use RPS mode when

  • The requirement is a rate: "the checkout API must handle 300 RPS at p95 under 400 ms."
  • Traffic comes from the public internet, mobile apps, webhooks or other services you don't control.
  • You are looking for the breaking point. A stepped RPS ramp shows the exact rate where latency and errors take off; see throughput vs latency.
  • You compare releases. A fixed RPS gives every run the same offered load, so latency differences are real regressions rather than side effects of the system's own speed.

Use VU mode when

  • The client population really is fixed: 50 internal workers polling a queue, a partner with a 20-connection pool, a desktop app fleet with a known size.
  • You are testing per-connection or per-session limits, such as WebSocket connections, max keep-alive connections, or session stores.
  • You want a gentle exploratory test that won't build an unbounded queue against a fragile staging environment.
Practical default

For HTTP APIs and websites, start in RPS mode with a target derived from production peak traffic plus headroom. Switch to VU mode only when you can name the fixed population you're modeling.

Sizing VUs in RPS mode

Arrival-rate tools still need VUs to send requests; they just add them on demand. If they run out, the tool can't keep the rate and starts dropping or delaying iterations. In k6 this shows up as a dropped_iterations metric and a warning like Insufficient VUs, reached 100 active VUs and cannot initialize more. Size the ceiling with the worst latency you expect, not the current one:

// k6: 300 RPS, allow for responses up to 2 s
export const options = {
  scenarios: {
    api: {
      executor: 'constant-arrival-rate',
      rate: 300,
      timeUnit: '1s',
      duration: '5m',
      preAllocatedVUs: 100,   // 300 × 0.3 s typical
      maxVUs: 600,            // 300 × 2 s worst case
    },
  },
};

In LoadTester you pick VU mode or exact RPS mode when creating the test; in RPS mode the live chart plots active VUs next to RPS, so a rising VU line tells you latency is growing even before p95 crosses your threshold.

How to report results unambiguously

Whichever mode you use, report all of these together so nobody has to guess what "1,000 users" meant:

  1. Model: VU (closed) or arrival rate (open).
  2. Configured load: VUs and think time, or target RPS.
  3. Achieved RPS during the steady-state window, per endpoint if possible.
  4. p50, p95 and p99 latency (see p95 vs p99 latency) and error rate.
  5. For RPS mode: peak active VUs and any dropped iterations. For VU mode: whether RPS stayed flat or fell during the run.

FAQ

How many RPS do 100 virtual users generate?

It depends on the loop time: RPS = VUs ÷ (response time + think time). With 200 ms responses and no think time, 100 VUs generate about 500 RPS. With a 5 s think time they generate about 19 RPS.

Is a virtual user the same as a real user?

No. A real user browses with long pauses and loads many assets per page, while a VU runs whatever script you give it, often with no pauses at all. One VU with zero think time can generate the server load of dozens or hundreds of real visitors.

Why did my RPS drop during the test even though VUs stayed constant?

In VU mode each VU waits for a response before sending the next request, so when response time rises, RPS falls. A falling RPS line with flat VUs is a sign the system is slowing down. Switch to arrival-rate mode to see how it behaves when load does not back off.

Should my load test target VUs or RPS?

Target RPS for public APIs and websites, because real traffic arrives at a rate independent of how fast you respond. Target VUs when you are modeling a fixed population, such as a set of workers or a partner with a fixed connection pool.

What is the difference between RPS and TPS?

RPS counts individual HTTP requests. TPS usually counts business transactions, such as a login or a checkout, each of which may contain several requests. Always state which one a report uses.

Run this as a real load testFree plan: 10 VUs, 50 RPS, live p95/p99 and error charts, no credit card. Starter $19/mo (250 VUs) or Pro $49/mo (2,000 VUs) with a 7-day free trial when you need more.
Start freeCompare plans