Mykhailo Ralduhin

Written by: Senior QA Engineer

Mykhailo Ralduhin

Updated: 12.10.2026

16 min read

Apache JMeter remains a practical choice for generating protocol-level load against web applications, APIs, databases, and other backend services. It can simulate concurrent users, measure response times and throughput, identify errors under load, and generate an HTML performance report.

This tutorial explains how to install JMeter, build a basic load test, run it correctly, interpret the results, and scale the test across several load generators. For a shorter definition of the tool and its supported use cases, see our JMeter glossary.

One limitation should be clear from the beginning: JMeter is not a real browser. According to the Apache JMeter documentation, it works at the protocol level and does not render pages or execute the JavaScript inside them. It is therefore suitable for measuring server and API performance, but not frontend rendering metrics such as Largest Contentful Paint.

Before creating a JMeter load test

A test script alone does not make a useful load test. Before opening JMeter, define:

  • which user journeys generate the most traffic or business value;
  • the expected number of concurrent users or requests per second;
  • how quickly the load should increase;
  • how long the system should remain under load;
  • acceptable response-time percentiles and error rates;
  • which application and infrastructure metrics will be monitored;
  • what test data and user accounts are required.

For example, an eCommerce test might cover product search, opening a product page, adding an item to the cart, and completing checkout in a payment sandbox. A useful workload should also reflect the expected distribution of these actions: users usually browse much more often than they purchase.

Define acceptance criteria before the test. A team might decide that the checkout API must maintain a specified p95 response time, error rate, and transaction throughput under the expected peak load. These values should come from product requirements, service-level objectives, production data, or business forecasts—not from generic industry benchmarks.

Whenever possible, use a production-like environment with comparable application versions, infrastructure, databases, caches, and integrations. Running a large test against production requires explicit approval and safeguards because it can affect real users and data.

Step 1: Install and start JMeter

JMeter is a Java application. At the time of this update, the official Apache download page lists JMeter 5.6.3 and requires Java 8 or later. Check the page for the latest stable release before installation.

To install JMeter:

1.
Install a supported Java runtime or JDK.
2.
Set JAVA_HOME if your operating system does not detect Java automatically.
3.
Download the JMeter binary archive.
4.
Verify the downloaded archive using its signature or SHA-512 checksum.
5.
Extract the archive into a directory without spaces.
6.
Open the bin directory.
7.
Run jmeter.bat on Windows or jmeter on macOS or Linux.

The graphical interface should be used to create and debug scripts. Apache explicitly recommends running real load tests in command-line mode because the GUI and visual listeners consume resources that could distort the results.

Step 2: Create a test plan and Thread Group

A JMeter Test Plan contains the complete test scenario: virtual users, requests, test data, validations, timers, and result handling. The official web test plan tutorial uses the same basic structure described below.

Start by renaming the Test Plan so its purpose is clear—for example, Checkout API Load Test.

Then right-click the Test Plan and select:

Add → Threads (Users) → Thread Group

Adding a Thread Group in JMeter

A Thread Group controls the load generated by a group of virtual users.

Configure these fields:

Setting
What it controls

Number of Threads

Number of concurrent virtual users

Ramp-up Period

Time over which JMeter starts all threads

Loop Count

Number of times each user repeats the scenario

Duration

Total time for which the Thread Group runs

Startup Delay

Delay before the group begins

Thread Group settings in JMeter

Suppose you configure 100 threads with a 60-second ramp-up. JMeter will gradually start the 100 virtual users during that minute instead of starting all of them at once.

Do not choose these values arbitrarily. Concurrent users, requests per second, and completed business transactions are different workload measures. A scenario with long pauses may produce far fewer requests than one that sends requests continuously, even if both use 100 threads.

Start with a small smoke run—one or two users and a single iteration—to confirm that the test logic works. Increase the load only after requests, sessions, data, and assertions have been validated.

Step 3: Add request configuration

Configuration elements reduce duplication and make a test plan easier to maintain.

Add HTTP Request Defaults

Right-click the Thread Group and select:

Add → Config Element → HTTP Request Defaults

HTTP Request Defaults

