Skip to main content

What are Virtual Users (VUs) in Load Testing? Definition + Examples

What are Virtual Users (VUs) in Load Testing? Definition + Examples

Key takeaways

  • A virtual user simulates one person’s session, including its pauses and sequence.
  • Virtual users are not requests per second; the relationship depends on response time and think time.
  • Model think time honestly, since removing it invents traffic nobody generates.

A virtual user (VU) in load testing is one simulated person running a scripted journey against your system: it sends a request, waits for the response, pauses the way a reader would (“think time”), then moves on to the next step. A load test runs many VUs at once, so the VU count is how you say how much concurrent activity to simulate. In JMeter a VU is a Java thread, in k6 it is a separate JavaScript runtime, and in Locust it is a greenlet.

This post covers what virtual users are, how they differ from real users and from requests per second (RPS), how JMeter and k6 model them, how many to simulate, and the misconfigurations that make VU-based results lie.

The quick answer

VUs are the test-harness primitive that simulates concurrent activity. They are how you express load.

Virtual Users (VUs)Real UsersRPS (Requests Per Second)
What it isA test thread or runtime simulating one concurrent personAn actual person in front of a browser or appA rate of work the system completes
Controlled byYou (the test design)Whoever your audience isFollows from VU count, response time and think time
Used forExpressing test loadProduction trafficMeasuring server-side capacity
Has think time?Optional, configurableYes, between actionsN/A. It is a rate

What is a virtual user in performance testing?

A virtual user is a unit of concurrent simulated activity in a load test. Mechanically it is one thread (JMeter), one JavaScript VM (k6) or one greenlet (Locust) that:

  1. Logs in (or starts with a pre-authenticated session).
  2. Walks through a defined user flow: checkout, search, browse, whatever the script defines.
  3. Sends HTTP requests, waits for responses and records how long each took.
  4. Optionally pauses for think time between actions. Take the pause from your analytics (time on page, time between steps) rather than from a rule of thumb.
  5. Loops through the flow again, or exits.

100 VUs running concurrently means 100 of these running in parallel. To the system under test, they look like 100 people using the service at the same moment.

The point of a load test is to express expected production load as some number of VUs, then watch how the system responds.

VUs vs real users

VUs are useful precisely because they are predictable in ways real users are not:

  • Repeatable. The same script with the same VU count produces comparable results across runs. Real-user traffic varies day to day.
  • Tunable. You can dial VUs from 10 to 10,000 in one script. Real-user traffic is whatever happens.
  • Observable. You define the script, so you know exactly what each VU does. Real-user behaviour is opaque.

But VUs are also unrealistic in ways that bias results:

  • Think time is approximate. A real user clicks “checkout”, reads the page, hesitates, clicks “confirm”. A VU might sleep 3 seconds. The shape of think time matters for cache behaviour and connection-pool dynamics. Too short and you are stress-testing; too long and you are not loading the system.
  • VU flows are uniform. Real users do unpredictable things: back buttons, abandoned carts, double-clicks. VUs follow the script you wrote, not the long tail of real behaviour.
  • VU data is bounded. Real users have unique IDs, addresses and payment methods. VUs share a CSV file unless you generate unique IDs per iteration. Test data variety matters for cache hit rates.

The right framing: VUs are a model. Useful for capacity planning and SLO validation, less useful for catching the rare bug that only appears in real-traffic edge cases. Pair VU-based load testing with real-user monitoring (RUM) for full coverage.

Virtual users vs requests per second

VUs and RPS measure different things and get confused constantly:

  • VUs = how many simulated users are active at the same time. A measure of load.
  • RPS = how many requests the system completes per second. A measure of work.

The conversion follows from Little’s Law. If each step of the journey is one request followed by a pause, one VU produces 1 / (response time + think time) requests per second:

  • 3 seconds of think time and a 200 ms response: 1 / 3.2 s = about 0.31 RPS per VU.
  • 1 second of think time and a 200 ms response: 1 / 1.2 s = about 0.83 RPS per VU.
  • No think time and a 100 ms response: 1 / 0.1 s = 10 RPS per VU, and more as responses get faster.

Turned around, VUs = target RPS x (response time + think time). To sustain 1,000 RPS with 3 seconds of think time and 200 ms responses you need about 3,200 VUs. Without think time, the same 1,000 RPS needs only about 100 VUs, which is why a test with no pauses says almost nothing about how many people the system can serve.

When someone says “we need to handle 5,000 users”, ask which number they mean: concurrent users, sessions per hour or requests per second. They lead to different test designs, and our guide on how to calculate concurrent users for a load test walks through getting from analytics to a VU count.

How JMeter models virtual users

In JMeter, each virtual user is one Java thread. A Thread Group defines:

  • Number of threads (users). The VU count.
  • Ramp-up period. How long JMeter takes to start all threads. 1,000 threads with a 600-second ramp-up start one thread every 0.6 seconds, about 1.67 per second.
  • Loop count or duration. Whether each VU runs the script N times or for D seconds.

