7 minutes read

Virtual users (VUs) are the simulated humans that hit your system during a load test. They’re the load. Where real users come from browsers and apps, VUs come from a test harness. JMeter threads, k6 worker goroutines, Locust greenlets. Each VU sends requests, waits for responses, sometimes pauses (“think time”), and repeats. Aggregate enough VUs and you get traffic that looks like a real audience.

This post covers what virtual users actually are, how they differ from real users and from requests-per-second (RPS), how the major tools model them, and the common misconfigurations that make VU-based test results lie.

Is Your Infrastructure Ready for Global Traffic Spikes?

Unexpected load surges can disrupt your services. With LoadFocus’s cutting-edge Load Testing solutions, simulate real-world traffic from multiple global locations in a single test. Our advanced engine dynamically upscales and downscales virtual users in real time, delivering comprehensive reports that empower you to identify and resolve performance bottlenecks before they affect your users.

View Pricing
Real-time insights
Discover More
Global scalability

The quick answer

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

Virtual Users (VUs)Real UsersRPS (Requests Per Second)
What it isA test thread/process simulating one concurrent humanAn actual human in front of a browser/appA rate of work the system completes
Controlled byYou (the test design)Whoever your audience isEmerges from VUs × request rate × think time
Used forExpressing test loadProduction trafficMeasuring server-side capacity
Has think time?Optional, configurableYes, between actionsN/A. It’s a rate

For a deeper dive into how VUs translate to RPS in practice, see understanding the difference between virtual users and requests per second.

What is a virtual user?

A virtual user is a unit of concurrent simulated activity in a load test. Mechanically, it’s typically one thread (in JMeter), one goroutine (in k6), or one greenlet/coroutine (in Locust) that:

Think your website can handle a traffic spike?

Fair enough, but why leave it to chance? Uncover your website’s true limits with LoadFocus’s cloud-based Load Testing for Web Apps, Websites, and APIs. Avoid the risk of costly downtimes and missed opportunities—find out before your users do!

Effortless setup No coding required
  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, measures latency.
  4. Optionally pauses for “think time” between actions (3-10 seconds is realistic).
  5. Loops, either through the same flow again or exits.

100 VUs running concurrently = 100 of these threads/coroutines running in parallel. To the system under test, they look indistinguishable from 100 real users hitting the service simultaneously.

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’re predictable in ways real users aren’t:

  • 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:

LoadFocus is an all-in-one Cloud Testing Platform for Websites and APIs for Load Testing, Apache JMeter Load Testing, Page Speed Monitoring and API Monitoring!

Effortless setup No coding required
  • 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’re stress-testing; too long and you’re 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, payment methods. VUs share a CSV file. 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.

VUs vs RPS

VUs and RPS measure different things and get confused constantly:

  • VUs = concurrent threads/users active at the same time. A LOAD measurement.
  • RPS = requests per second the system handles. A WORK measurement.

The conversion depends on per-VU request rate, which depends on think time:

  • A VU with 3 seconds of think time produces ~0.3 RPS (each request waits 3s before the next).
  • A VU with no think time produces 10-100+ RPS (sends as fast as the network/CPU allows).
  • A VU running a complex multi-step flow might produce different RPS at different stages.

Rule of thumb for typical web flows: 1 VU ≈ 0.3-1 RPS with realistic think time. If you need to handle 1,000 RPS and your think time is 3 seconds, you need ~3,000 VUs.

When someone tells you “we need to handle 5,000 users”. Clarify. Concurrent VUs? Per-second RPS? Different problems with different test designs.

For the full breakdown, see understanding the difference between virtual users and requests per second.

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 to start all threads (10 minutes to reach 1,000 VUs = ~1.67 VUs added per second).
  • Loop count or duration. Whether each VU runs the script N times or for D seconds.

The VU executes the samplers in the Thread Group sequentially: HTTP Sampler, Constant Timer (think time), next HTTP Sampler, and so on.

Per-machine VU ceiling: a typical JMeter instance handles 1,000-3,000 VUs before JVM heap or thread-context-switch overhead becomes the bottleneck. Beyond that you need distributed load testing (multiple JMeter slaves coordinated by a master).

The Stepping Thread Group plugin (and Ultimate Thread Group plugin) let you ramp VUs in steps for capacity testing or in sudden bursts for spike testing.

How k6 models virtual users

In k6, each VU is a JavaScript runtime context (powered by goja, a Go-based JS engine). VUs are much lighter-weight than JMeter threads. A typical k6 instance handles 10,000-30,000 VUs from a single machine because goroutines + goja are leaner than Java threads.

k6’s scenarios configuration is more flexible than JMeter’s Thread Group:

  • constant-vus. Fixed VU count, runs for a duration. The straightforward case.
  • ramping-vus. VU count ramps through multiple stages. Used for stepped load shapes.
  • constant-arrival-rate. Fixed RPS regardless of VU count (VUs auto-scaled to maintain rate). Closer to real-world traffic modelling.
  • ramping-arrival-rate. RPS ramps through stages. Used for arrival-rate-based capacity testing.

