Non-GUI Mode Execution in JMeter: The Complete Advanced Guide
Learn why serious load tests run from the command line, how the command is built, how results, reports, properties and memory are managed, how to stop, distribute and automate runs, and how to avoid the mistakes that spoil large tests.
Picture a busy restaurant. The dining room has candles, music and decorations, and it is where guests feel welcome. The kitchen has none of that. It has stoves, knives and a team that works fast, because its only job is to cook. If you tried to cook five hundred meals inside the dining room, among the candles and the guests, the food would be slow and the room would be a mess. JMeter has the same two rooms. The graphical window is the dining room, where you design and review a test. The command line is the kitchen, where the heavy cooking happens.
The non-GUI mode of JMeter runs a test plan without opening the graphical window. It uses fewer resources, it can run on servers that have no screen, and it fits neatly into automated pipelines. Every professional load test is executed this way, and JMeter itself prints a warning when you try to run a large test from the window. This guide explains non-GUI execution from the first command to the advanced topics: options, results, reports, properties, memory, stopping, distributed runs, containers, pipelines, security and design patterns.
1What Is Non-GUI Mode?
Running a test plan from the command line, with no window, only the work.
The simple definition
A GUI, short for graphical user interface, is the window with menus, trees and buttons that you see when you open JMeter. A command line is a text prompt where you type an instruction and the computer carries it out. Non-GUI mode means you start JMeter from the command line, give it a saved test plan, and it runs the plan without ever drawing the window. Many people also call it command-line mode, CLI mode or headless mode.
You still create the plan in the window. Non-GUI mode is only about how the plan is run. The plan is a file, usually with the extension .jmx, and the same file can run in either mode. This is why the workflow is so comfortable: design in the dining room, cook in the kitchen.
An architect draws a building on a computer with all the beautiful tools, but the builders work on the construction site with helmets and machines, not with the drawing software. The plan file is the blueprint, and non-GUI mode is the construction site.
What you get in return
Without the window, JMeter does not spend memory on drawing trees, updating tables and keeping results visible on the screen. All that effort goes to creating load and recording timings. The result is that one machine can generate more users, and the numbers it measures are more honest.
Think of the window as a tool for building and debugging only. Once a plan works with a few users, save it, close the window and use the command line for every real run.
2Why Load Tests Should Not Run in the GUI
The window is helpful for design, and costly during a real run.
When JMeter runs inside its window, every listener, tree and table that is open keeps data in memory and redraws itself as results arrive. At a small scale this is harmless. At a large scale it becomes the biggest consumer of resources on the machine. Here is what goes wrong.
Memory pressure
Result listeners keep every sample and response in memory. A long test with many users can fill the heap and crash the tool.
CPU spent on drawing
The window updates graphs and tables constantly. That CPU time is taken from the threads that create the load.
Distorted timings
A busy generator delays its own requests and measures its own delay as the server’s response time.
Fragile sessions
A window needs a logged-in desktop. A closed laptop, a dropped remote session or a screen lock can disturb the run.
No automation
Pipelines cannot click buttons. They can only run commands.
flowchart LR
A["Run inside the window"] --> B["Listeners store and draw every result"]
B --> C["Heavy memory and CPU use"]
C --> D["Generator becomes the bottleneck"]
D --> E["Results measure the tool, not the service"]
The warning JMeter gives you
When you start the window, JMeter prints a message in its console that says, in short, that the graphical mode is for creating and debugging and that real load tests should use the command line. The message exists because the tool’s own authors saw the same problem many times.
Judging the performance of a service from a run in the window, and then being surprised when the command-line run shows different numbers. The first run partly measured the window.
Example: the test that ate its own machine
A team ran a thousand-user test from the window with a results table and a graph open. After twenty minutes the tool froze, response times jumped and the team blamed the new release. The same plan, run from the command line with a report, showed stable times and no errors. The release was fine. The window was the problem.
3GUI Mode and Non-GUI Mode Side by Side
Same plan, same engine, very different jobs.
The test engine, the part that creates virtual users and sends requests, is the same in both modes. What differs is everything around it. The table shows the main differences.
| Aspect | GUI mode | Non-GUI mode |
|---|---|---|
| Main purpose | Build, debug and review plans | Run real load tests |
| Resource use | High, because of drawing and listeners | Low, focused on the engine |
| Results | Seen live in listeners | Saved to a file and reports |
| Automation | Not possible | Easy with scripts and pipelines |
| Remote servers | Needs a desktop session | Works through a plain text connection |
| Best number of users | One to a few for debugging | As many as the machine can generate |
| Repeatability | Depends on manual clicks | The same command gives the same run |
Different jobs, not rival tools
Do not think of one mode as good and the other as bad. Each has a job. The window is unbeatable for building plans, testing paths and checking a single reply. The command line is unbeatable for running at scale and for repeating the same run again and again. A mature team uses both, in a clear order: design first, run second.
A recipe editor and a professional stove are not competitors. You write and taste-test the recipe on a small scale in one place, and you cook the banquet in another. Mixing the two jobs leads to burnt food.
Keep a habit: after every plan change, run one user for one loop in the window and check the replies. Then move to the command line for anything larger.
4The Standard Workflow: Design in the Window, Run on the Command Line
A repeatable path from idea to trustworthy results.
A good performance test follows a clear path. Each stage has a purpose and a place where it is done. Following the path keeps your results reliable and your team in agreement about how a number was produced.
flowchart TD
A["Design the plan in the window"] --> B["Debug with one user and a small loop"]
B --> C["Parameterise: users, ramp-up, duration as properties"]
C --> D["Remove heavy listeners and save the plan"]
D --> E["Run from the command line with a results file"]
E --> F["Generate the HTML report"]
F --> G["Analyse, compare and share"]
G --> H{"Need another run?"}
H -->|"Yes"| C
H -->|"No"| I["Archive plan, results and report"]
Design and debug
Use the window with one user. Check every reply, every extractor and every assertion.
Prepare for scale
Move numbers such as users and duration into properties, disable view-only listeners and save the file.
Smoke run on the command line
Run with a handful of users for a minute. Confirm that results are written and the command works.
Real run
Start the full test with a clear results file name, then wait for it to finish.
Report and review
Produce the dashboard, read the numbers, and keep everything together for later comparison.
Skipping the smoke run and launching a two-hour test on the first attempt. A typing mistake in a file path can then waste the whole slot, and you find out only at the end.
5Anatomy of the Non-GUI Command
A short command with a few clear parts, each with one job.
The basic command starts the JMeter launcher, which is a small script in the tool’s folder, and then adds options. Each option starts with a dash and a letter. Most of the time you need only a few of them. Reading a command from left to right tells you the whole story of the run.
| Part | Example | Meaning |
|---|---|---|
| Launcher | jmeter | The script that starts the tool, found in the bin folder |
| Non-GUI flag | -n | Do not open the window |
| Test file | -t plan.jmx | The saved plan to run |
| Results file | -l results.jtl | Where raw results of every request are written |
| Log file | -j run.log | Where the tool writes its own messages |
| Report options | -e -o report-folder | Create the HTML dashboard into a new folder when the test ends |
Reading it like a sentence
A full command reads like a sentence: “Run JMeter without a window, use this plan, write results to this file, write the tool log to that file, and make a dashboard in this folder at the end.” If you can say the sentence, you understand the command, and if a colleague asks what your run did, the sentence is the answer.
Ordering a coffee has the same shape: the shop, the drink, the size, the milk, the name on the cup. Each part is short, and together they say exactly what you want. The command line is an order slip for the kitchen.
Put the command into a small script file with a clear name and keep that script next to the plan. Anyone can then repeat the exact run, and the script itself documents how the run was made.
Reusing the same results file name for every run. New results may be added on top of old ones, and the report then mixes two different tests. Give each run a unique name, for example with the date and time.
Why a script beats memory
Commands grow longer as plans mature. A realistic command may carry a dozen properties, several file paths and a report folder. Typing it by hand invites small mistakes, such as a missing space or a wrong folder name, and nobody remembers last month’s exact settings. A script file records every choice, can be reviewed by a colleague, and can be stored in version control beside the plan. When a result needs explaining, the script is the evidence of what was run, and when it needs repeating, the script is the instruction.
6The Essential Command-Line Options
A small toolbox that covers almost every real test.
JMeter has many options, and you will use a handful daily. The table below lists the ones every tester should know. Exact spelling and behaviour can differ slightly between versions, so check the help text of your own installation by asking the launcher for its help.
| Option | Purpose | Notes |
|---|---|---|
-n | Run without the window | Always needed for command-line runs |
-t | Path of the test plan | Required with -n |
-l | Results file | Raw sample results, usually in CSV form |
-j | Tool log file | Messages from the engine, useful for problems |
-J | Set a JMeter property | Passes numbers and names into the plan |
-G | Set a property on remote servers | Used in distributed runs |
-D | Set a Java system property | For low-level settings |
-p | Use a property file | Keeps many settings in one file |
-q | Add an extra property file | Layered on top of the normal properties |
-e | Create the HTML report after the test | Needs -o |
-o | Report output folder | Should not exist yet, or must be empty |
-g | Create a report from an existing results file | No test is run |
-f | Force deletion of old results and report folder | Use with care |
-r and -R | Run on remote servers | -r uses the configured list, -R takes a list |
-X | Stop remote servers at the end | Used with remote runs |
-L | Set a log level | For example for a single component |
The core four
If you remember only four options, remember the non-GUI flag, the test file, the results file and the report option pair. Everything else is added when a need appears.
A Swiss army knife has many blades, but most people use the main blade and the bottle opener. The options above are the blades. Learn the main ones well, and look up the others when a job calls for them.
Ask the launcher for its help once on every new version and save the output in your notes. Option lists grow over time, and a quick look prevents you from relying on an outdated memory.
7The Results File: What It Holds and Why It Matters
The results file is the permanent memory of your test. Everything else is built from it.
During a non-GUI run, nobody watches tables fill up. The record of what happened is the results file, often called a JTL file after its common extension. Each line describes one request: when it started, how long it took, which request it was, whether it passed, what the status code was and more. A report, a chart or a comparison is only as good as this file, so it is worth understanding.
Typical columns
| Column | Meaning |
|---|---|
| timeStamp | When the request started |
| elapsed | Total time from sending to receiving the full reply, in milliseconds |
| label | The name of the request, which should be clear and unique |
| responseCode and responseMessage | The status code and message of the reply |
| threadName | Which virtual user sent it |
| success | True or false, after assertions are applied |
| bytes and sentBytes | Size of the reply and of the request |
| grpThreads and allThreads | How many users were active at that moment |
| Latency and Connect | Time to the first byte and time to connect |
What you control
A set of properties decides which columns are saved, and whether results are written as text with commas or as XML. Text with commas is the common choice, because it is compact, fast to write and understood by the report generator and by many analysis tools. Saving extra data such as full response bodies makes the file very large, and it also slows the run. Save only what you need.
flowchart LR
A["Virtual users send requests"] --> B["Engine records one line per request"]
B --> C["Results file"]
C --> D["HTML dashboard"]
C --> E["Charts and analysis tools"]
C --> F["Comparison between runs"]
A results file is like the receipt roll in a shop’s till. Each line records one sale. Daily summaries, monthly totals and tax reports are all produced from that roll. If the roll is lost or messy, none of the summaries can be trusted.
Saving response bodies for every request in a long test. The file grows to many gigabytes, disk writes slow the generator, and the file may contain tokens and personal data. Save bodies only for failures, if at all.
8Passing Values from the Command Line: Properties
One plan, many runs, no need to edit the file each time.
A property is a named setting that JMeter holds for the whole process. You can set properties on the command line, and the plan can read them. This lets you run the same plan with ten users in the morning and five hundred users in the evening without opening the file. It is the most important habit for clean automation.
| Way to set a value | Option | Scope | Typical use |
|---|---|---|---|
| JMeter property | -J with name and value | The local JMeter process | Users, ramp-up, duration, host names |
| Global property for remote servers | -G with name and value | Sent to all remote servers | The same settings in distributed runs |
| Java system property | -D with name and value | The Java process | Low-level settings and some library options |
| Property file | -p or -q | The local process | Many settings in one file under version control |
Reading a property inside the plan
The plan reads properties with a built-in function that takes a name and a default value. The default is used when the command line does not provide a value. This makes the plan run sensibly in the window, where nobody passes a value, and run with chosen values on the command line.
Defaults that are safe
Choose small defaults such as one user and a short duration, so a careless start in the window cannot overload a system.
Clear names
Use names such as users, rampUpSeconds, durationSeconds and baseHost. They tell the story of the run.
One property file per environment
A file for test, a file for staging. The command picks the file, and the plan stays the same.
Log the values used
Print the chosen values at the start of the run, so the log tells exactly what was used.
A cake recipe says “bake for the time on the dial”. The dial is set outside the recipe each time. Properties are the dials of a test plan: the recipe stays the same while the settings change.
Putting passwords and tokens in command arguments. Command lines can appear in process lists, shell history and pipeline logs. Pass secrets through protected files or secret stores, and keep them out of visible commands.
9Parameterising Load: Users, Ramp-Up and Duration
Making the shape of the load easy to change from the outside.
The shape of a load test is made of a few numbers: how many virtual users, how fast they start, how long they run and how long they pause between steps. Each number belongs in a property, so that the same plan can serve smoke tests, normal-load tests, peak-load tests and endurance tests.
| Test type | Users | Ramp-up | Duration | Goal |
|---|---|---|---|---|
| Smoke | 1 to 5 | Very short | One to two minutes | Prove the plan and command work |
| Load | Expected normal peak | Gentle | Thirty to sixty minutes | Check behaviour at expected load |
| Stress | Above expected peak, in steps | Stepwise | Until the system shows strain | Find the breaking point |
| Endurance | Moderate | Gentle | Several hours | Find leaks and slow decay |
| Spike | Sudden jump | Very short | Short bursts | Check recovery from sudden traffic |
Why ramp-up matters
Starting all users at the same second creates an artificial storm that real life rarely produces. A ramp-up spreads the starts across a period, so the system warms up in a natural way. For analysis, you can also ignore the ramp-up period in the report and look only at the stable part of the run.
flowchart LR
A["Command line values: users, ramp-up, duration"] --> B["Properties read by the plan"]
B --> C["Thread group settings"]
C --> D["Shape of the load"]
D --> E["Smoke, load, stress, endurance or spike run"]
A stadium does not open all gates in one second. Spectators enter over an hour, and the crowd builds naturally. A ramp-up is the opening of the gates, and it lets staff see problems while the crowd is still small.
Always run a smoke test with the real command before a large run, using the same plan and a tiny load. If the smoke run succeeds, the big run fails only for real reasons.
10The HTML Dashboard Report
A ready-made report that turns the results file into charts and tables.
JMeter can create a web page called the dashboard report from a results file. It shows summary tables, response times over time, throughput, error percentages, percentiles and more. You can create it at the end of a run, by adding the report options to the command, or later from an existing results file.
At the end of the run
Add the report flag and an output folder to the command. The dashboard is built when the test ends.
From an existing file
Use the option that takes a results file and an output folder. No test runs, only the report is built. This is handy when the run happened on another machine.
What the dashboard shows
| Section | What it helps you see |
|---|---|
| APDEX table | A satisfaction score based on response time thresholds |
| Statistics table | Counts, errors, average, median and percentiles for each request |
| Errors table | Which errors occurred and how often |
| Response time charts | How times changed during the run, and how they spread |
| Throughput charts | Requests per second and bytes per second |
| Active threads chart | The shape of the load over time |
Rules to remember
The output folder must be new or empty, or the report generation refuses to continue. The results file must contain the columns the report needs, and the default settings usually provide them. Percentiles are more honest than averages, because a few very slow requests can hide behind a pleasant average.
A bank statement turns a long list of transactions into totals, charts and categories. The dashboard does the same with your requests. Nobody wants to read ten million lines, and everyone can read a one-page summary.
Reading only the average. An average of two hundred milliseconds can hide a small group of users who wait ten seconds. Check the ninety-fifth and ninety-ninth percentiles, and the error counts, before declaring a test healthy.
11Reading the Console Summary While the Test Runs
Without a window, the console is your live dashboard.
When a test runs in non-GUI mode, JMeter prints a short summary line to the console every so often. This is produced by a built-in element called the summariser. Each line shows how many requests were sent in the latest period and in total, the average, minimum and maximum response times, the number of errors and the rate of requests. By default the line appears about every thirty seconds, and a property can change that interval.
| Part of the line | Meaning | What to watch |
|---|---|---|
| Plus lines | Numbers for the latest interval only | Sudden jumps in time or errors |
| Equals lines | Numbers for the whole run so far | Overall trend |
| Count and rate | How many requests and how fast | A falling rate may mean server trouble or a stuck generator |
| Average, minimum, maximum | Response time in milliseconds | Maximum shows the worst case in that period |
| Error count and percentage | Failed requests | Any rise is a signal |
| Active users | The threads started and finished | Confirms the ramp-up works as planned |
Using it as an early warning
You do not need a graph to see trouble. If errors jump from zero to five percent in one interval, the plus line shows it at once. If the request rate falls while the user count stays the same, the server may be slowing down. Watching the console for the first few minutes of every large run costs nothing and catches many mistakes early, such as a wrong host name or an expired token.
A pilot watching the instrument panel does not see the whole sky but still knows altitude, speed and fuel. The console summary is a small panel: not pretty, but enough to tell whether the flight is going as planned.
Redirect the console output to a file as well as showing it on screen, so you can look back at the exact moment something changed. Many people forget this and lose the best clue.
Trusting the console summary alone for conclusions. It is a live hint, not a full analysis. Use the results file and the report for final decisions.
12The Tool Log and Log Levels
The log tells you what the tool itself thinks, which is different from what the server says.
JMeter keeps a log of its own activity, usually in a file named after the tool, or in the file you choose with the log option. The log records start and stop messages, warnings about missing files, errors from scripts, problems with connections and more. The results file records what happened to requests. The log records what happened to the tool. You need both.
Error
Something failed in a way that matters, such as a script that threw an exception.
Warn
Something looks wrong but the run continues, such as a missing optional file.
Info
Normal progress messages. This is the usual level for real runs.
Debug
Very detailed messages for one component. Use only for short investigations.
Reading the log after a problem
When a run behaves strangely, open the log and search from the end. Look for words such as error, exception, warning, refused and timeout. Problems that appear at the start of the file, such as a plan that could not find a data file, often explain what happened much later. Keep the log together with the results file, using a name that matches the run.
flowchart TD
A["Something looks wrong"] --> B{"Where is the clue?"}
B -->|"Slow or failing requests"| C["Results file and report"]
B -->|"Plan did not start or stopped early"| D["Tool log"]
B -->|"Machine looks overloaded"| E["Operating system monitoring"]
C --> F["Form a theory"]
D --> F
E --> F
A ship has a captain’s log of its own movements and a cargo manifest of what was carried. If the cargo is damaged, you check the manifest. If the ship was late, you check the captain’s log. Each answers different questions.
Turning on debug logging for the whole tool during a large run. The log grows very fast, disk writing slows the generator and the useful lines disappear in noise. Raise detail only for one component and only for a short run.
13Memory and the JVM: Giving JMeter Room to Work
JMeter runs on Java, and Java needs a clear memory budget.
JMeter is a Java program. Java programs run inside a JVM, a Java Virtual Machine, which has a fixed memory area called the heap. Objects such as virtual users, variables and stored results live in the heap. If the heap is too small, the program slows down as the garbage collector works harder and, in the worst case, stops with an out-of-memory error. If the heap is too large for the machine, the operating system may start swapping memory to disk, which is far worse.
Setting the heap
The launcher script defines a default heap size, and most installations allow you to override it through an environment variable before starting the tool. Check the comments in your launcher script for the exact variable name in your version. A common approach is to set the minimum and maximum heap to the same value, so the memory is reserved at start and does not resize during the run.
| Symptom | Likely cause | Direction |
|---|---|---|
| Out-of-memory error | Heap too small, or too much data kept in memory | Raise the heap, and reduce listeners, stored variables and saved bodies |
| Long pauses in the run | Heavy garbage collection | Give more heap, and lower the work per user |
| Machine becomes slow overall | Heap larger than free memory, causing swap | Lower the heap to fit the machine |
| Slow start-up | Very large plan or data files | Split data, trim the plan |
A sensible rule of thumb
Leave enough memory for the operating system and for anything else on the machine. Give the heap a generous but safe share of what remains. Then measure: run a trial at the planned load and watch memory use. A generator that sits at a moderate level for the whole run is healthy. One that climbs steadily is holding on to data it should not.
The heap is the size of the kitchen counter. Too small, and cooks keep stacking and unstacking bowls. Too large for the kitchen, and it spills into the corridor where people trip. The right counter fits the room and the number of cooks.
Watch the memory of the JMeter process during the first big test of any new plan, and write down the peak. That number tells you how many users one machine can safely carry.
14Operating System and Network Tuning for the Generator
A well-tuned machine produces trustworthy numbers.
Load generation puts pressure on the operating system. Each virtual user may hold open connections and files. The system has limits on how many it will allow, and the defaults are often too small for a heavy test. When the limits are reached, errors appear that look like server failures but are really generator failures.
Open files and sockets
Each connection counts as an open file on many systems. Raise the allowed number for the user that runs the test.
Available local ports
Every outgoing connection needs a local port. A very high request rate with short-lived connections can use them all.
CPU
Keep average CPU use of the generator moderate. If it is near the top, timings become unreliable.
Network bandwidth
The generator’s network link can become the bottleneck, especially with large replies.
Clock accuracy
For distributed runs, keep the clocks of all machines synchronised so that combined results line up.
Place the generator wisely
Put the load generator close to the system under test, in the same network zone, unless the test is about distance. A generator on a slow office connection would measure the office link. For tests that must show the user experience from different places, use generators in those places on purpose, and label the results by location.
flowchart LR
A["JMeter process"] --> B["Operating system limits"]
B --> C["Network link"]
C --> D["System under test"]
B -.->|"Too small: errors that look like server errors"| E["False failures"]
C -.->|"Too slow: delays that look like server delays"| F["False slowness"]
Testing how fast a bakery serves customers with a tester who stands in a long queue at the door would measure the queue, not the bakery. The tester must stand at the counter, with free hands and a clear path.
Reading connection errors as a server problem without checking the generator’s own limits and CPU. Always rule out the tester before blaming the tested.
15Running on Remote Machines: Sessions That Survive
A long test must not die when your connection does.
Many load generators are servers in a data centre or in the cloud. You reach them through a secure shell connection, which is a text session over the network. If the session closes, for example because your laptop sleeps or your network drops, programs started inside it may be stopped. A two-hour test that dies at minute ninety is a painful lesson.
Ways to keep the test alive
| Method | How it helps | Note |
|---|---|---|
| Terminal multiplexer | Keeps a session alive on the server so you can leave and return | Easy to reattach and watch the console |
| Background run that ignores hang-ups | The process continues after you disconnect | Send console output to a file |
| Service or scheduled job | The system starts and supervises the run | Good for regular tests |
| Pipeline agent | A build system starts the test and records the output | Best for repeatable, audited runs |
Checking on a run from outside
Once the test is in the background, you still want to know how it is going. Reading the tail of the console file, or of the results file, shows progress. A real-time dashboard fed by a backend listener, which a later chapter describes, is an even better option because it shows the trend without logging in.
Starting a long-haul truck delivery from a phone call is risky if the truck stops when the call ends. A good dispatcher gives the driver a route and lets the truck drive on its own, checking in by tracking, not by staying on the phone.
Before a long test, start a short test the same way, close your session on purpose, reconnect and confirm that the test is still running. This proves the method works before it matters.
Starting a test in the foreground of a remote session and then closing the laptop lid. The run may stop, the results file ends in the middle, and the time slot is wasted.
16Stopping a Test: Graceful and Immediate
There is a polite way to stop and a sudden way. Choose on purpose.
Sometimes a test must end before its planned time: the system under test is struggling, the data is wrong, or someone typed a wrong value. Without a window, you cannot press a stop button, so JMeter offers other ways. Each has a different effect on your results, and knowing the difference protects your data.
| Method | What happens | Results | Use when |
|---|---|---|---|
| Shutdown (graceful) | No new iterations start. Running users finish their current work | Clean, no half-finished requests | Normal early finish |
| Stop (immediate) | Threads are told to stop at once, and running requests may be interrupted | Some requests may be cut off | The system is in trouble and you need to stop quickly |
| Interrupt the process from the terminal | The shell sends a stop signal to the process | Depends on timing, and may leave the report unbuilt | Last resort for a stuck run |
| Kill the process | The operating system ends it forcibly | Results file may be incomplete | Only when nothing else works |
The control scripts
JMeter ships small scripts in its bin folder that send a shutdown or a stop message to a running non-GUI test on the same machine. The running process listens on a local port for these messages. Check the file names and the port setting for your version and platform, and test them once on a small run before you need them in an emergency.
flowchart TD
A["Need to end the test early"] --> B{"Is the system under test in danger?"}
B -->|"Yes"| C["Immediate stop"]
B -->|"No"| D["Graceful shutdown"]
D --> E["Users finish current iteration"]
C --> F["Some requests cut off"]
E --> G["Report can be built from a clean file"]
F --> G
A bus can stop at the next stop, letting passengers get off in an orderly way, or it can brake suddenly in the middle of the road. Both stop the bus. Only one keeps everyone’s luggage in place.
Treating a forced stop as a valid result. The last seconds of data may show odd errors from cut-off requests. Trim the end of the file or mark the run as incomplete before drawing conclusions.
Write the stop procedure in your run notes, including the exact script name and what to check afterwards. In a stressful moment, nobody remembers details.
17Distributed Non-GUI Execution
When one machine is not enough, several machines work together under one controller.
A single generator can only create so much load. For bigger tests, JMeter supports a distributed setup. One machine is the controller and starts the test. Several others are servers, also called workers or agents, that each run the same plan and create their share of the load. The controller collects results and builds the report.
flowchart TD
C["Controller: starts the test, collects results"] --> W1["Worker 1: runs the plan"]
C --> W2["Worker 2: runs the plan"]
C --> W3["Worker 3: runs the plan"]
W1 --> S["System under test"]
W2 --> S
W3 --> S
W1 -.->|"results"| C
W2 -.->|"results"| C
W3 -.->|"results"| C
The key facts
Each worker runs the full plan
If the plan says one hundred users, each worker runs one hundred. Three workers create three hundred users in total.
Data files are not shared
Each worker reads its own copy of any data file, so unique data must be split between workers on purpose.
Properties need care
Use the global property option so the workers receive the same settings as the controller.
The network matters
Controller and workers talk over the network. Open the needed ports, and keep them close to each other.
Security settings apply
Recent versions secure the controller and worker link by default and expect keys. Follow the documentation for your version.
When to use it
Use distributed mode when a single generator reaches its limit of CPU, memory or network before it reaches the target load. First measure one machine’s limit. Then add workers one at a time and check that total throughput grows in a straight line. If it does not, the bottleneck has moved elsewhere, perhaps to the controller, the network or the system under test.
A single person can carry only so many boxes. A moving team has a supervisor who sends several people with the same list. The total carried grows with the team, as long as the doorway is wide enough for all of them.
Forgetting that every worker multiplies the plan. A plan set for five hundred users on ten workers creates five thousand users, which may knock over an environment that was meant to take five hundred.
18Running JMeter in Containers
A container packs the tool and its settings into a portable box.
A container is a lightweight package that holds an application and everything it needs: the right Java version, the JMeter files, plugins and settings. You start it with one command, on any machine that supports containers. This solves the classic problem of “it works on my machine”, because every run uses exactly the same environment.
Benefits
- Identical environment for every run
- Quick start and quick clean-up
- Easy to scale out on a cluster
- Fits pipelines naturally
- Versions of tool and plugins are fixed
Care needed
- Results and reports must be saved outside the container
- Memory and CPU limits must be set to match the heap
- Networking between containers needs planning
- Shared data files need mounting
- Containers on busy hosts can compete for resources
What to put where
| Item | Inside the image | Mounted or passed at run time |
|---|---|---|
| JMeter and Java | Yes | No |
| Plugins | Yes, with fixed versions | No |
| Test plan | Optional | Preferred, so the plan can change without a new image |
| Data files | Small, stable files only | Large or changing files |
| Results and report | No | Always a mounted folder |
| Secrets | Never | Through the platform’s secret mechanism |
A lunch box has the meal ready, and you can eat it anywhere. If you want to keep the empty wrapper or the leftovers, you must take them out before throwing the box away. Results left inside a stopped container are easy to lose.
Setting a heap larger than the memory limit of the container. The platform may then end the process abruptly with no useful message. Keep the heap comfortably under the limit.
19Automating Tests in CI/CD Pipelines
Performance checks that run on every change catch problems while they are cheap to fix.
A pipeline is an automatic series of steps that builds, tests and delivers software. Because the non-GUI mode is a plain command, any pipeline can run it. Teams add a performance stage after functional tests. The stage starts the test, waits for the end, builds the report, checks the numbers against agreed limits and stores the results.
flowchart LR
A["Code change"] --> B["Build"]
B --> C["Unit and functional tests"]
C --> D["Deploy to test environment"]
D --> E["JMeter non-GUI run"]
E --> F["Report and checks"]
F --> G{"Within limits?"}
G -->|"Yes"| H["Promote"]
G -->|"No"| I["Fail and notify"]
Two kinds of pipeline tests
Smoke performance check
A few minutes at light load on every change. It catches big regressions such as a new slow query.
Scheduled full test
A long test at realistic load, run nightly or weekly, with results compared to earlier runs.
Make results visible
Store the plan, the command, the results file and the report as pipeline artifacts, so anyone can open them later. Show the key numbers in the pipeline summary: error rate, ninety-fifth percentile and throughput. A test that nobody can see is a test that nobody trusts.
A factory checks every car on the line for brakes and lights. A performance stage is a quick road test at the end of the line. It does not replace the full track test, but it catches the car with no brakes before it leaves the gate.
Run pipeline tests on a stable, dedicated environment. A shared environment with other activity makes results noisy, and people begin to ignore failures.
20Pass or Fail: Exit Codes and Quality Gates
A pipeline needs a clear yes or no. The tool alone does not always give one.
When a command finishes, it returns a number called the exit code. Zero normally means success. Pipelines read this number to decide whether to continue. For JMeter, a finished run usually returns zero even when many requests failed, because the run itself completed. The exit code tells you that the tool ran, not that the system performed well.
Adding your own gate
To make a pipeline fail on poor performance, add a step after the run that reads the results and compares them with limits. The limits come from agreement with the business, for example an error rate under one percent and a ninety-fifth percentile under two seconds for the main journey. If a limit is broken, the step returns a failing code and the pipeline stops.
| Gate | Typical limit | Why |
|---|---|---|
| Error rate | Under an agreed small percentage | Failures hurt users directly |
| Percentile response time | Under an agreed number for each key request | Shows the experience of slower users |
| Throughput | Above an agreed minimum | Shows capacity |
| Change from baseline | No more than an agreed worsening | Catches slow decay across releases |
| Test validity | Planned users reached, no generator overload | Makes sure the result can be trusted |
Include a validity check
A gate that only checks the server’s numbers can pass a broken test. If the generator was overloaded, or the test ended early, a green result means nothing. Add checks that the run lasted as planned, the expected number of users were active and the request count is in the expected range.
An airport security gate does not trust the fact that a passenger walked in. It checks a ticket, a passport and a scan against clear rules. A pipeline gate checks the numbers against clear limits, and refuses entry when they fail.
Assuming a green pipeline means good performance. If no gate reads the results, a test full of errors can still end with exit code zero, and the pipeline celebrates.
Example: the green run that was empty
A pipeline stage ran a test against a wrong host name. Every request failed instantly, the run finished quickly and the exit code was zero. The stage stayed green for a week until someone noticed the report showed a hundred percent errors. A simple gate on the error rate would have stopped it on day one.
21Live Monitoring with the Backend Listener
See the test in real time, from anywhere, without opening any window on the generator.
The Backend Listener is an element that sends results, in small batches, to an external system while the test runs. Common targets are time-series databases and monitoring platforms, which then draw live charts on a shared dashboard. This gives the whole team a view of the test, from any browser, without anyone logging in to the generator.
flowchart LR
A["JMeter non-GUI run"] --> B["Backend Listener sends batches"]
B --> C["Time-series store"]
C --> D["Live dashboard"]
D --> E["Team watches trends and alerts"]
A --> F["Results file kept as the permanent record"]
What a live dashboard adds
Correlation
Put response times beside server CPU, memory, database load and queue sizes on one screen. Causes become visible.
Alerts
Raise an alarm if errors cross a limit, so you can stop a bad run early.
Shared view
Developers, operations and testers watch the same numbers and talk about the same facts.
History
Past runs are stored, so comparing this release with the last one is a few clicks.
Costs and cautions
Sending results costs some CPU, memory and network. Keep the batch size and the sending interval reasonable, and avoid sending extra detail you will never chart. If the monitoring system is slow or unreachable, the listener must not slow the test. Watch for warnings in the log and keep the results file as the reliable record, because live systems may sample or drop data.
A live scoreboard in a stadium lets thousands of people follow the match without standing on the pitch. The official match record is still the referee’s report. The scoreboard is for the crowd, and the report is the proof.
Relying on the live dashboard as the only record. Network hiccups or limits can leave gaps. Always keep the results file as well.
22Which Listeners to Keep and Which to Remove
Listeners are useful in the window and costly in a real run.
A listener is an element that receives results and shows or saves them. In the window, listeners such as the results tree and the table help you debug. In a command-line run, the same listeners still collect data, even though nobody sees it, and they use memory. Before a real run, review every listener in the plan.
| Listener type | Window use | Real run |
|---|---|---|
| View Results Tree | Inspect single requests and replies | Disable or remove, very expensive |
| Table and graph results | Quick visual checks | Disable or remove |
| Simple Data Writer | Save results to a file | Keep only if you need extra files, the command already writes the main one |
| Summary report and aggregate report | Totals in the window | Disable, the console summary and dashboard replace them |
| Backend Listener | Send to monitoring | Keep, with sensible batch settings |
| Debug elements | Look at variables | Disable |
A simple way to manage this
Group view-only listeners under a clear parent or name them with a prefix such as “DEBUG”. Before each real run, disable all of them in one step. Better still, keep a separate copy of the plan for real runs where those listeners are simply absent. Either way, the rule is the same: only elements that serve the real run stay enabled.
If you need evidence of failed requests, save only the failures. Many listeners have an option to record errors only, which gives you the proof you need at a fraction of the cost.
During rehearsal, a theatre uses cameras, notes and observers in every row. During the real show, only the stage crew remains. The extra eyes were useful in practice and would only block the aisles now.
Believing that a disabled view means a free view. A listener that is enabled still collects data in memory even in a headless run. Disable it, not just close it.
23Files and Paths: Data Files, Relative Paths and Working Folders
Most “it worked in the window” problems are about where files are found.
Plans often read data files, such as lists of users or search terms. In the window, the tool finds them from the plan’s folder or from the path you typed. On another machine, in a container or inside a pipeline, the working folder may be different, and the same path may point to nothing. A missing file does not always stop the run. It may only log a warning and use empty values.
Safe habits for paths
Use relative paths
Keep data files next to the plan or in a folder beside it, and refer to them relatively, so the whole folder can be moved.
Pass folders as properties
When data lives elsewhere, pass its folder through a property, so no path is fixed inside the plan.
Check files at start
Add a small check in the pipeline that confirms the data files exist and are not empty before the run starts.
Keep line endings and encoding consistent
Files copied between systems can change line endings or character encoding, which can add invisible characters to values.
Data per worker
In distributed runs, each worker reads its own copy. If every worker has the same list of users, many virtual users will log in with the same account. Split the list so that each worker has a different part, or use a data approach that guarantees unique values. Otherwise the test may fail on duplicate logins or may touch the same records and distort results.
flowchart TD
A["Full list of 3000 users"] --> B["Part 1: 1000 users to worker 1"]
A --> C["Part 2: 1000 users to worker 2"]
A --> D["Part 3: 1000 users to worker 3"]
A team of delivery drivers, each given the full address list, would all drive to the first house. Each driver needs their own section of the list, or the first street gets a traffic jam and the last one is never visited.
Letting a run continue with a missing data file. The log shows a quiet warning, requests go out with empty values, and the whole test measures error pages.
24Debugging a Plan That Behaves Differently on the Command Line
When the window works and the command line does not, the difference is almost always in the surroundings.
The plan is the same, but the environment is not. The properties may differ, the working folder may differ, the heap may be different, the network may be different and listeners are absent. A calm checklist finds the cause faster than guessing.
| What differs | Typical symptom | How to check |
|---|---|---|
| Properties | Wrong host, wrong user count | Print used values at the start of the run and compare |
| Working folder | Data or include files not found | Look for file warnings in the log |
| Java and JMeter versions | Elements behave differently, plugins missing | Compare versions and plugin lists |
| Network location | Connection refused, timeouts, certificate errors | Test the target from the generator with a simple tool |
| Proxy and firewall rules | Requests blocked only on the server | Ask the network team, check proxy settings |
| Time zone and locale | Date-based values differ | Compare system settings |
| Missing listeners | “I cannot see the replies” | Save failures only, or run a tiny test with a tree enabled |
The two-step trick
First, run the plan on the command line with one user and one loop, on the same machine where you designed it. If it works, the plan is fine and the surroundings of the other machine are the cause. Second, run the same tiny test on the other machine. Differences between the two tiny runs point at the surroundings, and you can fix them one by one.
A recipe that works in your kitchen and fails in a friend’s kitchen has not changed. The oven, the pans and the flour are different. You compare the tools, not the recipe.
Print the key properties, the tool version and the machine name at the start of every run in the log. Months later, when a result looks odd, the log tells you exactly what was used.
Editing the plan to fix a problem that comes from the environment. The plan gets worse and the real cause stays, ready to return on the next machine.
25Keeping the Generator Honest: Accuracy and Calibration
A measuring tool must not disturb what it measures.
Every number in a report depends on the generator being healthy. If the machine is overloaded, it sends requests late and records replies late, and the recorded response time includes the generator’s own delay. This effect is called coordinated omission in its worst form, where slow periods are under-reported because blocked users send fewer requests. You cannot remove the effect completely, but you can detect and limit it.
Signs of an unhealthy generator
High CPU for long periods
If CPU stays near the top, timings are likely inflated.
Memory climbing steadily
The tool keeps data it should release, and pauses will follow.
Request rate below plan
Fewer requests than expected for the user count and think times suggest the generator, or the server, is stalling.
Network saturation
Traffic near the link’s limit means the network, not the service, shapes response times.
Calibration runs
Before trusting a setup, run the same plan against a very fast, simple target, such as a small static page, at the planned user count. If response times are very small and steady, the generator is healthy at that load. If they are already large, the generator is the limit, and no real test at that size would be reliable. Repeat after any change of machine, tool version or plan size.
flowchart TD
A["Calibration run against a fast target"] --> B{"Times small and steady?"}
B -->|"Yes"| C["Generator is healthy at this load"]
B -->|"No"| D["Reduce users per machine or add workers"]
D --> A
C --> E["Run the real test and keep watching CPU and memory"]
A kitchen scale must show zero when empty. Weighing nothing first proves the scale is honest. A calibration run is weighing nothing: it proves your tool adds no weight of its own.
Adding more virtual users to one machine until the system “breaks”, and then reporting that the service fails at that number. It may be the machine that failed. Always confirm with generator metrics before drawing conclusions about the service.
26Security and Safety of Command-Line Runs
A load test is a controlled attack on your own system. Handle it with care.
Non-GUI runs often happen on shared servers and in pipelines, where many people can read logs and files. At the same time, a load test can send enormous traffic to a system. Security here has two sides: protecting the data and credentials the test uses, and protecting the systems the test touches.
Secrets in commands
Command lines are visible in process lists and pipeline logs. Pass secrets through protected files or secret stores.
Results and logs
Files may hold tokens, user names and personal data. Limit what is saved, restrict who can read them and delete them on schedule.
Test data
Use synthetic accounts and records, never real customer data, especially in shared environments.
Permission to test
Only run load against systems you own or have written permission to test. Cloud providers and third-party services have rules about load testing.
Blast radius
A wrong host name can send load to production or to a partner’s service. Print and verify the target at the start of every run.
Remote control ports
Distributed workers listen on network ports. Keep them inside a private network and use the security features of your version.
A safety habit that pays for itself
Build a guard into the plan or the command script that checks the target host against an allowed list and refuses to start when it is not on the list. A single such guard prevents the most painful class of mistakes, the test that hits the wrong place.
A fire drill is useful because it is announced, planned and kept inside the building. A drill with real flames in the wrong building is an emergency. A guard on the target is the sign that says “drill area only”.
Copying a command from a test run to a staging or production run by changing one word and forgetting the host property. Review the target line in the log before the first minute ends.
Example: the wrong environment
A tester reused a command from last week’s run and forgot to change the environment property. For ten minutes, five hundred virtual users hit a live service used by real customers. The operations team saw the spike, the tester stopped the run, and the team added an allowed-host guard to every plan the same day.
27Design Patterns for Reliable Command-Line Testing
Habits that turn a one-off run into a repeatable practice.
Patterns are proven ways of arranging work. They save time and reduce arguments, because the team agrees on how things are done. These are the patterns that serve non-GUI testing best.
| Pattern | What it does | Why it helps |
|---|---|---|
| Parameterised plan | Users, durations and hosts come from properties | One plan serves every kind of run |
| Run script per scenario | A small script holds the full command for each scenario | Exact repeatability and clear documentation |
| Unique run folder | Each run writes into a folder named with date, time and scenario | No mixing of results, easy archive |
| Smoke before real | A tiny run proves the command and the data | Avoids wasted long slots |
| Environment guard | The run refuses to start on a target not on the list | Prevents tests against the wrong system |
| Calibrate then scale | Prove one machine, then add workers in steps | Trustworthy numbers at larger scale |
| Baseline comparison | Keep a reference run and compare each new run to it | Detects slow decay between releases |
| Results as artifacts | Plan, command, log, results and report are stored together | Any run can be reviewed or repeated later |
The run folder pattern in detail
Create a folder per run whose name tells the story: the date, the time, the scenario and perhaps the build number. Inside, keep the plan copy, the command script, the properties file, the results file, the tool log, the console output and the report folder. When someone asks about last month’s test, you open one folder and everything is there.
flowchart TD
A["Run folder named by date, scenario and build"] --> B["Plan copy"]
A --> C["Command script and properties"]
A --> D["Results file"]
A --> E["Tool log and console output"]
A --> F["HTML report folder"]
A --> G["Notes: goal, observations, decisions"]
A laboratory notebook records what was done, with which materials and what was seen. Another scientist can repeat the experiment from it. A run folder is the notebook of a performance test.
Write two lines of notes at the end of every run: what you expected and what you saw. Months later these notes are more valuable than the charts.
28Anti-Patterns: Common Mistakes and How to Fix Them
Habits that look harmless and spoil large tests.
An anti-pattern is a way of working that seems fine and keeps causing trouble. Learning to spot these is the fastest way to improve your practice. Each box names the problem, explains the harm and shows the better way.
Problem
Running the real load test from the window, with listeners open.
Why It’s Harmful
The tool spends memory and CPU on drawing and storing results, the generator becomes the bottleneck, and response times include the tool’s own delay.
Correct Approach
Design and debug in the window. Run every real test from the command line, with results written to a file.
Problem
Editing the plan file for every run to change users, durations or hosts.
Why It’s Harmful
Edits are easy to forget, impossible to review, and different copies of the plan drift apart. Nobody can tell which settings produced which result.
Correct Approach
Read settings from properties, pass them on the command line or in a property file, and store those with the run.
Problem
Reusing the same results file and report folder name for every run.
Why It’s Harmful
New data may be added to old data, or the report generation may refuse to run. Reports then mix different tests and mislead the team.
Correct Approach
Create a unique, dated run folder for every test and write the results and report inside it.
Problem
Starting a long test in a remote session without protecting it from a dropped connection.
Why It’s Harmful
When the session closes, the test may stop with an incomplete results file, and the time slot and the environment are wasted.
Correct Approach
Use a terminal multiplexer, a background run that ignores hang-ups, or a pipeline agent, and prove the method with a short run first.
Problem
Treating exit code zero as proof that the system performed well.
Why It’s Harmful
The tool returns success when it finishes, even if every request failed. Pipelines stay green while the system is broken.
Correct Approach
Add a gate that reads the results and checks error rate, percentiles, throughput and test validity against agreed limits.
Problem
Ignoring the health of the generator and trusting every number in the report.
Why It’s Harmful
An overloaded generator inflates response times and hides slow periods. The team may chase a server problem that never existed.
Correct Approach
Monitor CPU, memory and network of the generator, run calibration tests, and add workers before the machine is stressed.
Review your last three test runs against this list. Most teams find at least two of these habits, and fixing them takes less time than one confusing investigation.
29Best Practices Checklist for Non-GUI Execution
The habits of experienced performance engineers, in one list.
Best practices are not secrets. They are small habits, repeated until they feel natural. Together they make runs faster to prepare, safer to start and easier to trust.
Design in the window, run on the command line
Keep each mode for its job. Debug with one user, run real tests headless.
Parameterise everything that changes
Users, ramp-up, duration, hosts and folders come from properties, with safe small defaults.
Remove heavy listeners
Keep only what the real run needs, such as the results file and a backend listener.
Smoke test first
Run a tiny version with the real command to prove files, hosts and settings.
Use a unique run folder
Name it by date and scenario, and keep every artifact inside.
Watch the generator
Track CPU, memory and network, and calibrate before big tests.
Protect the run
Use a session that survives disconnects, and a guard on the target host.
Gate on results, not exit codes
Read the numbers and decide pass or fail from agreed limits.
Do
- Save results to a unique file
- Generate the report from the same file
- Record the command and settings with every run
- Use synthetic data and protect secrets
- Compare against a baseline
Do not
- Run big tests from the window
- Edit the plan for every run
- Leave debug listeners enabled
- Judge from averages alone
- Start a test without checking the target
A pilot’s checklist is short and the same on every flight. It does not make pilots less skilled. It frees their attention for the unusual, because the usual is already handled.
30Real-World Scenarios
Four familiar situations and how non-GUI execution supports each of them.
Nightly regression performance check
A product team runs a ten-minute test every night against the latest build. A pipeline job starts the command, writes the results to a dated folder and builds the dashboard. A gate compares the ninety-fifth percentile and the error rate with last week’s baseline. In the morning, the team sees a green or red mark and a link to the report.
Pre-launch load test for a sale
An online shop expects ten times its normal traffic. The team calibrates one generator, then scales to six workers. A backend listener feeds a live dashboard beside server metrics. Operations and developers watch together, and an alert threshold stops the run if errors pass a limit. The results guide capacity changes before the sale.
Overnight endurance test
A bank tests a service for twelve hours at moderate load to look for memory leaks. The run starts inside a terminal multiplexer on a remote machine. The console is written to a file, and a live dashboard shows memory of the service. In the morning, a slow rise in response times points to a leak the short tests never showed.
Developer self-service testing
A platform team provides a container image with JMeter and plugins, plus a run script. Any developer can run a small test of their service with one command, and the report is saved to a shared location. Performance testing becomes a normal part of development, not a special event.
| Scenario | Key technique | Main risk |
|---|---|---|
| Nightly check | Pipeline gate with baseline | Noisy shared environment |
| Pre-launch | Distributed run with live dashboard | Multiplying users by workers by mistake |
| Endurance | Protected remote session | Disk growth and lost session |
| Self-service | Container with a run script | Wrong target environment |
Whatever the scenario, start by writing one sentence about what question the test answers. Every setting, from users to duration, should serve that sentence.
31Capacity Planning for the Generator: How Many Users per Machine
Before you plan the test, plan the tester.
One of the first questions in any large test is how many virtual users a single machine can carry. There is no universal answer, because the number depends on the plan, the size of replies, the scripts used, the think times and the hardware. The honest way to find the answer is to measure it, step by step, with your own plan. This is called capacity planning for the generator.
A simple stepping method
Start small
Run with a modest number of users against a fast target and note CPU, memory and request rate.
Double in steps
Increase users in clear steps, holding each step long enough to see a stable picture.
Watch for the bend
Find the point where throughput stops growing in a straight line, or where CPU or response times start to climb.
Keep a safety margin
Choose a working number well below the bend, so normal variation does not push the machine into trouble.
| Factor | Effect on capacity |
|---|---|
| Size of replies | Bigger replies use more memory, CPU and network |
| Scripts and extractors | Heavy parsing or scripting lowers capacity |
| Secure connections | Encryption adds CPU cost, especially for new connections |
| Think times | Longer pauses mean less work per user, so more users fit |
| Saved data | Writing big results or bodies slows the run |
| Hardware and virtual machines | Shared hosts may give less real CPU than they show |
A lorry has a maximum safe load. You find it by loading it a little at a time and watching how it handles, not by reading the number on the manual and hoping. The safe load for your plan is also found by measuring, and then reduced a little for safety.
Record the working number with the plan version, the machine type and the date. When the plan changes, the number may change too, and a short re-measurement keeps the record true.
Copying a “users per machine” number from an article or another team. Their plan, their replies and their hardware are different from yours, and their number can be badly wrong for you.
32Think Time, Pacing and Realistic Load Models
Real users pause, read and decide. A test that never pauses is a different thing.
A think time is a pause between steps that imitates a person reading a page or filling a form. Without think times, each virtual user sends the next request the moment the last reply arrives, and a small number of users can create a huge request rate. That may be useful for a stress test, and it does not match normal use. How you shape the pauses decides what kind of load your test really creates.
| Technique | What it controls | Good for |
|---|---|---|
| Fixed or random think time | Pause per step for each user | Modelling human behaviour |
| Throughput control | Total requests per unit of time | Holding a target request rate |
| Pacing between iterations | Time between the start of each full journey | Keeping a steady number of journeys per hour |
| No pauses | Maximum speed per user | Stress and capacity tests |
Closed and open workloads
In a closed model, a fixed set of users loops through the journey. When the system slows down, users wait longer, and the request rate falls on its own. In an open model, new arrivals come at a set rate whatever the state of the system, like real visitors on a public website. Many tests are closed by default, because thread groups are closed. If your real traffic is open, consider arrival-rate approaches, or a throughput target, so that a slow system faces the same pressure and not less.
flowchart LR
A["Closed model: fixed users loop"] --> B["System slows down"]
B --> C["Users wait longer, request rate falls"]
D["Open model: arrivals at a set rate"] --> E["System slows down"]
E --> F["Requests keep arriving, queues grow"]
A restaurant with a fixed number of guests who each wait for their dish before ordering again is a closed system. A restaurant on a busy street, where new guests keep walking in whether or not the kitchen is slow, is an open system. The second one gets into trouble much faster.
Judging the capacity of a public service with a closed test that backs off when the server slows. The test looks calm while real traffic would have piled up. Match the model to the real pattern of arrivals.
Write down the expected requests per second for the real system, then check that your test reaches that rate in its steady phase. If it does not, your pauses or user counts are off.
33Reading and Comparing Reports Across Runs
A single report is a snapshot. Comparison turns it into knowledge.
The dashboard from one run tells you how the system behaved once. The real value comes from comparing runs: this release against the last, this configuration against that one, today against last month. To compare fairly, the runs must be alike in everything except the one thing you changed.
Make comparisons fair
Same plan and data
Use the same plan version, the same data and the same user shape in both runs.
Same environment state
Compare runs on the same kind of environment, with similar data volume and no other heavy activity.
Same measuring window
Ignore the ramp-up and the ramp-down, and compare the steady part of each run.
Repeat before concluding
Run the test more than once. A single difference may be normal variation.
What to compare
| Measure | Why it matters | Watch out for |
|---|---|---|
| Median response time | The typical experience | Hides slow outliers |
| Ninety-fifth and ninety-ninth percentiles | The experience of the slower users | Needs enough samples to be stable |
| Error rate and error types | Failures users feel directly | New error types are as important as higher counts |
| Throughput | How much work the system handles | Must be compared at the same user level |
| Resource use on the server | Efficiency and headroom | Needs monitoring on the server side |
Keep a short table of the main numbers for every run in a shared place, with the date, the build and a link to the run folder. A simple history table makes slow decay visible. A service that gets two percent slower with each release can look fine release by release and be badly slower after a year.
A child’s height marked on a door frame every birthday shows growth that nobody notices day to day. A performance history table marks the door frame of your service. A single mark means little, and a row of marks tells a story.
Comparing a run on a quiet environment with a run on a busy one and blaming the new release. Always check that the conditions match before reading the difference as a result.
34Preparing the Environment and Keeping Test Data Clean
A good run starts before the command and ends after the report.
The command is only a small part of a performance test. The environment under test must be ready, the data must be right, and the aftermath must be cleaned. Many failed tests are lost not in the tool but in the surroundings.
| Stage | What to do | Why |
|---|---|---|
| Before the run | Confirm the build version, configuration and data volume of the environment | Makes the result mean something |
| Before the run | Warm up caches and services in the way real systems would be warm | Avoids measuring only cold start behaviour, unless that is the goal |
| Before the run | Prepare unique test accounts and records | Avoids clashes between users |
| During the run | Watch the system’s own monitoring beside the test numbers | Connects cause and effect |
| After the run | Clean up created records, orders, files and queues | Keeps the next run comparable |
| After the run | Archive the run folder and remove sensitive files from shared places | Protects data and keeps evidence |
Data that grows during the test
Tests that create data, such as orders or messages, change the environment as they run. After thousands of orders, queries may become slower for reasons that have nothing to do with the new release. Plan how much data your test creates, and either clean it after each run or reset the environment to a known state. Without this step, each run starts from a different place, and comparisons lose their meaning.
flowchart LR
A["Known state"] --> B["Run test: creates data"]
B --> C["Changed state"]
C --> D["Clean up or reset"]
D --> A
A race track is raked and watered before every race so each runner starts on the same surface. If the surface changes from race to race, a faster time may only mean softer ground.
Add a cleanup journey to the plan or the pipeline that deletes test data at the end. Run it every time, even when the main test fails.
Leaving millions of test records behind in a shared environment. Other teams find strange data, queries slow down and the next tester inherits a mess.
35Troubleshooting Common Problems
A calm, step-by-step approach finds the cause of most problems in minutes.
Command-line runs fail in a small number of familiar ways. The table lists the common symptoms with their usual causes. Work from the top, and change one thing at a time.
| Symptom | Likely cause | What to check |
|---|---|---|
| Nothing happens, or the command ends at once | Wrong path to the plan, or Java not found | Print the path, confirm the file exists, check the Java installation |
| Report generation fails | Output folder already exists and is not empty | Use a new folder name for every run |
| Report is empty or odd | Results file has missing columns, or mixes several runs | Check the file header and use a fresh results file |
| All requests fail immediately | Wrong host or port, blocked network, certificate problem | Print the target and test it with a simple tool from the same machine |
| Out-of-memory error | Heap too small, or too many listeners and variables | Raise the heap, remove listeners, reduce stored data |
| Too many open files or connection errors at high load | Operating system limits reached | Raise limits for the user running the test |
| Throughput lower than the plan | Server slowing down, think times, or an overloaded generator | Compare with generator CPU and with the server metrics |
| Property value ignored | Name mismatch, or the plan does not read the property | Print used values at start and compare the spelling |
| Workers do not start | Network, ports, keys or version mismatch | Check the tool log on both sides and match versions |
| Stop script does not respond | Wrong machine, port or the test is not running there | Check the process and the port setting |
The five-question routine
flowchart TD
A["Run behaves badly"] --> B{"Did the command start the plan?"}
B -->|"No"| C["Check paths, Java, permissions"]
B -->|"Yes"| D{"Are requests reaching the right target?"}
D -->|"No"| E["Check host, port, proxy, certificates"]
D -->|"Yes"| F{"Is the generator healthy?"}
F -->|"No"| G["Fix memory, limits, CPU, add workers"]
F -->|"Yes"| H{"Are plan settings and data correct?"}
H -->|"No"| I["Fix properties and data files"]
H -->|"Yes"| J["Look at the system under test"]
Most problems end at the first or second question. A typing mistake in a path, a host name that points to the wrong place and a data file that is missing explain a large share of failed first runs. Only when these are ruled out is it worth looking at the system under test.
When a car does not start, a sensible driver checks fuel, battery and keys before calling a mechanic about the engine. The same order of checks, from simple to complex, saves time with a headless test run.
Changing three things at once and not knowing which one fixed the problem. Change one thing, rerun the tiny test and look. Then change the next.
36Pre-Flight Checklist and Long-Term Maintenance
A short routine before every run, and a longer one every few months.
Before a big test, spend ten minutes on a checklist. It is the cheapest insurance in performance testing, because it protects hours of preparation and an environment that other people are waiting for.
| Area | Question to ask | Why it matters |
|---|---|---|
| Target | Is the host and port the intended environment? | Prevents load on the wrong system |
| Plan | Are heavy listeners disabled and the correct plan version used? | Protects generator accuracy |
| Properties | Are users, ramp-up and duration as agreed? | Makes the run match the test goal |
| Data | Do data files exist, with enough unique records? | Avoids duplicate and empty values |
| Output | Is the run folder new, with enough disk space? | Avoids mixed results and full disks |
| Generator | Are heap, limits and CPU headroom ready? | Keeps timings honest |
| Monitoring | Are the live dashboard and server metrics ready? | Lets the team see what happens |
| Control | Is the session protected, and the stop method known? | Allows safe end of the run |
| People | Have owners of the system been told? | Prevents false alarms and surprises |
Smoke run
One to five users for a minute. Check that results, log and report are produced.
Calibration run
Planned users against a fast target. Confirm that the generator is healthy.
Real run
Start the test, watch the first minutes closely, and stay reachable until it ends.
Close-out
Build the report, write notes, archive the run folder and clean up test data.
Maintenance every few months
Review versions
Check the tool, Java and plugin versions, and test upgrades on a small plan first.
Refresh the baseline
After planned changes, record a new reference run so comparisons stay meaningful.
Clean old results
Delete or archive old run folders, especially those that may hold tokens or personal data.
Practice the stop procedure
Try graceful and immediate stops on a small run, so the team knows what happens.
Write the tool version and the machine details in every report. When two runs differ, the first question is what changed around them, and this record answers it.
37Frequently Asked Questions
Short, plain answers to the questions testers ask most often.
It is a way of running a saved test plan from the command line without opening the graphical window. It uses fewer resources and is meant for real load tests and automation.
The window and its listeners use memory and CPU that the load threads need. The generator becomes the bottleneck, and the measured times include the tool’s own delay.
The non-GUI flag, the test file, the results file and, for a report, the report flag with an output folder. Properties, logs and remote options are added when needed.
Read them from properties inside the plan, with safe defaults, and pass the real values on the command line or through a property file.
Add the report flag and an output folder to the run command, or build the report later from an existing results file. The output folder must be new or empty.
Use the shutdown script for a graceful end, where users finish their current work, or the stop script for an immediate end. Forcing the process to end is a last resort and may leave incomplete results.
Use distributed mode: one controller starts the test and several workers each run the full plan. Remember that users multiply by the number of workers, and that data files are not shared automatically.
Run it inside a terminal multiplexer, or in the background in a way that ignores hang-ups, or through a pipeline agent. Test the method with a short run first.
The tool usually exits with success when it finishes, whatever the error rate. Add a step that reads the results and fails the pipeline when limits are broken.
Enough for the plan and users, but not more than the machine can spare. Set minimum and maximum equal, measure a trial run, and watch for steady growth, which signals data being kept.
Yes. The console prints a summary at regular intervals, and a backend listener can send results to a monitoring system for a live dashboard. Keep the results file as the permanent record.
Monitor its CPU, memory and network, and run a calibration test against a fast target at the planned load. Small, steady times show the generator is healthy.
38Summary and Key Takeaways
The short version, for the day you only have two minutes.
Non-GUI mode is how JMeter is meant to be used for real load. The window is for building and debugging, and the command line is for running. With a small set of options, properties for every changing value, a unique run folder, a results file and a report, you get tests that are fast, repeatable and easy to automate.
Key Takeaways
- Design in the window, run on the command line — the window costs memory and CPU, and its drawing delays the very users that create your load.
- Learn the core command — the non-GUI flag, the test file, the results file and the report options cover most runs, and a small script keeps the command repeatable.
- Parameterise with properties — users, ramp-up, duration and hosts should come from the outside, with small safe defaults, so one plan serves many runs.
- Keep every run self-contained — a unique dated folder with plan, command, log, results and report lets anyone review or repeat the test later.
- Protect the run and the target — use sessions that survive disconnects, guard the host, stop gracefully when possible and never trust exit code zero as proof of good performance.
- Keep the generator honest — watch CPU, memory and network, size the heap carefully, calibrate on a fast target and scale out with workers in steps.
- Automate and guard secrets — run in pipelines with gates that read the numbers, use synthetic data, and keep tokens and credentials out of commands, logs and shared files.