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:
- A Klutch.sh account
- A GitHub account with a repository for your EMQX project
- Docker installed locally for testing (optional but recommended)
- Basic understanding of Docker and MQTT protocol
Installation and Setup
Step 1: Create Your Project Directory
First, create a new directory for your EMQX deployment project:
mkdir emqx-klutchcd emqx-klutchgit initStep 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 dashboardENV EMQX_NAME=emqxENV EMQX_HOST=127.0.0.1
# Optional: Copy custom configuration# COPY ./emqx.conf /opt/emqx/etc/emqx.confNote: 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 namenode.name = emqx@127.0.0.1
# Cluster configurationcluster.discovery = static
# MQTT listenerslisteners.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 configurationdashboard { listeners.http { bind = "0.0.0.0:18083" } default_username = "admin" default_password = "public"}
# Authenticationauthentication = [ { mechanism = password_based backend = built_in_database user_id_type = username }]
# Authorizationauthorization { no_match = deny deny_action = disconnect cache.enable = true}
# Logginglog { file_handlers.default { level = warning file = "/opt/emqx/log/emqx.log" }}
# Session configurationmqtt { 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 configurationCOPY ./emqx.conf /opt/emqx/etc/emqx.conf
# Expose MQTT portsEXPOSE 1883 8883 8083 8084 18083
ENV EMQX_NAME=emqxENV EMQX_HOST=127.0.0.1Step 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 readysleep 10
# Add users via EMQX API# Replace with your admin credentialsEMQX_API="http://localhost:18083/api/v5"ADMIN_USER="admin"ADMIN_PASS="public"
# Create application usercurl -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:
# Build the Docker imagedocker build -t my-emqx .
# Run the containerdocker run -d \ --name emqx-test \ -p 1883:1883 \ -p 8083:8083 \ -p 18083:18083 \ my-emqx
# Wait a moment for EMQX to startsleep 10
# Check the container logsdocker logs emqx-test
# Test MQTT connection using mosquitto client (if installed)# Subscribe to a topicmosquitto_sub -h localhost -p 1883 -t test/topic -v &
# Publish a messagemosquitto_pub -h localhost -p 1883 -t test/topic -m "Hello from EMQX!"
# Access the EMQX Dashboardecho "Open http://localhost:18083 in your browser"echo "Default credentials: admin / public"
# Stop and remove the test container when donedocker stop emqx-testdocker rm emqx-testStep 6: Push to GitHub
Commit your Dockerfile and any configuration files to your GitHub repository:
git add Dockerfile emqx.conf init-users.shgit commit -m "Add EMQX Dockerfile and configuration"git remote add origin https://github.com/yourusername/emqx-klutch.gitgit push -u origin mainConnecting 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.shPort: 8000Protocol: mqtt://MQTT over WebSocket:
URL: ws://example-app.klutch.sh:8000/mqttReplace example-app.klutch.sh with your actual Klutch.sh app URL.
With Authentication:
Username: iot_device_001Password: secure_device_passwordExample Connection Code
Node.js (using mqtt.js):
const mqtt = require('mqtt');
// Connect to EMQXconst 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 handlersclient.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 handlerclient.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 handlerclient.on('error', (error) => { console.error('Connection error:', error);});
// Disconnect handlerclient.on('close', () => { console.log('Disconnected from EMQX broker');});Python (using paho-mqtt):
import paho.mqtt.client as mqttimport jsonimport timefrom datetime import datetime
# Callback when connection is establisheddef 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 receiveddef 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 clientclient = mqtt.Client(client_id="python_client_001")
# Set username and passwordclient.username_pw_set("iot_device_001", "secure_device_password")
# Assign callbacksclient.on_connect = on_connectclient.on_message = on_message
# Connect to brokerclient.connect("example-app.klutch.sh", 8000, 60)
# Start the looptry: 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
-
Log in to Klutch.sh
Navigate to klutch.sh/app and sign in to your account.
-
Create a New Project
Go to Create Project and give your project a meaningful name (e.g., “EMQX MQTT Broker”).
-
Create a New App
Navigate to Create App and configure the following settings:
-
Select Your Repository
- Choose GitHub as your Git source
- Select the repository containing your Dockerfile
- Choose the branch you want to deploy (usually
mainormaster)
-
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)
-
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.
-
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.
-
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)
-
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
-
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:8000WebSocket 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_connectionssettings 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 examplesdevices/{device_id}/telemetry/temperaturedevices/{device_id}/telemetry/humiditydevices/{device_id}/status/onlinedevices/{device_id}/commands/reboot
fleet/{fleet_id}/vehicles/{vehicle_id}/locationfleet/{fleet_id}/vehicles/{vehicle_id}/diagnostics
home/{room}/sensors/temperaturehome/{room}/lights/controlhome/{room}/lights/statusTopic 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:
# Get cluster metrics via APIcurl -u admin:public http://example-app.klutch.sh:18083/api/v5/metrics
# Get client listcurl -u admin:public http://example-app.klutch.sh:18083/api/v5/clients
# Get node statuscurl -u admin:public http://example-app.klutch.sh:18083/api/v5/nodesHigh Availability Setup
For production deployments requiring high availability:
- Clustering: Deploy multiple EMQX nodes in a cluster
- Load Balancing: Use a load balancer to distribute client connections
- Session Persistence: Enable persistent sessions for critical clients
- Retained Messages: Configure retained message storage
- 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 databaseSELECT payload.deviceId as device_id, payload.temperature as temperature, payload.timestamp as timestampFROM "sensors/temperature"WHERE payload.temperature > 30Common 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_connectionsin 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 messagesclient.subscribe('$share/group1/sensors/temperature');Retained Messages
Store the last message on a topic for new subscribers:
// Publish with retain flagclient.publish('sensors/temperature', JSON.stringify(data), { retain: true });Last Will and Testament (LWT)
Notify other clients when a device disconnects unexpectedly:
# Python example with LWTclient.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.confbridges { 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 eventswebhooks { client_connected { url = "https://your-api.example.com/mqtt/connected" headers { content-type = "application/json" authorization = "Bearer your-token" } }}Additional Resources
- Klutch.sh Documentation
- Official EMQX Documentation
- EMQX Docker Image Documentation
- Klutch.sh Volumes Guide
- MQTT Protocol Specification
- EMQX GitHub Repository
- EMQX Dashboard Guide
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.