Load Testing VPS with JMeter

Updated on Apr 15, 2026
Mila H
8 MINS READ
Table of Contents
Load Testing VPS with JMeter

Load testing is an essential step for checking a server to determine how it can handle heavy traffic. You can use tools like Apache JMeter to simulate many users visiting a website or app at the same time and check its performance, speed, and stability. In this guide, you will learn a step-by-step instruction for load testing VPS with JMeter.

If you want to run a load test, it is recommended to use a powerful and reliable VPS. PerLod Hosting provides VPS solutions that are best for JMeter load testing.

Load Testing VPS with JMeter for Performance Optimization

With this step-by-step tutorial for load testing VPS with JMeter, you will achieve the following:

  1. Generate realistic HTTP load from a client machine, not the VPS.
  2. Collect server-side status metrics such as CPU, RAM, Disk, and Network during the test.
  3. Run JMeter headless and produce an HTML report.
  4. Parameterize tests via -J flags.
  5. Read results and spot bottlenecks.

Remember to test the systems you have exact permissions for. Let's dive into the guide steps for starting load testing on a VPS.

1. Set up Load Generator (Client Machine)

The first step is to choose your load generator. You must use a machine separate from the VPS under test.

Our client machine is Ubuntu 24.04. You must install Java on your client machine:

Bash
apt updateapt install default-jdk wget tar -y

Verify Java installation by checking its version:

Bash
java --version

Verify Java installation for JMeter setup

Then, download the latest version of JMeter on your load generator client machine. At the current time, the latest stable version of JMeter is 5.6.3. To download and install it, run the following commands:

Bash
export JMETER_VERSION=5.6.3 cd /optwget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-${JMETER_VERSION}.tgztar -xzf apache-jmeter-${JMETER_VERSION}.tgzln -sfn /opt/apache-jmeter-${JMETER_VERSION} /opt/jmeter

To verify JMeter is running correctly, run the command below:

Bash
/opt/jmeter/bin/jmeter -v

verify JMeter is running correctly

2. Prepare Target VPS for Load Testing

At this point, you must set up a few tools and some configurations on the target VPS for load testing. Our target VPS is Ubuntu 24.04.

Run the system update and install the required tools with the following command:

Bash
apt updateapt install -y htop sysstat net-tools iproute2

If sysstat is disabled on your target VPS, you must enable it with the following commands:

Bash
sed -i 's/ENABLED="false"/ENABLED="true"/' /etc/default/sysstat || truesystemctl enable --now sysstat

In separate SSH sessions, you can live-watch the following metrics during the load test.

For CPU, Memory, and Process, run:

Bash
htop

For CPU run queue, memory, IO, and context switches, execute:

Bash
iostat -xz 1

To get TCP socket summary, run:

Bash
ss -s

To check how many active HTTP/HTTPS sockets are available, run this command. You can adjust ports if needed:

Bash
watch -n1 "ss -tan | awk '\$4 ~ /:80|:443/ && \$1 ~ /ESTAB/ {c++} END{print c+0}'"

For web server logs, for example Nginx, run:

Bash
tail -F /var/log/nginx/access.logtail -F /var/log/nginx/error.log

3. Set up a Minimal and Production-Safe JMeter Test Plan

In this step, we will create a small JMX that:

  • Hits one URL with GET.
  • Uses a Thread Group with ramp-up and a time-boxed duration.
  • Uses properties so you can override host, users, ramp, and duration at runtime.

Create Test Plan File on Load Generator (Client Machine)

The first step is to create the test plan file on your client machine. Create the test directory and navigate to it with the following command:

Bash
mkdir -p ~/jmeter-tests && cd ~/jmeter-tests

Then, create the JMeter test plan file with your desired text editor:

Bash
nano vps-smoke.jmx

Add the following minimal JMX to the file:

XML
<?xml version="1.0" encoding="UTF-8"?><jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6.0">  <hashTree>    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="VPS Load Test" enabled="true">      <stringProp name="TestPlan.comments"></stringProp>      <boolProp name="TestPlan.functional_mode">false</boolProp>      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">        <collectionProp name="Arguments.arguments">          <elementProp name="protocol" elementType="Argument">            <stringProp name="Argument.name">protocol</stringProp>            <stringProp name="Argument.value">https</stringProp>            <stringProp name="Argument.metadata">=</stringProp>          </elementProp>          <elementProp name="host" elementType="Argument">            <stringProp name="Argument.name">host</stringProp>            <stringProp name="Argument.value">example.com</stringProp>            <stringProp name="Argument.metadata">=</stringProp>          </elementProp>          <elementProp name="port" elementType="Argument">            <stringProp name="Argument.name">port</stringProp>            <stringProp name="Argument.value">443</stringProp>            <stringProp name="Argument.metadata">=</stringProp>          </elementProp>          <elementProp name="path" elementType="Argument">            <stringProp name="Argument.name">path</stringProp>            <stringProp name="Argument.value">/</stringProp>            <stringProp name="Argument.metadata">=</stringProp>          </elementProp>          <elementProp name="users" elementType="Argument">            <stringProp name="Argument.name">users</stringProp>            <stringProp name="Argument.value">50</stringProp>            <stringProp name="Argument.metadata">=</stringProp>          </elementProp>          <elementProp name="rampup" elementType="Argument">            <stringProp name="Argument.name">rampup</stringProp>            <stringProp name="Argument.value">30</stringProp>            <stringProp name="Argument.metadata">=</stringProp>          </elementProp>          <elementProp name="duration" elementType="Argument">            <stringProp name="Argument.name">duration</stringProp>            <stringProp name="Argument.value">180</stringProp>            <stringProp name="Argument.metadata">=</stringProp>          </elementProp>        </collectionProp>      </elementProp>      <stringProp name="TestPlan.user_define_classpath"></stringProp>    </TestPlan>    <hashTree>      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Load" enabled="true">        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">          <boolProp name="LoopController.continue_forever">true</boolProp>          <stringProp name="LoopController.loops">-1</stringProp>        </elementProp>        <stringProp name="ThreadGroup.num_threads">${__P(users,50)}</stringProp>        <stringProp name="ThreadGroup.ramp_time">${__P(rampup,30)}</stringProp>        <boolProp name="ThreadGroup.scheduler">true</boolProp>        <stringProp name="ThreadGroup.duration">${__P(duration,180)}</stringProp>        <stringProp name="ThreadGroup.delay"></stringProp>      </ThreadGroup>      <hashTree>        <ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP Request Defaults" enabled="true">          <stringProp name="HTTPSampler.protocol">${__P(protocol,https)}</stringProp>          <stringProp name="HTTPSampler.domain">${__P(host,example.com)}</stringProp>          <stringProp name="HTTPSampler.port">${__P(port,443)}</stringProp>        </ConfigTestElement>        <hashTree/>        <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Headers" enabled="true">          <collectionProp name="HeaderManager.headers">            <elementProp name="" elementType="Header">              <stringProp name="Header.name">User-Agent</stringProp>              <stringProp name="Header.value">JMeter</stringProp>            </elementProp>            <elementProp name="" elementType="Header">              <stringProp name="Header.name">Accept</stringProp>              <stringProp name="Header.value">*/*</stringProp>            </elementProp>          </collectionProp>        </HeaderManager>        <hashTree/>        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="GET ${__P(path,/)}" enabled="true">          <boolProp name="HTTPSampler.postBodyRaw">false</boolProp>          <elementProp name="HTTPsampler.Arguments" elementType="Arguments">            <collectionProp name="Arguments.arguments"/>          </elementProp>          <stringProp name="HTTPSampler.path">${__P(path,/)}</stringProp>          <stringProp name="HTTPSampler.method">GET</stringProp>          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>          <boolProp name="HTTPSampler.monitor">false</boolProp>        </HTTPSamplerProxy>        <hashTree/>      </hashTree>    </hashTree>  </hashTree></jmeterTestPlan>

Once you are done, save and close the file.

Note: The jmeter="5.6.0" defined inside the JMX header does not have to exactly match your installed JMeter version. JMeter will load the plan without issues.

Enable File Descriptors on Load Generator (Client Machine)

You can configure the client system to handle high concurrency and more requests per second. To do this, you can increase the number of open files or sockets and expand the port range.

Bash
ulimit -n 65535

This sets the maximum number of open files and sockets to 65,535, so the system can handle more connections. If you add this setting to the /etc/security/limits.conf file, the change will stay after a reboot. Remember to re-login:

Bash
echo '* soft nofile 65535' | sudo tee -a /etc/security/limits.confecho '* hard nofile 65535' | sudo tee -a /etc/security/limits.conf

Also, you can expand the temporary port range on the client machine to handle very high requests per second:

Bash
sudo sysctl -w net.ipv4.ip_local_port_range="10240 65535"sudo sysctl -w net.ipv4.tcp_tw_reuse=1

4. Run JMeter Load Test and Generate an HTML Report

At this point, you can run the test on your load generator client machine. Remember to replace values to match your target VPS.

Bash
mkdir -p out /opt/jmeter/bin/jmeter \  -n \  -t ./vps-smoke.jmx \  -l ./out/results.jtl \  -e -o ./out/report \  -Jprotocol=https \  -Jhost=YOUR.DOMAIN.OR.IP \  -Jport=443 \  -Jpath=/ \  -Jusers=100 \  -Jrampup=60 \  -Jduration=300

Flags explanation:

  • -n = non-GUI.
  • -t = JMX file.
  • -l = results in JTL or CSV.
  • -e -o = build an HTML report in out/report.
  • -J* override the variables we put in the JMX.

During the test from your target VPS, you can live-watch the metrics we discussed in step 2.

When load testing VPS with JMeter finishes, open the HTML report with the following command:

Bash
xdg-open ./out/report/index.html 2>/dev/null || open ./out/report/index.html

In the HTML report, you must focus on:

  • Response Times Over Time
  • Throughput
  • Error %
  • Active Threads Over Time
  • Connect and Latency breakdown

And correlate with target VPS metrics.

Parameterized re-runs Test

You can also run JMeter tests with different parameters using the -J flag. It can quickly re-run tests without editing the JMX file. For example:

Bash
# Smaller smoke test (30 users, 2 mins)/opt/jmeter/bin/jmeter -n -t vps-smoke.jmx -l out/smoke.jtl -e -o out/smoke \  -Jhost=YOUR.DOMAIN -Jusers=30 -Jrampup=30 -Jduration=120 -Jpath=/ # Heavier (300 users, 10 mins)/opt/jmeter/bin/jmeter -n -t vps-smoke.jmx -l out/heavy.jtl -e -o out/heavy \  -Jhost=YOUR.DOMAIN -Jusers=300 -Jrampup=120 -Jduration=600 -Jpath=/api/health

Troubleshooting Common JMeter Load Testing Errors

  • For immediate errors like wrong URL, TLS issue, or firewall or WAF blocking, you can verify with curl -v https://YOUR.DOMAIN/.
  • To troubleshoot high error rates only at peak times, when the server is overloaded or the database is slow, you can adjust thread counts, pool sizes, and network limits.
  • For JMeter OOM or a slow client, avoid heavy listeners in the plan and increase JMeter memory.
  • For too many short connections, use Keep-Alive on client and server.
  • For handling high concurrency and more requests per second, increase the port range and reuse connections.

That's it, you are done. Just keep in mind that tests should be performed from a separate client machine, JMeter heap and client OS limits should be sufficient, and target VPS monitoring shells should be open during the test.

Conclusion

Load testing VPS with JMeter is essential for ensuring scalability, reliability, and performance under expected traffic conditions. It is a powerful tool for simulating user loads, identifying bottlenecks, and validating infrastructure improvements.

Ready to put these load-testing principles into practice? Check out PerLod VPS Plans to find the perfect host for your application.

We hope you enjoy this guide. Subscribe to our X and Facebook channels to get the latest articles.

For further reading:

Horizontal and Vertical Scaling in Dedicated Server

How to Implement a Zero-Trust Microsegmentation

Because it supports multiple protocols like HTTP, HTTPS, FTP, JDBC, etc., which allows distributed testing, and provides detailed metrics like response time, throughput, and error rates.

Key metrics include: Response Time, Throughput, Error Rate, and CPU/Memory Usage.

Yes. JMeter’s JDBC Request Sampler can simulate database queries to measure query response times and database scalability under load.