Enter values shared by all requests:

  • protocol, such as https;
  • server name or IP;
  • port, if the application uses a non-standard one;
  • optional connection and response timeouts.

Do not include https:// in the Server Name field. Add https separately as the protocol.

Add a Cookie Manager

For applications that use cookies and server-side sessions, add:

Add → Config Element → HTTP Cookie Manager

The Cookie Manager allows each virtual user to store and send cookies throughout the scenario.

Add a Header Manager

For REST APIs or requests requiring custom headers, add:

Add → Config Element → HTTP Header Manager

HTTP Header Manager

Common examples include:

  • Content-Type: application/json;
  • Accept: application/json;
  • an authorization token;
  • an application-specific correlation or tenant header.

Avoid hardcoding secrets in the .jmx file. Pass them through environment-specific properties, protected CI/CD variables, or a secure test-data mechanism.

Add an Authorization Manager if required

For supported HTTP authentication schemes, add:

Add → Config Element → HTTP Authorization Manager

HTTP Authorization Manager

Enter the base URL and the test account credentials. Token-based authentication generally requires a login request followed by extraction and reuse of the returned token.

Step 4: Add HTTP requests

Right-click the Thread Group and select:

Add → Sampler → HTTP Request

Create one sampler for each meaningful request in the user journey. A simplified eCommerce flow might include:

1.
POST /api/login
2.
GET /api/products
3.
GET /api/products/{id}
4.
POST /api/cart/items
5.
POST /api/checkout

For every sampler, configure:

  • a descriptive name;
  • HTTP method;
  • path;
  • query parameters;
  • request body, if applicable;
  • content encoding where necessary.

Use names such as Search products and Submit checkout, not HTTP Request 1. These labels become rows and series in the final report, so clear names make analysis much easier.

JMeter can also record browser traffic with its HTTP(S) Test Script Recorder. Recording may provide a useful starting point for older server-rendered applications, but recorded scripts should be cleaned before use. Remove requests to analytics, advertising, third-party widgets, fonts, and domains outside the system under test. Never generate load against an external service without permission.

Step 5: Add test data and correlation

A script that sends the same user credentials, product ID, or payload thousands of times rarely represents real usage. It may also produce misleading results because the application can repeatedly serve the same cached data.

Parameterize test data

Create a CSV file containing test accounts or input values, then add:

Add → Config Element → CSV Data Set Config

For example:

username,password,product_id

load_user_001,password_001,10001

load_user_002,password_002,10002

Reference these values in requests as:

${username}

${password}

${product_id}

The JMeter best-practices guide recommends reading prepared data from CSV instead of generating large volumes of random test data during execution, which consumes additional CPU and memory.

Correlate dynamic values

Applications frequently return values that must be passed into later requests, including:

  • access tokens;
  • CSRF tokens;
  • session IDs;
  • order IDs;
  • resource IDs;
  • pagination cursors.

Add an appropriate Post-Processor to the request that returns the value. For JSON APIs, use a JSON Extractor. Save the result to a variable—for example, access_token—and reference it later as ${access_token}.

Test correlation with a single user before applying load. If every request fails with 401, 403, or invalid-session errors, the test is measuring broken script logic rather than system performance.

Step 6: Add timers and assertions

Model user think time

Without timers, each JMeter thread sends the next request immediately after receiving the previous response. Real users pause to read pages, select products, or complete forms.

Add a Constant Timer, Uniform Random Timer, or another suitable timer to introduce realistic pauses. The JMeter Test Plan documentation warns that a plan without delays can send requests much faster than intended and overwhelm the target with an unrealistic traffic pattern.

Validate responses

A request returning HTTP 200 is not necessarily successful. An application may return an error message, an empty result, or an invalid business response with a successful status code.

Use lightweight assertions to check:

  • expected response codes;
  • required text or JSON fields;
  • the presence of an identifier;
  • maximum response duration where appropriate.

Assertions should verify that the tested transaction is valid, but excessive or computationally expensive assertions can reduce the load generator’s capacity. Keep performance-test validations focused on critical conditions.

Step 7: Add listeners and validate the script