The arrival-rate scenarios are particularly useful when you care about RPS, not VUs (which is most of the time). You tell k6 “hold 500 RPS for 10 minutes” and it provisions whatever VU count is needed.

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 hammers the server as fast as possible, generating unrealistically high per-VU RPS. The system fails faster than it would under real traffic.

Fix: add realistic think time (3-10 seconds) 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. Most caches need warm-up time; the connection pool needs to grow; the JVM needs to JIT-compile. A 10-minute ramp-up gives the system time to stabilise at each VU level.

3. Single test-data row

If all 1,000 VUs use the same userId, the database caches that user’s data perfectly. Test reports “fast performance” that won’t replicate in production where 1,000 users have 1,000 different cache misses.

Fix: parameterise test data via CSV files (JMeter CSV Data Set Config, k6 papaparse). Each VU pulls a different row.

4. Single source region

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

Fix: distribute VUs across multiple regions. LoadFocus runs distributed load tests from 25+ AWS regions with VU allocation per region (50% US East, 30% EU, 20% APAC, etc.).

5. Test harness itself saturates

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

Fix: run from a cloud test harness, OR monitor the test machine’s CPU/memory/network during the run and abort if it saturates.

Best practices for VU-based load testing

  1. Match VU count to your actual traffic shape, not a round number. Don’t pick “1,000 VUs” because it sounds nice. Forecast traffic, calculate VUs from RPS × think time, test that number.
  2. Use arrival-rate scenarios when possible. “Hold 500 RPS” is more meaningful than “hold 1,500 VUs at 3s think time”. The former matches a production SLA directly.
  3. Parameterise test data. Each VU should have unique inputs. Cache hit rates will lie otherwise.
  4. Add realistic think time. 3-10 seconds between user actions. Without think time you’re stress-testing.
  5. Ramp up over minutes, not seconds. Give caches and connection pools time to warm.
  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’s a virtual user in load testing?

A virtual user (VU) is one simulated concurrent user generated by a load testing tool like JMeter, k6, Gatling, or Locust. Mechanically, it’s a thread (JMeter) or goroutine (k6) that runs a scripted user flow against the system under test. 100 VUs = 100 simulated users hitting the system simultaneously.

What’s the difference between virtual users and real users?

Virtual users are scripted, repeatable, controlled by the test harness. Real users are unpredictable, varied, and hit the system in patterns you didn’t design. VUs are useful for capacity planning and SLO validation; real-user monitoring (RUM) catches the long tail of real-traffic edge cases.

What’s the difference between virtual users and RPS?

VUs measure concurrent activity (how many users are “in” the system at once). RPS measures rate of work (how many requests the system completes per second). With realistic think time, 1 VU produces ~0.3-1 RPS. See the dedicated VUs-vs-RPS post for the full breakdown.

How many virtual users can JMeter handle?

A typical JMeter instance handles 1,000-3,000 VUs before JVM heap or thread-context-switch overhead caps it. For higher VU counts you need distributed JMeter (master + slave nodes) or a managed runner like LoadFocus.

How many virtual users can k6 handle?

k6 handles 10,000-30,000 VUs from a single machine. Much higher than JMeter because goroutines + goja are lighter-weight than Java threads. For higher counts, k6 supports distributed execution via the k6-operator for Kubernetes or managed runners like LoadFocus.

Should I use think time in load tests?

Yes, for tests that simulate realistic user behaviour. 3-10 seconds of think time between actions matches typical browser-session pacing. Without think time, your “load test” is actually a stress test. Each VU hammers the server as fast as possible.

What’s the difference between VUs and concurrent users?

In practice they’re used interchangeably. Concurrent users is the conceptual term; VUs is the test-harness mechanism. 1,000 concurrent users = 1,000 VUs in the test.

How do I pick the right VU count?

Forecast peak traffic (RPS or transactions/second), then calculate VUs needed: VUs ≈ RPS × average think time + per-flow VU consumption. Or use arrival-rate scenarios (k6 constant-arrival-rate, JMeter Concurrency Thread Group) that auto-scale VUs to maintain target RPS. Usually more honest than picking a VU count directly.

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

Most common cause: test harness misconfiguration. Common culprits. No think time (turns into stress test), single test-data row (perfect cache hits), single source region (misses geo effects), test harness saturation (load generator was the bottleneck). See the misconfigurations section above.

Bottom line

Virtual users are the test-harness primitive that simulates concurrent activity. They’re how you express load in a load test. VU count alone is meaningless without context: think time, ramp-up shape, test data variety, source regions, harness capacity all matter.

For load tests that produce honest VU-to-real-world numbers, LoadFocus runs JMeter and k6 from 25+ cloud regions with realistic think time, parameterised data, and distributed VU allocation. For scalability validation where you need to know how throughput scales with VU count, the same infrastructure runs the stepped ramps that map the curve.

If your team wants engineers to design the right VU shape for your specific scenario, LoadFocus offers load testing services where the VU model is built into the test plan as a first-class design decision.

How fast is your website? Free Website Speed Test