Each thread runs the samplers in its Thread Group in order: HTTP request, a timer for think time, the next HTTP request, and so on.

How many threads one machine can run depends on the script, the heap size, the listeners left enabled and the hardware, so measure your own ceiling rather than trusting a number; our post on the maximum number of concurrent users JMeter can handle covers what runs out first. Beyond one machine, JMeter’s distributed mode lets one client control several JMeter server instances, and each server runs the full thread count: 1,000 threads on 6 servers is 6,000 VUs.

For load shapes beyond a single ramp, the JMeter Plugins project offers the Concurrency Thread Group (its suggested replacement for the older Stepping Thread Group) for stepped capacity testing, and the Ultimate Thread Group for freeform schedules such as a sudden spike test. Since JMeter 5.5 the core also ships an Open Model Thread Group (still marked experimental) that schedules arrivals per second instead of a fixed thread count.

How many virtual users can k6 handle?

In k6, each VU is a separate JavaScript VM running on Sobek, the Grafana fork of the goja engine written in Go. VUs are lighter than JMeter threads: the k6 docs put simple tests at about 1-5 MB of memory per VU and say a single k6 instance can run 30,000-40,000 VUs, while tests that upload files or load large JS modules need tens of megabytes per VU.

k6 sets the load shape through scenarios, each driven by an executor:

  • constant-vus. A fixed VU count runs as many iterations as it can for a duration.
  • ramping-vus. The VU count changes through stages. Used for stepped and spike shapes.
  • constant-arrival-rate. k6 starts iterations at a fixed rate and adds VUs (from preAllocatedVUs up to maxVUs) to keep that rate.
  • ramping-arrival-rate. The iteration start rate changes through stages.

The arrival-rate executors are useful when you care about throughput more than concurrency. They count iterations, not requests, so if each iteration makes one request, “30 iterations per second” is 30 RPS. They also keep sending at the target rate when the server slows down, whereas a fixed pool of VUs sends less as responses get slower and quietly softens the test. The k6 docs call these the open and closed models. Our k6 scenarios guide runs all six executors and shows an arrival-rate scenario, short of VUs, dropping two thirds of its iterations without failing the test.

Common VU misconfigurations

Five mistakes that produce misleading VU-based test results:

1. No think time

Setting VUs without think time turns a load test into a stress test. Each VU sends its next request the moment the last one returns, which multiplies the per-VU request rate (see the arithmetic above). The system fails at a VU count real users would never reach.

Fix: add think time measured from real sessions between user actions, or use arrival-rate scenarios that decouple VU count from RPS.

2. Insufficient ramp-up

Going from 0 to 1,000 VUs in 10 seconds is a spike test, not a load test. Caches need warm-up time, connection pools need to grow and a JVM needs to JIT-compile hot code. A ramp of several minutes lets the system stabilise at each VU level; our guide to the JMeter ramp-up period covers how to size it.

3. Single test-data row

If all 1,000 VUs use the same userId, the database caches that user’s data perfectly. The test reports fast responses that will not repeat in production, where 1,000 users cause 1,000 different cache misses.

Fix: parameterise test data from CSV files (JMeter’s CSV Data Set Config; in k6, papaparse loaded into a SharedArray so the file is held once rather than copied into every VU). Each VU then uses a different row.

4. Single source region

Running all VUs from one cloud region misses geo-distributed effects: CDN edge differences, regional rate limits and network latency. The numbers look better than the real world.

Fix: distribute VUs across regions. LoadFocus runs tests from 25 AWS regions and, on the Pro and Enterprise plans, can split the VUs by region with custom percentages, for example 50% US East, 30% EU West and 20% Asia Pacific.

5. The test harness itself saturates

A laptop running 3,000 VUs can hit its own CPU or network ceiling before the system under test does. The test then reports “the system is fast at 3,000 VUs” when the laptop was the bottleneck.

Fix: run from cloud load generators, or monitor the load generator’s CPU, memory and network during the run and abort if it saturates.

Best practices for VU-based load testing

  1. Match VU count to your actual traffic, not a round number. Forecast the RPS you need, then calculate VUs = RPS x (response time + think time) and test that number.
  2. Use arrival-rate scenarios when throughput is the requirement. “Hold 500 RPS” maps directly onto a production SLO; “hold 1,600 VUs at 3 s think time” only does through the arithmetic.
  3. Parameterise test data. Each VU should have its own inputs, or cache hit rates will flatter the result.
  4. Add realistic think time, taken from your analytics.
  5. Ramp up over minutes, not seconds, unless the burst is what you are testing.
  6. Distribute VUs across regions. Single-region tests miss geo-distributed bottlenecks.
  7. Monitor the test harness. If the load generator saturates, the numbers lie.