Listeners display or store the results produced by samplers.

During script development, useful listeners include:

  • View Results Tree;
  • Summary Report;
  • Aggregate Report;
  • Response Time Graph.

Aggregate Report

Run the scenario with one or two users and inspect individual requests in View Results Tree. Verify request bodies, headers, cookies, extracted variables, response codes, and assertions.

Checking a failed request in View Results Tree

Do not leave heavy visual listeners enabled during a full load test. Apache recommends using View Results Tree and View Results in Table only while debugging because retaining detailed samples consumes substantial memory.

Once the script is valid:

1.
Disable or remove heavy listeners.
2.
Save the plan as a .jmx file.
3.
Confirm that test data is available.
4.
Clear any earlier results.
5.
Start the real test from the command line.

Step 8: Run JMeter in command-line mode

Use the following command:

jmeter -n -t load-test.jmx -l results.jtl -e -o html-report

The parameters mean:

  • -n — run in command-line mode;
  • -t — specify the JMeter test plan;
  • -l — write samples to a results file;
  • -e — generate the HTML dashboard after the test;
  • -o — specify the report directory.

The report directory must be empty before the test begins.

The JMeter dashboard documentation explains that the generated report includes request statistics, configurable percentiles, error summaries, throughput graphs, response-time graphs, and APDEX data.

Run a baseline test first. Then increase the load through separate, controlled stages. A sudden jump from one user to several thousand makes it harder to identify the point at which response times, errors, or resource consumption begin to deteriorate.

Monitor the application during the run. JMeter explains what clients experienced, but server-side monitoring helps explain why. Correlate JMeter results with CPU, memory, garbage collection, database latency, connection pools, cache hit rates, queue depth, container restarts, and downstream-service errors.

How to read JMeter results

Do not judge a load test only by average response time. Averages can hide a slow segment of requests that affects a meaningful share of users.

Metric
What it tells you

Sample count

Number of completed requests for a sampler

Average

Mean response time; useful for context but not sufficient alone

Median

Middle response time, also known as p50

p90, p95, p99

Time within which 90%, 95%, or 99% of samples completed

Error %

Share of failed requests or failed assertions

Throughput

Requests or transactions completed per unit of time

Latency

Time until the first part of the response is received

Connect time

Time required to establish a connection, including the SSL handshake

Received/sent KB

Volume of network data transferred

Start with four questions:

1.
Did the system sustain the required throughput?
2.
Did p95 and p99 response times remain within the agreed thresholds?
3.
Did the error rate remain acceptable?
4.
Did performance remain stable throughout the test?

Then examine how metrics changed as load increased. A useful analysis might show that throughput stopped growing while p95 response time and database connection waits increased. That pattern is more actionable than saying only that the application was “slow.”

Do not assume every error comes from the application. Failures can also be caused by exhausted test data, expired tokens, load-generator CPU limits, network saturation, or an incorrect JMeter scenario.

Distributed load testing with JMeter

A single machine may be unable to generate the required traffic. In that case, JMeter can control multiple remote load generators from one controller.

Before using distributed mode:

  • install the same JMeter version on every node;
  • use the same Java version;
  • install the same required plugins;
  • copy CSV files and other dependencies to every generator;
  • configure RMI over SSL;
  • open the required ports between the controller and agents;
  • confirm that no load generator is CPU-, memory-, or network-bound.

The controller sends the test plan to each remote engine, but it does not automatically send external data files.

Start jmeter-server on each remote generator, then run the test from the controller:

jmeter -n -t load-test.jmx -R 10.0.0.11,10.0.0.12 -l distributed-results.jtl -e -o distributed-report -X

One detail is easy to miss: every remote engine executes the complete test plan. If the plan contains 500 threads and you run it on four remote engines, the combined load is 2,000 threads—not 500. This behavior is documented in Apache’s remote testing guide.

Do not run a load generator on the application server. JMeter would consume the same CPU, memory, and network resources you are trying to measure, making the results unreliable.

For large cloud-based tests, independently managed JMeter instances may sometimes be easier to scale than RMI-based remote mode. Their result files can be collected and combined after execution.

