Skip to content

Deploying EMQX

Introduction

EMQX is a highly scalable, open-source MQTT broker designed for IoT (Internet of Things) and real-time messaging applications. Developed by EMQ Technologies, EMQX is built on Erlang/OTP and provides enterprise-grade reliability, making it one of the most popular MQTT brokers in the world.

EMQX excels at:

  • Massive Scalability: Supports millions of concurrent MQTT connections with horizontal scaling
  • MQTT Protocol Support: Full implementation of MQTT 3.1, 3.1.1, and 5.0 specifications
  • Real-Time Messaging: Ultra-low latency message delivery for IoT devices and applications
  • High Availability: Built-in clustering and node auto-discovery for fault tolerance
  • Flexible Authentication: Multiple authentication mechanisms including username/password, JWT, and OAuth 2.0
  • Rule Engine: Powerful built-in rule engine for message routing and data integration
  • Protocol Bridge: Bridges to other messaging protocols like MQTT-SN, CoAP, LwM2M, and WebSocket
  • Data Persistence: Message persistence with built-in database and external integrations
  • Rich API: RESTful API and Dashboard for monitoring and management
  • Plugin Architecture: Extensible through plugins for custom functionality

Common use cases include IoT device connectivity, smart home automation, industrial IoT (IIoT), connected vehicles, mobile push notifications, real-time chat applications, and telemetry data collection.

This comprehensive guide walks you through deploying EMQX on Klutch.sh using Docker, including detailed installation steps, sample configurations, connection examples, and production-ready best practices for scalable IoT messaging infrastructure.

Prerequisites

Before you begin, ensure you have the following:


Installation and Setup

Step 1: Create Your Project Directory

First, create a new directory for your EMQX deployment project:

Terminal window
mkdir emqx-klutch
cd emqx-klutch
git init

Step 2: Create the Dockerfile

Create a Dockerfile in your project root directory. This will define your EMQX container configuration:

FROM emqx/emqx:5.5.0
# Expose MQTT ports
# 1883: MQTT over TCP
# 8883: MQTT over SSL/TLS
# 8083: MQTT over WebSocket
# 8084: MQTT over secure WebSocket
# 18083: EMQX Dashboard (HTTP)
EXPOSE 1883 8883 8083 8084 18083
# Set default environment variables
# These can be overridden in Klutch.sh dashboard
ENV EMQX_NAME=emqx
ENV EMQX_HOST=127.0.0.1
# Optional: Copy custom configuration
# COPY ./emqx.conf /opt/emqx/etc/emqx.conf

Note: EMQX 5.5.0 is a stable release with improved performance and features. Using specific version tags ensures reproducible deployments.

Step 3: (Optional) Create Custom Configuration

For production deployments, you may want to create a custom emqx.conf file with specific settings. Create a file named emqx.conf:

# emqx.conf - Custom EMQX Configuration
# Node name
node.name = emqx@127.0.0.1
# Cluster configuration
cluster.discovery = static
# MQTT listeners
listeners.tcp.default {
bind = "0.0.0.0:1883"
max_connections = 1024000
}
listeners.ssl.default {
bind = "0.0.0.0:8883"
max_connections = 512000
ssl_options {
cacertfile = "/opt/emqx/etc/certs/cacert.pem"
certfile = "/opt/emqx/etc/certs/cert.pem"
keyfile = "/opt/emqx/etc/certs/key.pem"
}
}
listeners.ws.default {
bind = "0.0.0.0:8083"
max_connections = 1024000
websocket.mqtt_path = "/mqtt"
}
# Dashboard configuration
dashboard {
listeners.http {
bind = "0.0.0.0:18083"
}
default_username = "admin"
default_password = "public"
}
# Authentication
authentication = [
{
mechanism = password_based
backend = built_in_database
user_id_type = username
}
]
# Authorization
authorization {
no_match = deny
deny_action = disconnect
cache.enable = true
}
# Logging
log {
file_handlers.default {
level = warning
file = "/opt/emqx/log/emqx.log"
}
}
# Session configuration
mqtt {
max_packet_size = 1MB
max_clientid_len = 65535
max_topic_levels = 128
max_qos_allowed = 2
max_topic_alias = 65535
retain_available = true
wildcard_subscription = true
shared_subscription = true
}

If you create a custom configuration file, update your Dockerfile:

FROM emqx/emqx:5.5.0
# Copy custom configuration
COPY ./emqx.conf /opt/emqx/etc/emqx.conf
# Expose MQTT ports
EXPOSE 1883 8883 8083 8084 18083
ENV EMQX_NAME=emqx
ENV EMQX_HOST=127.0.0.1

Step 4: (Optional) Create Authentication Setup Script

You can create a script to set up users and authentication. Create a file named init-users.sh:

#!/bin/bash
# init-users.sh - Initialize EMQX users and authentication
# Wait for EMQX to be ready
sleep 10
# Add users via EMQX API
# Replace with your admin credentials
EMQX_API="http://localhost:18083/api/v5"
ADMIN_USER="admin"
ADMIN_PASS="public"
# Create application user
curl -X POST "${EMQX_API}/authentication/password_based:built_in_database/users" \
-u "${ADMIN_USER}:${ADMIN_PASS}" \
-H "Content-Type: application/json" \
-d '{
"user_id": "iot_device_001",
"password": "secure_device_password",
"is_superuser": false
}'
curl -X POST "${EMQX_API}/authentication/password_based:built_in_database/users" \
-u "${ADMIN_USER}:${ADMIN_PASS}" \
-H "Content-Type: application/json" \
-d '{
"user_id": "app_client",
"password": "secure_app_password",
"is_superuser": false
}'
echo "Users created successfully"

Step 5: Test Locally (Optional)

Before deploying to Klutch.sh, you can test your EMQX setup locally:

Terminal window
# Build the Docker image
docker build -t my-emqx .
# Run the container
docker run -d \
--name emqx-test \
-p 1883:1883 \
-p 8083:8083 \
-p 18083:18083 \
my-emqx
# Wait a moment for EMQX to start
sleep 10
# Check the container logs
docker logs emqx-test
# Test MQTT connection using mosquitto client (if installed)
# Subscribe to a topic
mosquitto_sub -h localhost -p 1883 -t test/topic -v &
# Publish a message
mosquitto_pub -h localhost -p 1883 -t test/topic -m "Hello from EMQX!"
# Access the EMQX Dashboard
echo "Open http://localhost:18083 in your browser"
echo "Default credentials: admin / public"
# Stop and remove the test container when done
docker stop emqx-test
docker rm emqx-test

Step 6: Push to GitHub

Commit your Dockerfile and any configuration files to your GitHub repository:

Terminal window
git add Dockerfile emqx.conf init-users.sh
git commit -m "Add EMQX Dockerfile and configuration"
git remote add origin https://github.com/yourusername/emqx-klutch.git
git push -u origin main

Connecting to EMQX

Once deployed, you can connect to your EMQX broker from any MQTT client or application. Since Klutch.sh routes TCP traffic through port 8000, use the following connection information:

Connection Details

MQTT over TCP:

Host: example-app.klutch.sh
Port: 8000
Protocol: mqtt://

MQTT over WebSocket:

URL: ws://example-app.klutch.sh:8000/mqtt

Replace example-app.klutch.sh with your actual Klutch.sh app URL.

With Authentication:

Username: iot_device_001
Password: secure_device_password

Example Connection Code

Node.js (using mqtt.js):

const mqtt = require('mqtt');
// Connect to EMQX
const client = mqtt.connect('mqtt://example-app.klutch.sh:8000', {
clientId: 'nodejs_client_' + Math.random().toString(16).substr(2, 8),
username: 'iot_device_001',
password: 'secure_device_password',
clean: true,
reconnectPeriod: 1000,
});
// Connection event handlers
client.on('connect', () => {
console.log('Connected to EMQX broker');
// Subscribe to topics
client.subscribe('sensors/temperature', (err) => {
if (!err) {
console.log('Subscribed to sensors/temperature');
// Publish a message
client.publish('sensors/temperature', JSON.stringify({
deviceId: 'temp_sensor_01',
temperature: 22.5,
unit: 'celsius',
timestamp: new Date().toISOString()
}));
}
});
});
// Message handler
client.on('message', (topic, message) => {
console.log(`Received message on ${topic}:`, message.toString());
const data = JSON.parse(message.toString());
console.log('Temperature:', data.temperature, data.unit);
});
// Error handler
client.on('error', (error) => {
console.error('Connection error:', error);
});
// Disconnect handler
client.on('close', () => {
console.log('Disconnected from EMQX broker');
});

Python (using paho-mqtt):

import paho.mqtt.client as mqtt
import json
import time
from datetime import datetime
# Callback when connection is established
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to EMQX broker")
# Subscribe to topics
client.subscribe("sensors/temperature")
client.subscribe("sensors/humidity")
# Publish a test message
payload = {
"deviceId": "temp_sensor_01",
"temperature": 22.5,
"unit": "celsius",
"timestamp": datetime.now().isoformat()
}
client.publish("sensors/temperature", json.dumps(payload))
else:
print(f"Connection failed with code {rc}")
# Callback when a message is received
def on_message(client, userdata, msg):
print(f"Received message on {msg.topic}:")
try:
data = json.loads(msg.payload.decode())
print(f" Temperature: {data['temperature']} {data['unit']}")
print(f" Device: {data['deviceId']}")
print(f" Timestamp: {data['timestamp']}")
except json.JSONDecodeError:
print(f" {msg.payload.decode()}")
# Create MQTT client
client = mqtt.Client(client_id="python_client_001")
# Set username and password
client.username_pw_set("iot_device_001", "secure_device_password")
# Assign callbacks
client.on_connect = on_connect
client.on_message = on_message
# Connect to broker
client.connect("example-app.klutch.sh", 8000, 60)
# Start the loop
try:
client.loop_forever()
except KeyboardInterrupt:
print("Disconnecting...")
client.disconnect()

Go (using paho.mqtt.golang):

package main
import (
"encoding/json"
"fmt"
"log"
"time"
mqtt "github.com/eclipse/paho.mqtt.golang"
)
type SensorData struct {
DeviceID string `json:"deviceId"`
Temperature float64 `json:"temperature"`
Unit string `json:"unit"`
Timestamp time.Time `json:"timestamp"`
}
func main() {
// Configure MQTT options
opts := mqtt.NewClientOptions()
opts.AddBroker("tcp://example-app.klutch.sh:8000")
opts.SetClientID("go_client_001")
opts.SetUsername("iot_device_001")
opts.SetPassword("secure_device_password")
opts.SetCleanSession(true)
opts.SetAutoReconnect(true)
// Set connection handler
opts.OnConnect = func(c mqtt.Client) {
fmt.Println("Connected to EMQX broker")
// Subscribe to topic
token := c.Subscribe("sensors/temperature", 0, messageHandler)
token.Wait()
fmt.Println("Subscribed to sensors/temperature")
// Publish a message
data := SensorData{
DeviceID: "temp_sensor_01",
Temperature: 22.5,
Unit: "celsius",
Timestamp: time.Now(),
}
payload, _ := json.Marshal(data)
c.Publish("sensors/temperature", 0, false, payload)
}
// Set connection lost handler
opts.OnConnectionLost = func(c mqtt.Client, err error) {
fmt.Printf("Connection lost: %v\n", err)
}
// Create and start the client
client := mqtt.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
log.Fatal(token.Error())
}
// Keep the program running
select {}
}
func messageHandler(client mqtt.Client, msg mqtt.Message) {
fmt.Printf("Received message on %s:\n", msg.Topic())
var data SensorData
if err := json.Unmarshal(msg.Payload(), &data); err == nil {
fmt.Printf(" Temperature: %.1f %s\n", data.Temperature, data.Unit)
fmt.Printf(" Device: %s\n", data.DeviceID)
fmt.Printf(" Timestamp: %s\n", data.Timestamp.Format(time.RFC3339))
} else {
fmt.Printf(" %s\n", string(msg.Payload()))
}
}

JavaScript (Browser - WebSocket):

// Using MQTT.js in the browser
// Include via CDN: <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
const client = mqtt.connect('ws://example-app.klutch.sh:8000/mqtt', {
clientId: 'browser_client_' + Math.random().toString(16).substr(2, 8),
username: 'iot_device_001',
password: 'secure_device_password',
});
client.on('connect', () => {
console.log('Connected to EMQX via WebSocket');
// Subscribe to topics
client.subscribe('sensors/#', (err) => {
if (!err) {
console.log('Subscribed to sensors/#');
}
});
// Publish data
const sensorData = {
deviceId: 'browser_sensor_01',
temperature: 23.5,
unit: 'celsius',
timestamp: new Date().toISOString()
};
client.publish('sensors/temperature', JSON.stringify(sensorData));
});
client.on('message', (topic, message) => {
console.log(`Message on ${topic}:`, message.toString());
// Update UI with received data
const data = JSON.parse(message.toString());
document.getElementById('temperature').textContent =
`${data.temperature} ${data.unit}`;
});

Java (using Eclipse Paho):

import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.json.JSONObject;
import java.util.UUID;
public class EMQXClient {
private static final String BROKER = "tcp://example-app.klutch.sh:8000";
private static final String CLIENT_ID = "java_client_" + UUID.randomUUID();
private static final String USERNAME = "iot_device_001";
private static final String PASSWORD = "secure_device_password";
public static void main(String[] args) {
try {
MqttClient client = new MqttClient(BROKER, CLIENT_ID, new MemoryPersistence());
// Set connection options
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(USERNAME);
options.setPassword(PASSWORD.toCharArray());
options.setCleanSession(true);
options.setAutomaticReconnect(true);
// Set callback
client.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
System.out.println("Connection lost: " + cause.getMessage());
}
@Override
public void messageArrived(String topic, MqttMessage message) {
System.out.println("Message received on " + topic + ":");
System.out.println(" " + new String(message.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("Message delivered");
}
});
// Connect
System.out.println("Connecting to EMQX broker...");
client.connect(options);
System.out.println("Connected!");
// Subscribe
client.subscribe("sensors/temperature", 0);
System.out.println("Subscribed to sensors/temperature");
// Publish
JSONObject data = new JSONObject();
data.put("deviceId", "temp_sensor_01");
data.put("temperature", 22.5);
data.put("unit", "celsius");
data.put("timestamp", System.currentTimeMillis());
MqttMessage message = new MqttMessage(data.toString().getBytes());
message.setQos(0);
client.publish("sensors/temperature", message);
// Keep running
Thread.sleep(60000);
// Disconnect
client.disconnect();
System.out.println("Disconnected");
} catch (Exception e) {
e.printStackTrace();
}
}
}

Deploying to Klutch.sh

Now that your EMQX project is ready and pushed to GitHub, follow these steps to deploy it on Klutch.sh with persistent storage.

Deployment Steps

    1. Log in to Klutch.sh

      Navigate to klutch.sh/app and sign in to your account.

    2. Create a New Project

      Go to Create Project and give your project a meaningful name (e.g., “EMQX MQTT Broker”).

    3. Create a New App

      Navigate to Create App and configure the following settings:

    4. Select Your Repository

      • Choose GitHub as your Git source
      • Select the repository containing your Dockerfile
      • Choose the branch you want to deploy (usually main or master)
    5. Configure Traffic Type

      • Traffic Type: Select TCP (EMQX requires TCP traffic for MQTT connections)
      • Internal Port: Set to 1883 (the default MQTT port that your container listens on)
    6. Set Environment Variables (Optional)

      If you need to customize EMQX behavior, you can add environment variables:

      • EMQX_NAME: Node name for EMQX (default: emqx)
      • EMQX_HOST: Host address (default: 127.0.0.1)
      • EMQX_DASHBOARD__DEFAULT_USERNAME: Dashboard admin username (default: admin)
      • EMQX_DASHBOARD__DEFAULT_PASSWORD: Dashboard admin password (change from default!)
      • EMQX_LOG__CONSOLE_HANDLER__LEVEL: Logging level (debug, info, warning, error)

      Security Note: Always change the default dashboard password for production deployments.

    7. Attach a Persistent Volume (Recommended for Data Persistence)

      If you want your EMQX data, configuration, and logs to persist across deployments and restarts:

      • In the Volumes section, click “Add Volume”
      • Mount Path: Enter /opt/emqx/data (this is where EMQX stores persistent data, configurations, and logs)
      • Size: Choose an appropriate size based on your expected data volume (e.g., 5GB for small deployments, 20GB+ for production)

      Note: Persistent storage is essential for maintaining retained messages, session data, and configuration across container restarts.

    8. Configure Additional Settings

      • Region: Select the region closest to your IoT devices and users for optimal latency
      • Compute Resources: Choose CPU and memory based on your workload (minimum 512MB RAM recommended, 1GB-2GB for production with many connections)
      • Instances: Start with 1 instance (EMQX clustering can be configured later for high availability)
    9. Deploy Your EMQX Broker

      Click “Create” to start the deployment. Klutch.sh will:

      • Automatically detect your Dockerfile in the repository root
      • Build the Docker image
      • Attach the persistent volume (if configured)
      • Start your EMQX container
      • Assign a URL for external connections
    10. Access Your EMQX Broker

      Once deployment is complete, you’ll receive a URL like example-app.klutch.sh. You can connect to your EMQX broker using this URL on port 8000:

      MQTT Connection:

      mqtt://example-app.klutch.sh:8000

      WebSocket Connection:

      ws://example-app.klutch.sh:8000/mqtt

Production Best Practices

Security Recommendations

  • Enable Authentication: Always enable authentication in production using EMQX’s built-in authentication or external authentication providers
  • Strong Passwords: Use complex, randomly generated passwords for dashboard and MQTT users
  • SSL/TLS Encryption: For production, configure SSL/TLS for encrypted MQTT connections (port 8883)
  • Access Control Lists (ACL): Implement fine-grained topic-level access control to restrict publish/subscribe permissions
  • Dashboard Security: Change the default dashboard credentials immediately and restrict dashboard access
  • API Security: Secure the EMQX REST API with authentication tokens
  • Network Security: EMQX is accessible only through Klutch.sh’s secure network by default
  • Regular Updates: Keep your EMQX version up to date with security patches

Performance Optimization

  • Connection Pool: Optimize max_connections settings based on expected client load
  • Message Persistence: Configure appropriate message retention policies to balance durability and performance
  • QoS Levels: Use appropriate Quality of Service (QoS) levels - QoS 0 for non-critical data, QoS 1-2 for critical messages
  • Retained Messages: Limit retained message usage to avoid memory issues
  • Session Management: Configure session expiry intervals appropriate for your use case
  • Topic Design: Use hierarchical topic structures for efficient routing and filtering
  • Payload Size: Keep message payloads small for better throughput

MQTT Topic Design Best Practices

Design a clear topic hierarchy for your IoT application:

# Good topic hierarchy examples
devices/{device_id}/telemetry/temperature
devices/{device_id}/telemetry/humidity
devices/{device_id}/status/online
devices/{device_id}/commands/reboot
fleet/{fleet_id}/vehicles/{vehicle_id}/location
fleet/{fleet_id}/vehicles/{vehicle_id}/diagnostics
home/{room}/sensors/temperature
home/{room}/lights/control
home/{room}/lights/status

Topic naming conventions:

  • Use forward slashes / to separate levels
  • Use lowercase for consistency
  • Be descriptive but concise
  • Avoid special characters
  • Use wildcards for subscriptions: + (single level), # (multi-level)

Monitoring and Observability

Monitor your EMQX deployment for:

  • Active client connections and connection rate
  • Message throughput (messages/second)
  • Message queue length and delivery latency
  • CPU and memory utilization
  • Network bandwidth usage
  • Authentication failures and security events
  • Topic subscription patterns
  • Retained message count

Use EMQX’s built-in dashboard and metrics API:

Terminal window
# Get cluster metrics via API
curl -u admin:public http://example-app.klutch.sh:18083/api/v5/metrics
# Get client list
curl -u admin:public http://example-app.klutch.sh:18083/api/v5/clients
# Get node status
curl -u admin:public http://example-app.klutch.sh:18083/api/v5/nodes

High Availability Setup

For production deployments requiring high availability:

  1. Clustering: Deploy multiple EMQX nodes in a cluster
  2. Load Balancing: Use a load balancer to distribute client connections
  3. Session Persistence: Enable persistent sessions for critical clients
  4. Retained Messages: Configure retained message storage
  5. Backup Strategy: Regularly backup EMQX configuration and authentication data

Rule Engine and Data Integration

EMQX’s rule engine allows you to process and route messages:

# Example rule: Store temperature data to database
SELECT
payload.deviceId as device_id,
payload.temperature as temperature,
payload.timestamp as timestamp
FROM
"sensors/temperature"
WHERE
payload.temperature > 30

Common integrations:

  • Databases (PostgreSQL, MySQL, MongoDB, Redis)
  • Time-series databases (InfluxDB, TimescaleDB)
  • Message queues (RabbitMQ, Kafka)
  • Cloud services (AWS IoT, Azure IoT Hub)
  • Webhook endpoints for custom processing

Troubleshooting

Cannot Connect to Broker

  • Verify you’re using the correct connection URL with port 8000
  • Check that the internal port is set to 1883 in your app configuration
  • Ensure TCP traffic type is selected
  • Verify authentication credentials if using username/password
  • Check that EMQX container is running and healthy

Authentication Failed

  • Verify username and password are correct
  • Check that authentication is enabled in EMQX configuration
  • Ensure users are created in EMQX (via dashboard or API)
  • Review EMQX logs for authentication error messages

Messages Not Being Received

  • Verify client is subscribed to the correct topic
  • Check that the topic name matches exactly (MQTT topics are case-sensitive)
  • Ensure the publisher is using the correct QoS level
  • Verify no ACL rules are blocking the subscription
  • Check network connectivity between publisher and subscriber

High Memory Usage

  • Review retained message count and size
  • Check for clients with large message queues
  • Monitor session data accumulation
  • Configure appropriate message TTL (Time To Live)
  • Consider upgrading compute resources

Connection Limit Reached

  • Increase max_connections in EMQX configuration
  • Monitor connection patterns for connection leaks
  • Implement connection pooling in client applications
  • Consider scaling to multiple EMQX nodes in a cluster

Dashboard Not Accessible

  • Verify port 18083 is exposed (for local testing)
  • For Klutch.sh deployment, dashboard access may require port forwarding
  • Check default credentials haven’t been changed without documentation
  • Review EMQX logs for dashboard startup errors

Advanced EMQX Features

Shared Subscriptions

Share message load across multiple subscribers:

// Node.js example with shared subscription
// All subscribers with the same group will share messages
client.subscribe('$share/group1/sensors/temperature');

Retained Messages

Store the last message on a topic for new subscribers:

// Publish with retain flag
client.publish('sensors/temperature', JSON.stringify(data), { retain: true });

Last Will and Testament (LWT)

Notify other clients when a device disconnects unexpectedly:

# Python example with LWT
client.will_set("devices/device001/status", "offline", qos=1, retain=True)

Message Bridging

Bridge EMQX to other MQTT brokers or message systems:

# Bridge configuration in emqx.conf
bridges {
mqtt.bridge1 {
connector {
server = "mqtt://other-broker.example.com:1883"
username = "bridge_user"
password = "bridge_password"
}
direction = ingress
remote_topic = "remote/topic/#"
local_topic = "local/topic/${remote_topic}"
}
}

WebHook Integration

Send MQTT events to HTTP endpoints:

# WebHook for client connected events
webhooks {
client_connected {
url = "https://your-api.example.com/mqtt/connected"
headers {
content-type = "application/json"
authorization = "Bearer your-token"
}
}
}

Additional Resources


Conclusion

Deploying EMQX to Klutch.sh with Docker provides a powerful, scalable MQTT broker infrastructure perfect for IoT applications, real-time messaging, and device connectivity. With support for millions of concurrent connections, advanced features like rule engines and data integration, and production-ready security, your EMQX deployment is ready to power mission-critical IoT and messaging workloads. Whether you’re building smart home systems, industrial IoT platforms, or real-time communication applications, EMQX on Klutch.sh offers the reliability and performance you need.