Skip to main content
k6 protocol extensions enable you to test systems using protocols beyond HTTP and WebSockets. Official protocol extensions are maintained by Grafana Labs and support automatic extension resolution.

Overview

Protocol extensions let you:
  • Test message brokers (MQTT, Kafka)
  • Load test databases (Redis, SQL)
  • Validate IoT infrastructure
  • Test event-driven architectures
  • Simulate thousands of protocol clients

MQTT

The k6/x/mqtt extension adds first-class support for the MQTT protocol, enabling you to test MQTT brokers and IoT infrastructure.

Key features

  • Event-driven architecture with lifecycle events (connect, message, end, reconnect, error)
  • Synchronous and asynchronous methods for all operations
  • Quality of Service (QoS) levels: 0, 1, and 2
  • Message retention and Last Will and Testament (LWT) support
  • Multiple broker URL schemas: mqtt://, mqtts://, ws://, wss://
  • SSL/TLS support with standard k6 TLS configuration
  • Automatic metrics collection

Basic example

import { Client } from 'k6/x/mqtt';

export default function () {
  const client = new Client();

  client.on('connect', async () => {
    console.log('Connected to MQTT broker');
    client.subscribe('greeting');
    client.publish('greeting', 'Hello MQTT!');
  });

  client.on('message', (topic, message) => {
    const str = String.fromCharCode.apply(null, new Uint8Array(message));
    console.info('topic:', topic, 'message:', str);
    client.end();
  });

  client.on('end', () => {
    console.log('Disconnected from MQTT broker');
  });

  client.connect(__ENV['MQTT_BROKER_ADDRESS'] || 'mqtt://broker.emqx.io:1883');
}

Async programming

Use async/await with MQTT event handlers:
import { Client } from 'k6/x/mqtt';

export default function () {
  const client = new Client();

  client.on('connect', async () => {
    console.log('Connected to MQTT broker');
    await client.subscribeAsync('probe');

    const intervalId = setInterval(() => {
      client.publish('probe', 'ping MQTT!');
    }, 1000);

    setTimeout(() => {
      clearInterval(intervalId);
      client.end();
    }, 3100);
  });

  client.on('message', (topic, message) => {
    console.info(String.fromCharCode.apply(null, new Uint8Array(message)));
  });

  client.on('end', () => {
    console.log('Disconnected from MQTT broker');
  });

  client.connect(__ENV['MQTT_BROKER_ADDRESS'] || 'mqtt://broker.emqx.io:1883');
}

QoS levels

import { Client, QoS } from 'k6/x/mqtt';

export default function () {
  const client = new Client();

  client.on('connect', () => {
    // Subscribe with QoS 1 (at least once)
    client.subscribe('sensor/data', { qos: QoS.AtLeastOnce });

    // Publish with QoS 2 (exactly once)
    client.publish('sensor/data', 'temperature:22.5', { qos: QoS.ExactlyOnce });
  });

  client.connect('mqtt://localhost:1883');
}

MQTT metrics

The extension automatically generates metrics:
  • mqtt_connects - Connection attempts
  • mqtt_connect_duration - Connection time
  • mqtt_publishes - Number of publish operations
  • mqtt_subscribes - Number of subscribe operations
  • mqtt_messages_received - Received messages
  • mqtt_messages_sent - Sent messages

Use cases

IoT load testing

Simulate thousands of IoT devices publishing sensor data

Message throughput

Test MQTT broker performance and message delivery rates

QoS validation

Verify Quality of Service guarantees under load

Event-driven systems

Test MQTT-based microservices and event architectures

Redis

The k6/x/redis extension provides a client library for interacting with Redis databases directly from k6 scripts.

Features

  • Load test Redis databases
  • Use Redis as a data store for test-script logic
  • Promise-based async API
  • Support for common Redis commands

Basic example

import redis from 'k6/x/redis';

export const options = {
  vus: 10,
  duration: '30s',
};

const client = new redis.Client('redis://localhost:6379');

export default async function () {
  // Set a value
  await client.set('key', 'value');

  // Get the value
  const value = await client.get('key');
  console.log('Retrieved:', value);

  // Increment a counter
  await client.incr('counter');

  // Check if key exists
  const exists = await client.exists('key');
  console.log('Key exists:', exists);
}

Connection options

import redis from 'k6/x/redis';

const client = new redis.Client({
  host: 'localhost',
  port: 6379,
  password: 'your-password',
  db: 0,
  dialTimeout: '5s',
  readTimeout: '3s',
  writeTimeout: '3s',
});

Common operations

import redis from 'k6/x/redis';

const client = new redis.Client('redis://localhost:6379');

