Skip to main content

Multipass Local Slave Setup (Free Hands-On)

This is a free, fully local alternative to the OCI cloud setup in Section 15. Instead of paying for cloud VMs, you spin up disposable Ubuntu VMs on your own machine with Multipass (Canonical's lightweight VM manager) and use them as JMeter slaves. It's the cheapest way to practice the full distributed workflow end-to-end before you touch the cloud.

For the theory behind distributed testing, see Section 12. For the paid cloud version, see Section 15.

Why Multipass

  • Free — no cloud account, no credit card, no egress charges
  • Fast — a worker VM boots in ~30 seconds; tear it down just as fast
  • Same subnet as the host — on Windows, Multipass VMs sit on the Hyper-V Default Switch network alongside the host, so RMI works in both directions out of the box. This sidesteps the NAT callback problem that breaks cloud slaves (see Section 12 — NAT breaks distributed testing)
  • Disposable — snapshot a configured VM, clone it, or delete --purge when done

The trade-off: workers share your host's CPU and RAM, so this is for validating the setup and small loads, not generating massive load. For real scale, use cloud slaves (Section 15).


Prerequisites

  • Windows 10/11 (this guide assumes the Hyper-V backend, which Multipass uses by default on Windows)
  • Spare host resources — budget ~2 vCPUs and ~4 GB RAM per worker on top of what your host needs
  • The same JMeter version you run on your controller (this guide uses 5.6.3 to match Section 15)

Note: Multipass enables Hyper-V automatically on Windows Pro/Enterprise. On Windows Home (no Hyper-V), Multipass falls back to VirtualBox — the VM steps are identical, but the networking details in Step 3 differ.


Step 1: Install Multipass

Download the installer from multipass.run, or use winget:

winget install Canonical.Multipass

The installer enables Hyper-V and may require a reboot. Verify:

multipass version

Step 2: Launch Worker VMs

Launch one VM per slave. Give each a clear name, matching the convention from Section 15:

multipass launch 24.04 --name jmeter-slave-1 --cpus 2 --memory 4G --disk 10G
multipass launch 24.04 --name jmeter-slave-2 --cpus 2 --memory 4G --disk 10G

Check they're running and note their IPs:

multipass list
Name State IPv4 Image
jmeter-slave-1 Running 172.28.x.x Ubuntu 24.04 LTS
jmeter-slave-2 Running 172.28.y.y Ubuntu 24.04 LTS

The 172.28.x.x addresses come from the Hyper-V Default Switch. Write them down — you'll use them as the -R targets on the controller. (They can change on host reboot; see Step 3.)


Step 3: How Multipass Networking Works (Default Switch)

This is the part that makes local distributed testing just work, so it's worth understanding.

On Windows, Multipass attaches each VM to the Hyper-V Default Switch, an internal NAT network:

  • Your host gets a virtual adapter on this network — vEthernet (Default Switch), typically 172.28.x.1
  • Each VM gets an address on the same subnet172.28.x.y
  • The NAT only applies to VM → internet traffic. Host ↔ VM traffic is direct, because both sides are on the same internal subnet

That direct, same-subnet path is exactly what bidirectional RMI needs:

  1. Controller → slave (send the test plan) — host reaches the VM's 172.28.x.y directly
  2. Slave → controller (send results back) — the VM reaches the host's 172.28.x.1 directly

Contrast this with the cloud setup in Section 15, where the controller sat behind a home/office router with no address the cloud slaves could call back to. Here, host and VMs are effectively on one subnet — the ideal case from Section 12.

Find your host's Default Switch IP — you'll need it in Step 8:

ipconfig

Look for Ethernet adapter vEthernet (Default Switch) and note its IPv4 address (e.g. 172.28.x.1).

Dynamic IP caveat: Windows recreates the Default Switch subnet on every host reboot, so both the VM IPs and the host's Default Switch IP can change. After a reboot, re-run multipass list and ipconfig and update your -R target and RMI hostname accordingly. This is the one rough edge of the default networking.


Step 4: Install Java (Inside Each VM)

Open a shell into the VM (you land as the ubuntu user with passwordless sudo):

multipass shell jmeter-slave-1

Then, inside the VM, install Java 17 (match the major version you run on the controller):

sudo apt-get update
sudo apt-get install -y openjdk-17-jdk
java -version
# Expected: openjdk version "17.0.x"

Step 5: Install JMeter (Inside Each VM)

Still inside the VM, install JMeter to /opt/jmeter — same steps as Section 15:

cd /opt
sudo wget https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.3.tgz
sudo tar -xzf apache-jmeter-5.6.3.tgz
sudo ln -sfn /opt/apache-jmeter-5.6.3 /opt/jmeter
sudo rm -f apache-jmeter-5.6.3.tgz

/opt/jmeter/bin/jmeter --version
# Should show: Apache JMeter 5.6.3

Important: Use the same JMeter version on every VM and the controller. Version mismatches cause silent serialization failures in distributed mode.


Step 6: Create the Start/Stop Scripts (Inside Each VM)

These are the same scripts as Section 15, with the user path adjusted to ubuntu. HOST_IP auto-detects the VM's Default Switch address, so you don't have to hardcode it.

start-slave.sh

mkdir -p /home/ubuntu/jmeter-PT/linux
cat > /home/ubuntu/jmeter-PT/linux/start-slave.sh << 'EOF'
#!/bin/bash
JMETER_HOME="/opt/jmeter"
HOST_IP=$(hostname -I | awk '{print $1}')

export JVM_ARGS="-Xms512m -Xmx1g -XX:+UseG1GC -XX:MaxGCPauseMillis=100"

pkill -f "jmeter-server" 2>/dev/null || true
sleep 1

echo "Starting JMeter slave on ${HOST_IP}..."
nohup ${JMETER_HOME}/bin/jmeter-server \
-Djava.rmi.server.hostname=${HOST_IP} \
-Dserver.rmi.localport=50000 \
-Dserver_port=1099 \
-Dserver.rmi.ssl.disable=true \
> /home/ubuntu/jmeter-PT/linux/jmeter-slave.log 2>&1 &

echo "JMeter slave PID: $!"
EOF

chmod +x /home/ubuntu/jmeter-PT/linux/start-slave.sh

Key flags (same rationale as Section 15):

  • -Djava.rmi.server.hostname — announces the VM's Default Switch IP so the controller's callbacks land
  • -Dserver.rmi.localport=50000 / -Dserver_port=1099 — pin the RMI ports
  • -Dserver.rmi.ssl.disable=true — disable RMI SSL (no rmi_keystore.jks on internal networks)

stop-slave.sh

cat > /home/ubuntu/jmeter-PT/linux/stop-slave.sh << 'EOF'
#!/bin/bash
pkill -f "jmeter-server" 2>/dev/null && echo "Stopped." || echo "No slave process found."
EOF
chmod +x /home/ubuntu/jmeter-PT/linux/stop-slave.sh

Firewall note: Multipass Ubuntu images ship with ufw inactive, so there's nothing to open inside the VM. If you enabled ufw yourself, run sudo ufw allow 1099/tcp && sudo ufw allow 50000/tcp.


Step 7: Start the Slave

Inside each VM:

bash /home/ubuntu/jmeter-PT/linux/start-slave.sh
tail -10 /home/ubuntu/jmeter-PT/linux/jmeter-slave.log

You should see:

Created remote object: UnicastServerRef2 [liveRef: [endpoint:[172.28.x.y:50000]...]]

Type exit to leave the VM shell — the slave keeps running thanks to nohup.


Step 8: Configure the Controller (Windows Host)

The controller is your Windows host running JMeter. For the slaves to call results back, the controller must announce its Default Switch IP (from Step 3), not 127.0.0.1.

Allow Java through the Windows Firewall so the slaves' RMI callbacks reach the controller. Either approve the prompt Windows shows on first run, or pin the callback port and add a rule for it (run PowerShell as Administrator):

New-NetFirewallRule -DisplayName "JMeter RMI callback" -Direction Inbound `
-Protocol TCP -LocalPort 60000 -Action Allow

Step 9: Run the Distributed Test

From the controller (Windows host), targeting the VM IPs from multipass list:

jmeter -n -t Dummy-HTTP-Test.jmx -l results.jtl ^
-R 172.28.x.y,172.28.y.y ^
-Djava.rmi.server.hostname=172.28.x.1 ^
-Jclient.rmi.localport=60000 ^
-Jserver.rmi.ssl.disable=true
  • -R — comma-separated VM IPs (the slaves)
  • -Djava.rmi.server.hostname — the host's Default Switch IP, so slaves can send results back
  • -Jclient.rmi.localport=60000 — pin the callback port to match the firewall rule
  • -Jserver.rmi.ssl.disable=true — disable RMI SSL on the controller side too

When it finishes, confirm the JTL actually has rows (the console may still show the summary = 0 display quirk — see Section 12).


Step 10: Share Test Data with the VMs

JMeter sends the .jmx to slaves but not CSV data or JARs (see Section 12 — Files Are Not Automatically Distributed). Multipass makes this easy with a host-folder mount:

multipass mount "C:\Users\user\Documents\VTC\2026\testing-knowledge-base\jmeter-working-dir\test_data" jmeter-slave-1:/home/ubuntu/jmeter-PT/linux/test_data

Now the VM sees your test data at the same path the test plan expects. Unmount with multipass umount jmeter-slave-1.


Troubleshooting

VM IP changed after a host reboot

The Default Switch subnet is recreated on reboot. Re-run multipass list for the new VM IPs and ipconfig for the new host Default Switch IP, then restart the slave (start-slave.sh auto-detects the new VM IP) and update the -R / -Djava.rmi.server.hostname values in your run command.

Controller can't reach the slave

multipass list REM is the VM Running?
ping 172.28.x.y REM can the host reach the VM IP?

If ping works but JMeter doesn't connect, confirm the slave is listening (tail the slave log for the Created remote object line).

Test starts but JTL is empty

Almost always the result callback being blocked:

  • Confirm -Djava.rmi.server.hostname is the host's Default Switch IP (172.28.x.1), not 127.0.0.1
  • Confirm the Windows Firewall allows inbound java.exe / port 60000 (Step 8)

Version mismatch errors

Serialization errors (ClassNotFoundException, InvalidClassException) mean the VM and controller run different JMeter versions. Reinstall the matching version (Step 5).


Tips

  • Validate locally first — this is the same advice as Section 15, except here it costs nothing. Get a 2-thread / 30-second run returning a populated JTL before scaling up
  • Pause to reclaim resourcesmultipass stop jmeter-slave-1 frees the RAM/CPU without deleting the VM; multipass start brings it back
  • Snapshot a configured worker — once Java + JMeter + scripts are in place, multipass snapshot jmeter-slave-1 lets you roll back or clone instead of rebuilding
  • Automate the build — pass a --cloud-init file to multipass launch to install Java, download JMeter, and drop the scripts in on first boot, turning Steps 4–6 into one command
  • Keep JMeter versions identical across all VMs and the controller — the single most common cause of silent distributed failures
  • This is for setup validation, not scale — workers compete with your host for CPU/RAM. Once the workflow is proven here, move real load to cloud slaves (Section 15)