Distributed Testing — Master and Slaves

9 min read

A single JMeter process running on one machine can generate substantial load — but it has a ceiling. When your thread count, request rate, or test duration pushes past what one JVM can sustain, the load generator itself becomes the bottleneck. Distributed testing is the mechanism for spreading load generation across multiple machines while aggregating all results in one place.

The architecture

JMeter distributed testing uses a controller/worker model. The terminology in the documentation is "master" and "slaves" — or equivalently "controller" and "servers."

Distributed Testing
  • – Runs JMeter GUI or CLI
  • – Distributes test plan to all workers
  • – Sends start / stop signals
  • – Aggregates all worker results
  • – Writes single combined .jtl file
  • – Run jmeter-server binary
  • – Execute the full Thread Group independently
  • – Listen on RMI port 1099
  • – Send results back to master in real time
  • – Each worker multiplies the thread count
  • – The application under test
  • – Receives load from all workers simultaneously
  • – Must be accessible from all worker IPs
  • Same JMeter version on master and workers –
  • Test files on each worker (or via NFS) –
  • Port 1099 open between master and workers –
  • Thread count multiplies per worker –

How thread counts multiply

This is the most important distributed testing concept to internalise: each worker executes the full Thread Group independently.

If your test plan has one Thread Group configured for 100 threads, and you run the test on 3 workers, the total virtual user count is 300 — not 100 shared across three machines. The master distributes the test plan to each worker, and each worker runs 100 threads.

Design thread counts for distributed mode:

  • Target total VUs: 1,000
  • Number of workers: 5
  • Threads per worker: 200

Set the Thread Group to 200 threads, run on 5 workers, achieve 1,000 total VUs.

Setting up a worker

On each machine designated as a worker:

  1. Install the same JMeter version as the master
  2. Copy bin/jmeter.properties from the master if you have custom settings
  3. Configure the RMI server port (avoids random port allocation on restart):
# In bin/jmeter.properties on each worker
server.rmi.localport=1099
server_port=1099
  1. Set the hostname to the worker's IP (critical for RMI callbacks to reach the right address):
# Start the worker — explicitly bind to its own IP
bin/jmeter-server -Djava.rmi.server.hostname=10.0.0.2

On Linux/macOS, jmeter-server is a shell script in the bin/ directory. On Windows, it is jmeter-server.bat.

The worker is now listening. It waits for a master to connect and send a test plan.

Configuring the master

On the master machine, edit bin/jmeter.properties:

# Comma-separated list of worker IPs (or hostnames)
remote_hosts=10.0.0.2,10.0.0.3,10.0.0.4

Alternatively, specify workers inline at run time without modifying the properties file:

jmeter -n -t test.jmx -l results.jtl -R 10.0.0.2,10.0.0.3,10.0.0.4

Running the distributed test

# Run on all workers configured in remote_hosts
jmeter -n -t test.jmx -l results.jtl -r
 
# Run on specific workers (overrides remote_hosts)
jmeter -n -t test.jmx -l results.jtl -R 10.0.0.2,10.0.0.3
 
# Stop all workers after test completes (exit cleanly)
jmeter -n -t test.jmx -l results.jtl -r -X

JMeter sends the test plan to each worker, starts the test simultaneously across all workers, streams sample results back to the master in real time, and writes all results to a single results.jtl file. When the test completes, the master sends a stop signal to all workers.

Distributing test plan files

The workers execute the test plan locally — they need access to all files the test plan references. CSV data files, JDBC driver JARs, and any files read by JSR223 scripts must be present on each worker at the same path.

Two common approaches:

Copy files to each worker before the test:

# From the master, rsync test data to each worker
rsync -av data/ user@10.0.0.2:~/jmeter-data/
rsync -av data/ user@10.0.0.3:~/jmeter-data/

Use a shared network filesystem (NFS): Mount the test plan directory from a shared NFS volume on all workers. All machines see the same files without synchronisation overhead.

Passing properties to workers

The -J flag sets a property on the master only. For distributed tests, use -G to set a property on all workers:

jmeter -n -t test.jmx -l results.jtl -r \
  -GbaseUrl=https://staging.example.com \
  -GthinkTimeMs=2000 \
  -X

Properties set with -G are sent to every worker when the test starts. Without -G, workers use whatever is in their local jmeter.properties — which may be different from the master's configuration.

Firewall and network requirements

JMeter uses Java RMI for master-worker communication. RMI requires:

  • Workers listening on port 1099 (the RMI registry port)
  • The master must be able to reach workers on port 1099
  • Workers must be able to reach the master on a callback port (configure with client.rmi.localport=2099 on the master to fix this port rather than using a random one)

In cloud environments, configure security groups or firewall rules to allow TCP traffic on these ports between master and worker instances.

When distributed JMeter is the right call

Distributed JMeter adds significant operational complexity — provisioning machines, keeping versions in sync, managing CSV data distribution, configuring firewalls. Consider alternatives before committing to it:

SituationRecommendation
Need > 1,000 VUs, team owns infrastructureDistributed JMeter on VMs
Need > 1,000 VUs, cloud-native teamK6 Cloud, BlazeMeter, or OctoPerf
Need 200–500 VUs from a single machineTune heap and reduce listeners
Testing non-HTTP protocols (JDBC, JMS)Distributed JMeter — cloud tools lack this
One-off load test, fast setup neededManaged service with JMeter UI (BlazeMeter)

⚠️ Common mistakes

  • Running different JMeter versions on master and workers. JMeter uses Java serialisation to send the test plan between master and workers. Version mismatches cause serialisation errors (ClassNotFoundException, InvalidClassException) that manifest only at test start, after provisioning is complete. Pin the JMeter version across all machines and verify with jmeter --version on each.
  • Forgetting that thread count multiplies per worker. Configuring 500 threads in the Thread Group and launching on 4 workers produces 2,000 threads, not 500. This is the most common distributed configuration mistake — it either overloads the target system or produces test results for a much higher load than intended.
  • Not setting -Djava.rmi.server.hostname on workers. Without this, the worker registers its own hostname with the RMI registry. If the hostname resolves differently from the master's network (common in cloud environments where internal DNS differs from external), the master connects to port 1099 but the RMI callback to the worker fails. Always bind explicitly to the worker's IP that the master can reach.

🎯 Practice task

Simulate a distributed test setup — even without multiple machines.

  1. On your local machine, start JMeter Server in one terminal: bin/jmeter-server -Djava.rmi.server.hostname=127.0.0.1. Confirm it prints "Created remote object: rmi://127.0.0.1:1099/JMeterEngine".

  2. In bin/jmeter.properties, set remote_hosts=127.0.0.1 (pointing to yourself).

  3. In a second terminal, run the distributed test: jmeter -n -t test.jmx -l results-distributed.jtl -r -X.

  4. Confirm the test runs via the server process (you should see log output in the first terminal). Check results-distributed.jtl — it should contain results from the remote execution.

  5. Change the Thread Group to 5 threads. Run distributed. Confirm the results show 5 threads worth of samples — not 10 (since there is only one worker, multiplication factor is 1 × 5 = 5).

This local simulation validates the master-worker communication path before moving to real multi-machine environments.

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