Teams usually start thinking about performance when a big launch, campaign or seasonal peak appears on the roadmap. At that point, the key question is simple: can our current hosting handle the expected traffic, and what breaks first if it cannot? The most reliable way to answer this is to run structured load tests against your site or API before the traffic spike happens. In this article, we will walk through a practical process for testing realistic workloads using three popular open-source tools: k6, Apache JMeter and Locust.
We will focus on how to design realistic scenarios, how to prepare your hosting environment, and how to interpret the results so you can make concrete decisions: scale up your VPS, introduce a cache layer, change PHP-FPM settings, or move to a dedicated server. Whether you run WordPress, Laravel, Node.js or a custom stack, the same principles apply. By the end, you will have a reusable blueprint to adapt to your hosting environment, subject to its testing permissions, resource limits and monitoring access.
Why Load Testing Your Hosting Before Traffic Spikes Matters
Load testing is not about achieving a perfect benchmark score; it is about reducing uncertainty. Before a campaign goes live, you want to know:
- How many concurrent users or requests per second your hosting can serve with acceptable response times
- Where the first bottleneck appears: CPU, RAM, disk I/O, database, PHP workers, or external API calls
- How your application behaves when limits are reached: graceful degradation vs. 500 errors and timeouts
We covered capacity planning from a hosting perspective in detail in our hosting scaling checklist for traffic spikes and big campaigns. Load testing is the hands-on counterpart of that planning work: instead of only estimating, you simulate the coming traffic as closely as possible.
It helps to distinguish a few test types:
- Load test: Gradually increase traffic up to an expected peak (for example, 200 logged-in users) and observe metrics.
- Stress test: Push the system beyond its expected peak to see how it fails and how it recovers.
- Soak (endurance) test: Keep a realistic constant load for hours to uncover memory leaks, connection leaks or slow bloat.
For most websites and SaaS apps preparing for a spike, you will primarily run a load test plus a short stress test, and optionally a soak test if you suspect memory issues.
Planning a Realistic Load Test Scenario
The quality of your load test depends more on your scenario design than on the tool. Before opening k6, JMeter or Locust, answer these questions.
1. Define clear performance goals
Start with a small set of measurable targets such as:
- Target concurrency: e.g. 150 concurrent users on the store during campaign peak
- Latency budget: e.g. 95% of requests < 800 ms; 99% < 1.5 s
- Error budget: e.g. HTTP 5xx < 0.5% and timeouts < 1%
Align these goals with your business and SEO expectations. For example, our article on how Core Web Vitals relate to hosting explains how server response times can affect LCP. These load-test latency targets are not themselves Core Web Vitals measurements or guarantees of search ranking.
2. Estimate traffic and concurrency
If you do not have historical data, you can still create reasonable estimates. We recommend using the approach described in our guide to estimating traffic and bandwidth on shared hosting and VPS. In simplified form:
- Estimate total daily sessions during the spike (for example, from a campaign forecast)
- Identify the busiest hour and estimate its share of daily sessions from analytics or an explicit campaign assumption
- Estimate requests per second (RPS) as sessions per second × pageviews per session × requests per pageview at the layer being tested. Estimate average concurrent sessions as sessions started per second × average session duration in seconds; pageviews per session alone do not determine concurrency
For instance, if you expect 10,000 sessions on the busiest day, assume 25% occur in the busiest hour (2,500), and estimate about 4 pageviews per session, you get roughly 10,000 pageviews in that hour, which is about 2.8 pageviews per second. This is an hourly average, not the peak within that hour. Include API and static-asset requests in the request mix, then account for which requests reach the origin versus being served from browser or CDN caches; cache misses are not an additional request category.
3. Model user behaviour, not just single URLs
Load tests that hit a single URL (like the home page) are easy to run but often misleading. Real users:
- Navigate between multiple pages
- Perform actions like search, add-to-cart, login, or checkout
- Sometimes make invalid requests or trigger edge cases
Try to model a few key user journeys with approximate probabilities, such as:
- 50% browse catalog only
- 30% search + view product detail
- 15% add to cart but do not complete checkout
- 5% complete checkout
Tools like JMeter and Locust are particularly good at modelling such flows; k6 can also express them through JavaScript functions and scenarios.
4. Choose the right environment
Whenever possible, run heavy tests against a staging or pre-production environment that is:
- On the same hosting type and similar specs as production (same dchost.com VPS size, same PHP/MySQL versions)
- Using representative synthetic data or a sanitised database clone with personal data and production credentials removed
- Behind equivalent proxies, WAF, CDN and caching rules without sharing production cache namespaces
- Using test accounts and sandboxed or stubbed payment, email and fulfilment integrations so checkout tests cannot charge customers, send real messages or create shipments
Before any test, obtain permission for the target systems and confirm the hosting provider’s load-testing policy, especially on shared infrastructure. Keep third-party endpoints out of the generated load unless explicitly authorised. If you must test against production, use off-peak hours, start at low intensity, agree on load ceilings and stop thresholds for latency, errors and resource usage, and have an operator ready to stop the test. Communicate with stakeholders in advance and isolate test transactions from real orders and notifications. Also check your CDN and caching strategy first, so you do not unintentionally DOS your own origin with fully uncached traffic.
Preparing Your Hosting and Observability Stack
Load test results are easier to diagnose if you can see what is happening inside the server. Before starting k6, JMeter or Locust, prepare your monitoring and logging for both the target system and every load generator. On the generators, track CPU usage per core or process, RAM and swap, network throughput and errors, and tool warnings throughout the run. A separate generator can still reach its own resource limits and constrain the load it sends.
1. Baseline monitoring on the VPS or server
At a minimum, you should watch:
- CPU usage and steal time
- RAM usage and swap
- Disk I/O wait and throughput
- Network throughput and errors
- Database CPU, slow queries and locks
We showed how to set this up in detail in our guide to monitoring VPS resources with htop, iotop, Netdata and Prometheus. Even a quick Netdata dashboard or a Prometheus + Grafana setup will make your load tests much more informative.
2. Application and web server logs
Enable and monitor the following logs with access controls and rotation. Keep debug output out of public responses, avoid recording credentials or sensitive payloads, and account for logging overhead when comparing test runs:
- Web server access/error logs (Nginx or Apache)
- Application logs (Laravel, WordPress debug logs, Node.js logs, etc.)
- Database slow query logs (MySQL, MariaDB or PostgreSQL)
During the test, watch for spikes in 4xx/5xx, upstream timeouts, or slow queries. Our article on reading web server logs to diagnose 4xx–5xx errors is a useful companion while you analyse your test runs.
3. Align infrastructure with realistic settings
Before you test, configure your stack to match how you intend to run in production:
- Set reasonable
pm.max_childrenand related PHP-FPM settings (see the DCHost blog for related hosting guides) - Ensure your object cache (Redis/Memcached) is enabled if you plan to use it
- Enable CDN or reverse proxy caching if it will be active in the real event
- Disable heavy background jobs or backups only if they will also be paused during the real spike; otherwise include their expected resource usage in the test
The goal is not to cheat, but to reflect the realistic production architecture you will rely on during the spike.
Load Testing with k6: Modern, Scriptable and CI-Friendly
k6 is a modern load testing tool that uses JavaScript for scripting and is very comfortable for developers. It is ideal for HTTP APIs, microservices and web apps where you want tests that fit nicely into your CI/CD pipeline.
1. Installing k6
Install k6 on the load generator using the method for your operating system in the official Grafana k6 installation guide. If using the standalone download, extract the archive for your platform and place the k6 executable in PATH so it is available from your terminal. Run k6 from a separate server, not from the same VPS that hosts your site, so load generation does not compete with the application for resources.
2. Writing a basic k6 script
Here is a minimal k6 script that hits your home page and checks that it returns HTTP 200 status:
import http from 'k6/http';
import { check, sleep } from 'k6';
export let options = {
vus: 50, // virtual users
duration: '2m', // test duration
thresholds: {
http_req_duration: ['p(95)<800'], // 95% of requests < 800 ms
http_req_failed: ['rate<0.01'], // < 1% failed requests
},
};
export default function () {
let res = http.get('https://example.yourdomain.com/');
check(res, {
'status is 200': (r) => r.status === 200,
});
sleep(1);
}
This is a fixed 50-user load test. For an initial smoke test, use only a few users. To simulate a ramp-up, replace the vus and duration entries in the existing options with the stages array below, retaining the thresholds:
export let options = {
stages: [
{ duration: '2m', target: 50 }, // ramp up to 50 VUs
{ duration: '5m', target: 50 }, // stay at 50 VUs
{ duration: '2m', target: 100 }, // ramp up to 100 VUs
{ duration: '5m', target: 100 }, // stay at 100 VUs
{ duration: '2m', target: 0 }, // ramp down
],
};
3. Modelling user journeys in k6
For a fixed browsing journey, replace the earlier default function with the following, retaining the imports and options. This example visits the same three URLs in order, without randomisation:
export default function () {
// Visit home page
http.get('https://example.yourdomain.com/');
sleep(1);
// Browse a category
http.get('https://example.yourdomain.com/category/shoes');
sleep(1);
// View a product
http.get('https://example.yourdomain.com/product/sneaker-123');
sleep(1);
}
You can also parameterise URLs, logins and payloads from CSV/JSON files and add checks for specific HTML elements or JSON fields. k6’s thresholds feature is particularly useful for enforcing your performance budgets during CI: configure CI to block merges when the load test fails its latency budget.
Load Testing with Apache JMeter: GUI and Protocol Flexibility
Apache JMeter is one of the oldest and most flexible load testing tools. It supports many protocols in addition to HTTP: JDBC, FTP, SMTP and more. For hosting-related scenarios, it is especially useful when you need:
- Complex multi-step web flows with logins, cookies and CSRF tokens
- Testing backend services like database queries or message queues (with care)
- Reusable test plans maintained by QA engineers through a GUI
1. Creating a basic HTTP test plan
Prerequisite: Install JMeter and a maintained Java runtime supported by your selected JMeter release before creating the test plan. Follow the official JMeter getting-started guide for release requirements and installation steps. The typical JMeter workflow for a website or API is:
- Create a Test Plan and add a Thread Group (this defines concurrent users and ramp-up time).
- Add one or more HTTP Request samplers for each step in your user journey.
- Add Config Elements such as HTTP Header Manager (for user agents, auth tokens) and Cookie Manager.
- Add Timers to model pauses between actions. Timers run before each sampler in their scope; attach a timer as a child of one sampler when the pause should apply only there.
- Add Listeners like Summary Report, Aggregate Report and Graph Results to inspect metrics while developing the plan.
- Set the number of threads (users), ramp-up period and loop count.
JMeter’s GUI is helpful for designing the scenario. For actual high-load runs, save the plan and run it in non-GUI (CLI) mode from a separate VPS to avoid overloading your workstation.
2. Correlation and parameterisation
Realistic load tests often need to capture tokens from responses (like CSRF or session IDs) and reuse them in subsequent requests. JMeter supports this through Post-Processors (like Regular Expression Extractor or JSON Extractor) and Variables. For example, you can:
- Send a login request and extract a
tokenfield from JSON - Store it in a JMeter variable
- Use that variable in the Authorization header for the next requests
This makes JMeter powerful for simulating authenticated user flows, admin panels or API clients.
Load Testing with Locust: Pythonic User Behaviour
Locust is a Python-based load testing framework that describes user behaviour as Python classes. It is a suitable option when your team prefers code-based scenarios to GUI-maintained test plans.
1. Basic Locustfile example
A minimal locustfile.py for a browsing scenario could look like this:
from locust import HttpUser, task, between
class WebsiteUser(HttpUser):
wait_time = between(1, 3)
@task(3)
def browse_home(self):
self.client.get('/')
@task(2)
def browse_category(self):
self.client.get('/category/shoes')
@task(1)
def view_product(self):
self.client.get('/product/sneaker-123')
Here, tasks have different weights (3:2:1), modelling their relative selection probabilities rather than a fixed browsing sequence. The wait_time function defines how long a simulated user waits between tasks. For a fixed number of users, these pauses affect request rate and requests in flight, not the configured user count.
2. Running Locust
Prerequisite: Install a Python version supported by your selected Locust release, then create and activate an isolated project virtual environment. Follow the official Locust installation guide, install with pip install locust in that environment, and validate with locust -V. Keep the environment active for the command below. Restrict access to the web UI before starting Locust. The UI binds to all interfaces by default; the command below does not configure authentication. Before using it on a VPS, apply and verify firewall rules that allow the UI port (8089 by default) only from trusted operator addresses and deny other access, covering both IPv4 and IPv6 where enabled. Do not proceed while the controls are publicly reachable. With those restrictions in place, run:
locust -f locustfile.pyLocust starts a web UI (by default on port 8089) where you can configure:
- Number of users to simulate
- Spawn rate (users per second)
- Target host (for example, your staging URL)
You can also run Locust in headless mode for automated runs and in a distributed mode with multiple worker processes across several VPS instances when you need to generate very high loads.
Interpreting Results and Turning Them Into Hosting Actions
After running your tests, you end up with response times, percentiles, error rates and throughput. Validate the generated load before diagnosing hosting capacity: compare actual active users, achieved RPS and the duration at each load level with your intended profile. A configured user count does not guarantee a fixed RPS; response times and pauses between actions also affect throughput. Review generator CPU, memory and network metrics alongside tool warnings, including Locust CPU warnings. If the generator is constrained or the intended load was not achieved, investigate the generator and workload pacing, then rerun within the agreed limits. Do not treat a throughput plateau alone as evidence that the target needs an upgrade. Once the run is valid, connect its results with server-side measurements to choose hosting or configuration changes.
1. Key metrics from k6, JMeter and Locust
Across all three tools, watch for:
- Throughput: requests per second (RPS) or transactions per second
- Latency: average, median, p90, p95, p99 response times
- Error rate: HTTP 4xx/5xx, timeouts, connection errors
- Concurrency: track simulated users and requests in flight separately; a user waiting between actions is not an in-flight request
Overlay these with your server metrics (CPU, RAM, I/O, DB load). For example:
- If CPU hits 100% while RPS stalls and latency spikes, this suggests a CPU bottleneck; check which processes consume CPU.
- If CPU is moderate but I/O wait is high, especially on HDD or slow SSD, investigate a storage I/O bottleneck; confirm with storage latency and queue metrics.
- If MySQL slow queries appear, investigate your database and queries; slow-query entries alone do not prove they limit throughput.
2. Common bottlenecks and fixes on hosting
Use the following symptoms as starting points for investigation, then choose actions based on the confirmed bottleneck:
- PHP-FPM worker exhaustion: Many pending PHP requests and long queues; CPU may be high, or workers may be waiting on other services.
- Action: Review
pm.max_children,pm.start_serversand related values for your process-manager mode. Increase worker capacity only within available memory and CPU headroom; investigate slow requests before upgrading to more vCPU.
- Action: Review
- Database saturation: High CPU in MySQL/PostgreSQL and slow queries warrant investigation.
- Optimise indices and queries; our guide on MySQL indexing for WooCommerce can help.
- Consider moving the database to a separate VPS or a higher-tier plan if measurements confirm that database CPU capacity remains the limiting factor after tuning.
- Disk I/O bottlenecks: High I/O wait and slow writes, especially when logs or backups run during peaks, warrant checking storage latency and queues.
- Consider faster storage (for example, an NVMe-based VPS or dedicated server) if the available storage performance addresses the confirmed bottleneck.
- Optimise log rotation and background jobs so they do not coincide with spikes where operationally feasible; reproduce that schedule in follow-up tests.
- Network or reverse proxy limits: High connection counts and timeouts at the proxy rather than the app warrant investigation.
- Check network throughput, errors and connection limits to distinguish a network constraint from a proxy configuration limit.
- Tune Nginx worker connections, timeouts and buffering where measurements identify those limits.
- Use microcaching or full-page caching for cache-safe public responses to reduce dynamic hits; bypass shared caching for personalised or authenticated responses.
When measurements show that you need more resources rather than configuration changes, compare larger VPS plans for CPU/RAM, dedicated servers for sustained high workloads, or colocation if you operate your own hardware. Verify that the chosen plan addresses the measured bottleneck.
3. Validate improvements with follow-up tests
Each change you make—whether it is increasing PHP workers, adding Redis, or tuning MySQL—should be followed by a comparable rerun of your load test using the same workload, load profile and duration. Compare:
- Before vs. after latency percentiles
- Before vs. after CPU/RAM/I/O usage
- Error rates and timeouts
This iterative approach turns load testing into a feedback loop rather than a one-off exercise.
A Reusable Step-by-Step Blueprint for Load Testing Your Hosting
To make this practical, here is a blueprint to adapt to your hosting environment. Before generating load, confirm the provider’s testing policy and agree on target systems, load limits and stop conditions, especially on shared hosting. Restrict access to test controls before startup. Throughout each run, monitor generator health as well as the target; before acting on results, verify the achieved users, RPS and duration against the intended load profile.
- Clarify the event and goals
- Define expected traffic (visitors/hour, RPS, concurrent users) and performance budgets.
- Write them down so you can verify them later.
- Prepare a realistic staging environment
- Clone production to a staging site on the same type of hosting.
- Use a representative database with sensitive data anonymised or replaced, and reproduce relevant performance settings.
- Use test accounts and sandbox payment services; disable real email, SMS and fulfilment actions so checkout and form tests cannot trigger live transactions or messages.
- Set up monitoring and logging
- Collect CPU, RAM, disk, network and DB metrics where your hosting access permits; record visibility gaps that limit diagnosis.
- Confirm access/error logs are enabled and rotated correctly.
- Choose and configure your tool
- Use k6 for scriptable HTTP/API tests and CI integration.
- Use JMeter if you want GUI-based complex flows or diverse protocols.
- Use Locust if your team is comfortable with Python and code-based scenarios.
- Design 2–3 key user journeys
- Define flows for anonymous browsing, search, and checkout or form submission.
- Assign rough probabilities/weights to each.
- Run a smoke test first
- Start with 5–10 concurrent users to ensure your script and environment work.
- Fix any errors, broken logins or missing tokens.
- Run the main load test
- Gradually ramp up to your target concurrency over several minutes.
- Maintain peak load for a duration representative of the expected event while watching metrics. A 10–20 minute run can be an initial check, not proof of sustained capacity.
- Optionally run a short stress test
- Within the agreed test limits, increase load beyond the expected peak in controlled steps—for example, 20–50% above peak if permitted. Stop at predefined error, latency or resource limits and observe recovery after reducing load.
- Analyse and act
- Correlate tool metrics (RPS, latency, errors) with server data.
- Implement tuning changes or plan a hosting upgrade where necessary.
- Repeat under comparable load after changes
- Verify that your adjustments actually improved headroom and stability.
Conclusion: Make Load Testing Part of Your Hosting Routine
Load testing with tools like k6, JMeter and Locust is not reserved for giant tech companies; it is a practical discipline that fits perfectly into the lifecycle of any serious website, e‑commerce store or SaaS project. By designing realistic scenarios, preparing your monitoring, and running structured tests ahead of major campaigns, you can identify and address bottlenecks that could cause slowdowns or outages during the event.
Treat performance as an ongoing process: test new features, verify scaling decisions, and validate changes against your performance goals. Combine regular load testing with the best practices from our guides on benchmarking a new VPS and setting up monitoring and alerts, to guide performance tuning and capacity planning.
If you are planning a traffic spike, campaign or new product launch and want to make sure your infrastructure is ready, you can assess your current hosting using the blueprint above within the agreed testing limits. If the tests show you need more headroom, use the measured bottlenecks to choose between tuning and a capacity upgrade, then validate the change with a comparable test.