Frequently Asked Questions

What is a virtual user in load testing?

A virtual user (VU) is one simulated concurrent user generated by a load testing tool such as JMeter, k6, Gatling or Locust. Mechanically it is a thread (JMeter) or a JavaScript VM (k6) that runs a scripted user flow against the system under test. 100 VUs means 100 simulated users active at the same time.

What is the difference between virtual users and real users?

Virtual users are scripted, repeatable and controlled by the test harness. Real users are unpredictable and hit the system in patterns you did not design. VUs suit capacity planning and SLO validation; real-user monitoring catches the long tail of real-traffic edge cases.

What is the difference between virtual users and RPS?

VUs measure concurrent activity (how many users are in the system at once). RPS measures the rate of work (how many requests the system completes per second). One VU produces 1 / (response time + think time) requests per second, so with 3 seconds of think time and 200 ms responses that is about 0.31 RPS per VU.

How many virtual users can JMeter handle?

It depends on the script, heap size and hardware, so measure the ceiling on your own load generator. Run in non-GUI mode with listeners off to raise it. For more VUs, use JMeter’s distributed mode (one client controlling several JMeter servers) or a managed runner such as LoadFocus, which runs JMeter plans with up to 20,000 virtual users.

How many virtual users can k6 handle?

The k6 documentation says a single k6 instance can run 30,000-40,000 VUs, with simple tests using about 1-5 MB of memory per VU. Heavier scripts use much more. Beyond one machine, k6 runs distributed through the k6-operator on Kubernetes or on a managed service.

Should I use think time in load tests?

Yes, for tests that simulate realistic user behaviour. Take the pause from your analytics. Without think time each VU sends requests as fast as responses return, and the test behaves like a stress test at a much lower VU count.

What is the difference between VUs and concurrent users?

In test design they are used interchangeably: concurrent users is the business term and VUs the test-harness mechanism, so 1,000 concurrent users means 1,000 VUs in the test. Analytics tools count “concurrent users” differently (sessions active in a time window), so check which definition a number came from before you copy it into a test.

How many virtual users should I simulate for a realistic load test?

Start from the throughput you need, not from a VU count. Forecast peak requests per second (or transactions per second), measure the average response time and think time per step, and calculate VUs = RPS x (response time + think time). Alternatively, use arrival-rate scenarios (k6 constant-arrival-rate, JMeter’s Open Model Thread Group or the Arrivals Thread Group plugin) that add VUs as needed to hold the target rate.

Why does my load test show fast response times but production is slow?

Usually a test design problem: no think time, a single test-data row (perfect cache hits), a single source region (no geo effects) or a saturated load generator. See the misconfigurations section above.

Bottom line

Virtual users are the test-harness primitive that simulates concurrent activity. VU count alone means little without its context: think time, response time, ramp-up shape, test data variety, source regions and harness capacity all change what a given number of VUs does to your system.

To run the result at scale, LoadFocus runs JMeter and k6 scripts from 25 cloud regions, keeps the think times, pacing and CSV data in your script, splits VUs across regions by percentage and shows how many VUs were running when each error appeared. For scalability validation, where you need to see how throughput changes with VU count, set a ramp-up time and steps so users arrive gradually. For the k6 side in more depth, see running k6 at scale.

If your team wants engineers to design the VU model for your scenario, LoadFocus offers load testing services: they write the JMeter or k6 scripts, run them and deliver the report.

Sources

  • Grafana k6 documentation: Running large tests (grafana.com/docs/k6/latest/testing-guides/running-large-tests) (VUs per instance, memory per VU), Executors (grafana.com/docs/k6/latest/using-k6/scenarios/executors), Constant arrival rate (grafana.com/docs/k6/latest/using-k6/scenarios/executors/constant-arrival-rate), Open and closed models (grafana.com/docs/k6/latest/using-k6/scenarios/concepts/open-vs-closed) and Data parameterization (grafana.com/docs/k6/latest/examples/data-parameterization), read October 2026.
  • Apache JMeter documentation: Thread Group and Open Model Thread Group and Remote testing, read October 2026.

Bogdan

Founder at LoadFocus

Bogdan builds and runs the tools this blog is about. He writes from what the products actually do in production, including the parts that break.

Weekly tips right in your inbox.

Don’t miss out on the latest testing tips and tricks, tools and news related to software testing.

Already +20,000 members in the list.

The Load Testing Platform with AI That Explains Your Results

Load test websites and APIs from 25 cloud regions, monitor page speed and uptime, and get AI analysis that explains your results in plain English.

Cloud load testing, website speed and API monitoring in one place.

Hi there! Can I share a cool thing I'm working on with you?

A software testing newsletter that sparks joy.

Our goal is to create helpful content for software developers and testers.

If you're not into it, you can unsubscribe instantly.