JMeter pros and cons

Pros
Cons

Free and open source

Large tests require careful JVM and resource configuration

Supports HTTP, REST, SOAP, JDBC, JMS, TCP, FTP, and other protocols

The GUI should not be used to generate production-scale load

Visual interface helps QA engineers create and debug scenarios

XML-based .jmx files can be awkward to review in Git

Can run from the command line and CI/CD pipelines

It does not render pages or execute browser JavaScript

Supports parameterization, correlation, assertions, and plugins

Realistic workload modeling still requires performance-engineering knowledge

Can distribute load across multiple machines

RMI-based distributed execution adds network and configuration complexity

JMeter is a strong fit when a team needs broad protocol support, has existing .jmx assets, or prefers a visual test-plan editor. Its age is not automatically a disadvantage: the mature ecosystem and large knowledge base are valuable for complex enterprise environments.

The main risk is treating it as a simple “number of users” generator. Reliable results depend on realistic data, pacing, correlation, monitoring, controlled environments, and clear performance requirements.

When k6 or Gatling may be a better choice

JMeter is not the best option for every engineering team.

Consider k6 when you want a developer-oriented, code-first workflow, concise JavaScript-based scenarios, and straightforward integration with CI/CD and modern observability systems.

Consider Gatling when the team prefers code-defined simulations and needs a high-performance load-generation model suitable for engineering-led performance testing.

JMeter may remain the better option when you need its broad protocol coverage, have a large existing test suite, or want QA engineers to build and inspect scenarios through a GUI before running them from the command line.

Our comparison of load testing tools covers JMeter, k6, Gatling, Locust, and other options in more detail.

Common JMeter load testing mistakes

Several mistakes can invalidate an otherwise well-built test:

  • running the full load from the GUI;
  • leaving View Results Tree enabled;
  • testing without response assertions;
  • using one account or one cached data record for every user;
  • omitting timers and generating an unrealistic request storm;
  • running JMeter on the system under test;
  • testing third-party services without permission;
  • analyzing only average response time;
  • increasing the load before validating the script;
  • reporting client-side symptoms without checking backend metrics;
  • treating a high thread count as proof of realistic traffic;
  • changing the application, environment, and test plan between runs without documenting the differences.

A load test should be repeatable. Store the .jmx plan, configuration, test data, application build, environment details, thresholds, and results together so later runs can be compared under equivalent conditions.

FAQ

Is JMeter free?

Yes. Apache JMeter is open-source software. Teams can use it without purchasing a commercial license, although infrastructure, test-data preparation, monitoring, maintenance, and engineering time still have costs.

Can JMeter test REST APIs?

Yes. Its HTTP Request sampler can send REST API requests using methods such as GET, POST, PUT, PATCH, and DELETE. It also supports headers, JSON request bodies, parameterization, authentication, response extraction, and assertions.

Can JMeter test frontend browser performance?

Not completely. JMeter measures protocol-level request and response performance but does not render pages or execute their browser JavaScript. Use browser-based performance tools when you need rendering and user-experience metrics.

How many users can JMeter simulate?

There is no universal limit. Capacity depends on the scenario, response sizes, listeners, assertions, scripts, available CPU and memory, network bandwidth, and the number of load generators. Run a calibration test and monitor the JMeter host to confirm that the load generator is not the bottleneck.

Should JMeter tests run in CI/CD?

Small baseline or threshold tests can run in CI/CD to detect obvious regressions. Longer peak, endurance, stress, and distributed tests are usually better scheduled in controlled environments where infrastructure and monitoring are available.

Need help testing performance under realistic load?

Creating requests is the easy part. The harder work is designing a representative workload, preparing test data, monitoring the complete architecture, and connecting response-time changes to their actual cause.

DeviQA’s load and stress testing services help engineering teams plan, execute, and analyze performance tests for web platforms, APIs, distributed systems, and business-critical applications.

Mykhailo Ralduhin

About the author

Mykhailo Ralduhin

Senior QA engineer

Mykhailo Ralduhin is a Senior QA Engineer at DeviQA, specializing in building stable, well-structured testing processes for complex software products.