Running JMeter from CLI (Non-GUI Mode)

8 min read

Every lesson so far has used the JMeter GUI to build and debug test plans. Now that your test plan is ready, you need to put the GUI away. The GUI exists for test plan authorship. The CLI exists for test execution. Running a real load test in GUI mode produces inaccurate results and risks crashing the JVM. This is not a preference — it is a hard constraint.

GUI mode vs CLI mode

GUI mode vs non-GUI (CLI) mode

GUI Mode

  • Use for: designing, debugging, verifying

  • Swing UI renders every result in real time

  • All listeners active — heavy memory use

  • JVM thread overhead from UI event loop

  • Throughput numbers are 20–50% lower than CLI

  • Run: jmeter (launches the GUI)

CLI (Non-GUI) Mode

  • Use for: all actual load test execution

  • No UI rendering — zero Swing overhead

  • GUI listeners disabled — only data writers run

  • Full JVM heap available for test threads

  • Accurate throughput — what the server actually sees

  • Run: jmeter -n -t test.jmx

The core command

jmeter -n -t test.jmx -l results.jtl

Three flags are mandatory for every load test run:

  • -n — activates non-GUI mode. Without this flag, JMeter launches the GUI.
  • -t <filename> — the test plan file to run. Relative or absolute path.
  • -l <filename> — the results log file. JMeter writes every sample result here as a CSV row. Without this, results are not persisted.

Run this command. Watch the terminal. When it finishes, you have a results.jtl file containing every sample from the test.

Generating the HTML dashboard

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

Adding -e tells JMeter to generate the HTML dashboard after the test completes. Adding -o ./report/ specifies the output directory.

The HTML dashboard is the standard deliverable from a JMeter load test — a shareable, self-contained HTML report with response time charts, throughput graphs, APDEX scores, and per-sampler percentile tables. Open ./report/index.html in a browser.

Important: the -o directory must not already exist (or must be empty). JMeter refuses to overwrite an existing report directory. Use timestamped directory names in scripts:

TIMESTAMP=$(date +%Y%m%d-%H%M%S)
jmeter -n -t test.jmx \
  -l results-${TIMESTAMP}.jtl \
  -e -o report-${TIMESTAMP}/

Generating a report from an existing JTL file

You do not need to re-run the test to regenerate the HTML dashboard. If you already have a .jtl file, generate the report from it:

jmeter -g results.jtl -o ./report/

This is useful when you ran the test in CI with -e -o and want to regenerate the report with different settings, or when the original report directory was accidentally overwritten.

All useful CLI flags

FlagPurposeExample
-nNon-GUI moderequired
-tTest plan file-t test.jmx
-lResults log file-l results.jtl
-eGenerate HTML dashboard after test
-oHTML dashboard output directory-o ./report/
-gGenerate HTML from existing JTL-g results.jtl
-JSet a JMeter property-JbaseUrl=https://test.com
-GSet property on all remote hosts-Gvusers=100
-pLoad a properties file-p staging.properties
-qAdditional properties file (appends)-q user-overrides.properties
-rRun distributed test on remote hosts
-RSpecify remote hosts inline-R 10.0.0.1,10.0.0.2
-XExit after distributed test completeswith -r

What CLI output looks like

As the test runs, JMeter prints summary lines to the terminal:

summary +    312 in 00:00:30 =   10.4/s Avg:   287 Min:    80 Max:  1850 Err:     2 (0.64%)
summary +    310 in 00:00:30 =   10.3/s Avg:   301 Min:    75 Max:  2100 Err:     1 (0.32%)
summary =    622 in 00:01:00 =   10.4/s Avg:   294 Min:    75 Max:  2100 Err:     3 (0.48%)

Lines prefixed summary + are interim summaries for the last interval (default: 30 seconds). Lines prefixed summary = are cumulative totals for the whole test so far. The final summary = line is the overall test result.

Columns: total samples | elapsed time | requests/second | average response time (ms) | minimum | maximum | error count (error percentage).

Change the summary interval: add summariser.interval=10 to user.properties for 10-second summaries instead of 30.

JVM heap for CLI runs

The same heap settings apply in CLI mode as in GUI mode. Set them before running large tests:

# On macOS/Linux — set in env or edit bin/jmeter
HEAP="-Xms2g -Xmx4g" jmeter -n -t test.jmx -l results.jtl
 
# Or export for the session
export JVM_ARGS="-Xms2g -Xmx4g"
jmeter -n -t test.jmx -l results.jtl

For very large tests (1000+ threads, long durations), 4–8GB is appropriate. Monitor java process memory during the test — if the process grows consistently toward the heap limit, increase it or reduce listener activity.

Checking exit codes for CI/CD

JMeter exits with code 0 on completion — even if 50% of requests failed. For CI/CD pipeline integration, you need to check the actual error rate after the test:

#!/bin/bash
jmeter -n -t test.jmx -l results.jtl -e -o ./report/
 
# Count errors in JTL (column 8 is "success" — false = error)
TOTAL=$(awk -F',' 'NR>1 {count++} END {print count}' results.jtl)
ERRORS=$(awk -F',' 'NR>1 && $8=="false" {count++} END {print count+0}' results.jtl)
ERROR_RATE=$(echo "scale=2; $ERRORS * 100 / $TOTAL" | bc)
 
echo "Total: $TOTAL | Errors: $ERRORS | Error rate: ${ERROR_RATE}%"
 
if (( $(echo "$ERROR_RATE > 1.0" | bc -l) )); then
    echo "ERROR: error rate ${ERROR_RATE}% exceeds 1% threshold"
    exit 1
fi

This pattern is what makes JMeter useful in CI/CD — the pipeline fails the build if the error rate or response times exceed thresholds.

⚠️ Common mistakes

  • Running load tests in GUI mode. JMeter itself warns you when you click Run in GUI mode: "You are about to run a test with the GUI. Running tests with the GUI is not recommended for load tests." That warning exists for a reason. Throughput numbers from GUI mode runs are unreliable — typically 20–50% below CLI mode numbers.
  • Forgetting -l results.jtl and losing all test data. Without a results file, the test runs and produces no persistent output. If you forget to add Simple Data Writer to the test plan and forget -l on the CLI, the test results exist only in memory and disappear when JMeter exits. Always specify -l.
  • Running the test again without deleting the old report directory. jmeter -e -o ./report/ fails immediately with an error if ./report/ exists and is not empty. Script your runs to use timestamped directories, or explicitly delete the old report before each run: rm -rf ./report/ && jmeter -n -t test.jmx -l results.jtl -e -o ./report/.

🎯 Practice task

Run your full test from CLI and generate a report.

  1. Save your test plan. Open a terminal in the directory containing the .jmx file.
  2. Run: jmeter -n -t test.jmx -l results.jtl. Watch the summary lines print. Wait for it to complete.
  3. Confirm results.jtl exists and has content: wc -l results.jtl (should be 1 header row + one row per sample).
  4. Generate the HTML report: jmeter -g results.jtl -o ./report/. Open ./report/index.html.
  5. In the HTML report, find: (a) the APDEX score, (b) the 90th percentile response time for your main sampler, (c) the overall error rate.
  6. Run again with an increased user count: jmeter -n -t test.jmx -Jvusers=20 -Jduration=60 -l results-20u.jtl -e -o ./report-20u/. Compare the 90th percentile to the single-user run.

// tip to track lessons you complete and pick up where you left off across devices.