Getting Started with k6: Modern Load Testing with JavaScript
Learn how to use k6 for load testing with its JavaScript-based scripting, built-in metrics, and developer-friendly workflow.
Mark
Performance Testing Expert
k6 has become one of my preferred tools for load testing, particularly when working with development teams who are already comfortable with JavaScript. Unlike JMeter’s XML-based test plans, k6 uses plain JavaScript, making it easier to version control, review in pull requests, and integrate into modern development workflows.
Why k6?
After years of using JMeter and LoadRunner, k6 stands out for several reasons:
| Feature | k6 | JMeter |
|---|---|---|
| Scripting language | JavaScript ES6 | XML/GUI |
| Resource usage | Low (Go-based) | High (Java-based) |
| CI/CD integration | Native | Requires plugins |
| Learning curve | Low for JS developers | Moderate |
| Protocol support | HTTP, WebSocket, gRPC | Extensive |
The trade-off is that k6 focuses primarily on HTTP-based protocols, while JMeter supports a wider range including JDBC, JMS, and LDAP. For API and web application testing, k6 is often the better choice.
Installation
k6 is distributed as a single binary with no dependencies.
Linux (Debian/Ubuntu):
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
macOS:
brew install k6
Windows:
choco install k6
Verify the installation:
k6 version
Your First k6 Script
Create a file called script.js:
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 10,
duration: '30s',
};
export default function () {
const res = http.get('https://test-api.k6.io/public/crocodiles/');
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});
sleep(1);
}
This script runs 10 virtual users for 30 seconds, each making requests with a 1-second pause between iterations.
Running the Test
Execute the script:
k6 run script.js
The output shows real-time metrics:
running (0m30.0s), 00/10 VUs, 285 complete and 0 interrupted iterations
default ✓ [======================================] 10 VUs 30s
✓ status is 200
✓ response time < 500ms
checks.........................: 100.00% ✓ 570 ✗ 0
data_received..................: 1.2 MB 40 kB/s
data_sent......................: 45 kB 1.5 kB/s
http_req_duration..............: avg=89.12ms min=76.23ms max=234.56ms
http_reqs......................: 285 9.5/s
iteration_duration.............: avg=1.09s min=1.07s max=1.23s
iterations.....................: 285 9.5/s
vus............................: 10 min=10 max=10
vus_max........................: 10 min=10 max=10
Ramping Load Patterns
For more realistic scenarios, use stages to ramp users up and down:
export const options = {
stages: [
{ duration: '2m', target: 50 }, // Ramp up to 50 users
{ duration: '5m', target: 50 }, // Stay at 50 users
{ duration: '2m', target: 100 }, // Ramp up to 100 users
{ duration: '5m', target: 100 }, // Stay at 100 users
{ duration: '2m', target: 0 }, // Ramp down to 0
],
};
Thresholds for Pass/Fail
Define success criteria that cause the test to fail if not met:
export const options = {
thresholds: {
http_req_duration: ['p(95)<500'], // 95% of requests under 500ms
http_req_failed: ['rate<0.01'], // Less than 1% failure rate
checks: ['rate>0.99'], // 99% of checks must pass
},
};
This is particularly useful in CI/CD pipelines where you want automated pass/fail decisions.
Handling Authentication
For APIs requiring authentication:
import http from 'k6/http';
const BASE_URL = 'https://api.example.com';
export function setup() {
const loginRes = http.post(`${BASE_URL}/auth/login`, {
username: 'testuser',
password: 'testpass',
});
return { token: loginRes.json('access_token') };
}
export default function (data) {
const params = {
headers: {
Authorization: `Bearer ${data.token}`,
},
};
http.get(`${BASE_URL}/api/protected-resource`, params);
}
The setup() function runs once before the test and shares data with all virtual users.
Output to JSON for Analysis
Export results for later analysis:
k6 run --out json=results.json script.js
Or stream metrics to InfluxDB for Grafana dashboards:
k6 run --out influxdb=http://localhost:8086/k6 script.js
When to Choose k6
I recommend k6 when:
- Your team is comfortable with JavaScript
- You’re testing REST APIs or web applications
- CI/CD integration is a priority
- You need lightweight, fast execution
Stick with JMeter when:
- You need to test JDBC, JMS, or other non-HTTP protocols
- You prefer a GUI for test design
- You have existing JMeter expertise and test assets
k6 has become an essential part of my performance testing toolkit, particularly for modern API-first applications where its JavaScript syntax and CI/CD integration provide significant advantages over traditional tools.
Tags: