CLI Mode, Headless Execution, Result Files, and Reproducible Runs: Core Concepts and Mental Model
Chapter 20 made JMeter projects portable by externalizing configuration and resolving module paths deliberately. That still leaves a production question: exactly how was the plan executed? A GUI click does not preserve the full command, run directory, JMeter/Java versions, input hashes, result policy, report path, or shell exit status. Chapter 21 turns execution itself into a versionable DevOps contract so the same bounded plan can be run on a developer workstation, scheduler, or CI runner with auditable evidence.
Learning objectives
- Explain why GUI is primarily an authoring/debug surface and CLI is the load-execution surface.
-
Map JMX/properties/run parameters into one explicit
jmeter -nprocess. -
Distinguish JTL sample results,
jmeter.logengine diagnostics, console summaries, HTML reports, and run manifests. - Separate engine/process success from sample correctness and post-run performance gates.
- Inspect versions, hashes, paths, result policy, target state, and output directories before running.
- Preserve enough metadata to reproduce the same run contract from a clean shell.
1. The practical problem: “I ran the same JMX” is not reproducibility
Two engineers can load the same JMX and still run different experiments: one uses GUI, one CLI; one has 20 threads from a property file, one silently defaults to 1; one overwrites yesterday's JTL; one enables verbose logs; one runs JMeter 5.6.3/Java 17 while another has a floating installation. If only the HTML dashboard survives, the original execution cannot be reconstructed.
http://127.0.0.1:8021, synthetic requests, maximum 2
threads ×4 samples/thread, 20 ms pacing, no credentials, no plugins,
no remote engines, no containers, no paid CI, and no
public/production target. Never substitute a
public/shared/production endpoint for this localhost fixture. Never
increase load to compensate for missing execution evidence.
2. Mental model: explicit inputs → one engine process → raw artifacts → independent gate
CLI execution turns a JMeter plan into a reproducible process contract: explicit inputs enter one engine process, the process emits separate raw/log/report artifacts, and a post-run validator decides whether the observed workload is acceptable.
flowchart TD I[Versioned JMX + property files + deterministic data] --> C[Explicit jmeter -n CLI command] C --> J[JVM / JMeter engine] J --> T[Bounded thread execution] T --> S[Authorized local target] J --> R[JTL sample results] J --> L[jmeter.log engine diagnostics] J --> O[Process exit status] R --> H[Optional HTML dashboard] R --> G[Post-run sample/SLO gate] L --> G O --> G M[Run manifest: command + versions + hashes + paths] --> G S --> E[Target event telemetry] E --> G
The JMX/property/data files are immutable inputs. The launcher
constructs one exact CLI command and starts one JMeter JVM/engine.
The engine creates thread activity against the target. Raw sample
outcomes go to JTL; engine/classpath/configuration messages go to
jmeter.log; the shell receives a process exit status.
The HTML dashboard is derived evidence, not the raw source. A
post-run validator then checks sample count, failures, latency and
artifact integrity. The run manifest binds command, versions, hashes
and paths to those outputs, while target telemetry independently
confirms what work reached the service.
3. The core CLI contract
| Flag | Meaning / state boundary |
|---|---|
-n |
Run JMeter in CLI/non-GUI mode; changes engine/UI execution mode. |
-t plan.jmx |
Selects the versioned test plan input. |
-l results.jtl |
Creates the top-level sample-result listener/artifact for this run. |
-j jmeter.log |
Places JMeter engine/runtime logs in a run-specific file. |
-q local.properties |
Loads additional JMeter properties before test execution. |
-Jname=value |
Defines/overrides one local JMeter property for the process. |
-e |
Generate HTML dashboard after the load test. |
-o report-dir |
Select dashboard output directory; it must be non-existent/empty. |
-g results.jtl |
Report-only mode from an existing compatible CSV sample log. |
-Lcategory=LEVEL |
Targeted logging override for diagnostics; use sparingly under load. |
4. GUI versus CLI
Use GUI to build, inspect scope, run tiny validation/debug checks, and temporarily inspect a few samples. Current JMeter guidance explicitly says not to run load tests in GUI mode. GUI components/renderers consume generator resources and a GUI click is harder to automate faithfully.
Use CLI for meaningful load because it gives explicit files, command-line properties, predictable log/result destinations, shell exit handling and automation hooks.
5. JTL is raw sample evidence
The -l file records the top-level test results using
save-service settings. Current CSV defaults can include timestamp,
elapsed, label, response code/message, thread name, success/failure
message, bytes sent/received and active-thread counts. CSV is much
smaller than XML and cannot store response bodies, which makes it a
safer default for repeated load samples.
JTL is not just “data for the dashboard.” Your post-run gate should read it directly before derived reports are accepted.
6. jmeter.log is engine evidence
JTL answers “what happened to samples?”
jmeter.log answers “what happened inside JMeter?” It
captures plan loading, component/plugin/classpath messages, script
exceptions, remote/engine problems and other diagnostics that may
not appear as ordinary target samples.
Always use -j to put it inside the run directory. Do
not rely on whatever default jmeter.log happens to
exist in the launch directory.
7. HTML report is derived evidence
-e -o generates the dashboard after a successful load
execution path; -g results.jtl -o report can generate
it later from an existing compatible CSV log. The dashboard is
useful for analysis but should be reproducible from the retained raw
JTL/properties.
The output directory must be new/empty. A safe launcher creates the parent run directory but deliberately does not pre-create the report directory.
8. Process exit is not the performance gate
An engine/process exit code answers whether the JMeter invocation itself completed according to its process-level rules. It does not replace sample assertions, error-rate checks, expected-row counts or latency thresholds. A plan can finish as a process while some HTTP samples are unsuccessful.
Therefore the launcher records two statuses:
engine_exit_code and a separate post-run
gate_exit_code. CI should fail if either is non-zero,
while preserving which layer failed.
9. Run manifest binds inputs to outputs
A manifest should record at least:
- run ID and creation time;
- caller cwd and resolved project root;
- exact command tokens;
- JMX/property-file absolute paths and SHA-256 hashes;
- JMeter and Java version output;
- JTL/log/report paths;
- engine exit and gate exit;
- gate metrics/sample counts.
Do not put passwords/tokens into command manifests.
10. State checklist before execution
| State | Question |
|---|---|
| Generator | JMeter/Java versions, CPU/heap/network/disk, cwd/project root, free output space? |
| Thread/arrival | Resolved threads/loops/timers/duration and expected sample count? |
| Component scope | Are assertions/timers/controllers attached to the intended samplers? |
| Variables/properties/data | Which inputs are immutable files/properties versus thread-local values? |
| Protocol/session | What connection/session/correlation state will each thread create? |
| Target | Exact authorized host/port, health, baseline event/state count? |
| Artifacts | Unique run dir, JTL format, jmeter.log, report, console capture, manifest? |
| Credential/trust | Any secret in CLI/environment/property file/log? None in mandatory lab. |
| Validity | Can generator sustain configured work and does achieved sample/target count match expectation? |
11. Read-only preflight
PowerShell:
& "$env:JMETER_HOME\bin\jmeter.bat" -v
java -version
Get-FileHash .\plans\cli-local.jmx -Algorithm SHA256
Get-FileHash .\config\local.properties -Algorithm SHA256
Get-Content .\config\local.properties
Test-Path .\results\planned-run-id
curl.exe --fail --silent http://127.0.0.1:8021/health
curl.exe --fail --silent http://127.0.0.1:8021/stats
Nothing above generates load. It proves current tool/input/path/target state first.
12. Why the safe launcher does not use -f
Current JMeter exposes -f/--forceDeleteResultFile to
delete an existing result file and report folder before starting.
That is convenient for disposable automation but dangerous with a
misresolved/user-controlled path because it intentionally destroys
evidence.
This chapter instead creates a unique run directory and refuses if it already exists. Historical evidence is never deleted as part of “run again.”
13. DevOps connection
A performance test becomes automatable when the same explicit input/command/output contract works in an interactive shell and an unattended runner. The runner should not need GUI state or undocumented local files; it should only need the versioned project, pinned JMeter/Java runtime, authorized target and sufficient generator resources.
Knowledge check
Why are engine_exit_code and gate_exit_code separate?
Engine exit describes process/engine execution; the gate evaluates sample/result expectations such as failures, count, and latency.
Why keep raw JTL after generating HTML?
The dashboard is derived from JTL; retaining raw data allows later regeneration and independent validation.
Why explicitly pass -j inside each run directory?
It prevents unrelated/default logs from being mixed and preserves engine diagnostics for the exact run.
What is dangerous about -f?
It intentionally deletes existing result/report paths before execution, so a bad path can destroy evidence.
What makes a CLI run reproducible?
Explicit versioned inputs/properties, exact command, isolated outputs, versions/hashes, raw artifacts and an independent target/result verification.
Official references and version notes
-
JMeter Getting Started — CLI mode
—
-n,-t,-l,-j,-g,-e,-o,-J,-L, remote flags, and CLI/load guidance. -
JMeter Listeners / Result files
— CSV versus XML, save-service defaults, CLI
-llistener, result fields, and memory guidance. -
JMeter Generating Dashboard Report
— dashboard-required CSV fields,
-g,-e -o, output-folder rules, graphs, and report properties. - JMeter Best Practices — GUI authoring/debugging and CLI execution for load.
- Apache JMeter downloads — current stable release and Java requirement.
Version-sensitive statements were rechecked against current Apache
JMeter primary documentation on 2026-09-05. The course baseline
remains Apache JMeter 5.6.3 with a Java 17 JDK;
JMeter 5.6.3 requires Java 8+. JMeter's own manual says GUI mode
is for building/debugging while CLI mode must be used for load
testing. Current CLI flags include -n (CLI),
-t (JMX), -l (JTL), -j (run
log), -g <CSV> (report only),
-e (report after test), and -o (report
output). The report output folder must not exist or must be empty.
-f force-deletes an existing result file/report
folder before a test, so the safe launcher in this chapter
intentionally does not use it; instead it refuses to reuse an
existing run directory. CSV result files are smaller than XML and
are the normal large-run choice. Current CSV defaults include
timing/status/thread and byte fields such as
timeStamp, elapsed, label,
responseCode, success,
bytes, sentBytes,
grpThreads, and allThreads when their
save-service fields are enabled (the current defaults required by
the dashboard are documented as correct unless changed). Response
data is not supported in CSV. JMeter process/engine completion is
therefore treated separately from a post-run SLO/result gate: a
launcher first records the engine exit code, then validates JTL
sample counts/failures/latency. A zero engine exit is not used as
evidence that every sample met an SLO.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.