export default async function () {
  // String operations
  await client.set('user:1:name', 'Alice');
  await client.setex('session:abc', 3600, 'session-data'); // With expiry
  const name = await client.get('user:1:name');

  // Hash operations
  await client.hset('user:1', 'email', '[email protected]');
  await client.hset('user:1', 'age', '30');
  const email = await client.hget('user:1', 'email');
  const user = await client.hgetall('user:1');

  // List operations
  await client.lpush('queue:jobs', 'job1');
  await client.rpush('queue:jobs', 'job2');
  const job = await client.lpop('queue:jobs');

  // Set operations
  await client.sadd('users:active', 'user1', 'user2', 'user3');
  const members = await client.smembers('users:active');

  // Delete keys
  await client.del('key');
}

Use cases

Cache load testing

Test Redis cache performance under high load

Session storage

Validate session management at scale

Test data sharing

Share data between VUs using Redis

Rate limiting

Test rate limiting implementations

SQL

The k6/x/sql extension enables testing of SQL databases including PostgreSQL, MySQL, SQLite, and more.

Supported databases

  • PostgreSQL
  • MySQL / MariaDB
  • Microsoft SQL Server
  • SQLite
  • ClickHouse

Basic example

import sql from 'k6/x/sql';

const db = sql.open('postgres', 'postgres://user:password@localhost:5432/testdb?sslmode=disable');

export default function () {
  // Execute a query
  const results = sql.query(db, 'SELECT * FROM users WHERE active = $1', true);
  console.log(`Found ${results.length} active users`);

  // Insert data
  sql.exec(db, 'INSERT INTO users (name, email) VALUES ($1, $2)', 'Alice', '[email protected]');

  // Execute with multiple rows
  for (let i = 0; i < 10; i++) {
    sql.exec(db, 'INSERT INTO logs (message) VALUES ($1)', `Log entry ${i}`);
  }
}

export function teardown() {
  db.close();
}

Connection strings

const db = sql.open('postgres', 'postgres://user:password@localhost:5432/database?sslmode=disable');

Transaction example

import sql from 'k6/x/sql';

const db = sql.open('postgres', 'postgres://localhost:5432/testdb');

export default function () {
  // Begin transaction
  const tx = db.begin();

  try {
    // Execute queries in transaction
    tx.exec('UPDATE accounts SET balance = balance - 100 WHERE id = 1');
    tx.exec('UPDATE accounts SET balance = balance + 100 WHERE id = 2');

    // Commit transaction
    tx.commit();
  } catch (error) {
    // Rollback on error
    tx.rollback();
    console.error('Transaction failed:', error);
  }
}

Use cases

Database load testing

Test database performance under concurrent queries

Connection pooling

Validate connection pool behavior

Transaction testing

Test transaction isolation and deadlocks

Query performance

Measure query execution times at scale

Using protocol extensions

All official protocol extensions support automatic resolution:
import mqtt from 'k6/x/mqtt';
import redis from 'k6/x/redis';
import sql from 'k6/x/sql';

// Use extensions directly - k6 loads them automatically
Run your test:
k6 run script.js

Build custom binary

To bundle protocol extensions in a custom binary:
xk6 build \
  --with github.com/grafana/xk6-sql@latest \
  --with github.com/grafana/xk6-output-prometheus-remote

Combining protocols

Test complex systems using multiple protocols:
import http from 'k6/http';
import { Client as MQTTClient } from 'k6/x/mqtt';
import redis from 'k6/x/redis';
import { check } from 'k6';

const redisClient = new redis.Client('redis://localhost:6379');

export default function () {
  // HTTP API call
  const res = http.post('https://api.example.com/orders', JSON.stringify({
    product: 'widget',
    quantity: 1,
  }));

  check(res, { 'order created': (r) => r.status === 201 });
  const orderId = res.json('id');

  // Store in Redis
  redisClient.set(`order:${orderId}`, res.body);

  // Publish MQTT notification
  const mqtt = new MQTTClient();
  mqtt.on('connect', () => {
    mqtt.publish('orders/new', `Order ${orderId} created`);
    mqtt.end();
  });
  mqtt.connect('mqtt://localhost:1883');
}

Best practices

Connection pooling

Reuse connections in the init context when possible

Async operations

Use async/await for non-blocking protocol operations

Error handling

Always handle connection errors and timeouts

Resource cleanup

Close connections in teardown or finally blocks

Next steps

Explore Extensions

Discover more k6 extensions

Build Custom Binary

Bundle extensions with xk6

xk6-disruptor

Add chaos testing capabilities

Browser Extension

Test frontend with browser automation

Build docs developers (and LLMs) love