WuKongIM Docs

MQTT Operations and Troubleshooting

Configure consistent cluster entrypoints, understand logical quotas and diagnose public observations.

Development preview

MQTT is disabled by default. Complete Linux, fault and load qualification remains outstanding. Logical quotas are not guarantees of hardware capacity, throughput or concurrent users.

Deployment requirements

Enable mqtt.enable on every MQTT ingress node, use matching candidate binaries/tools and keep namespace consistent. Namespace is a stable ClientID namespace; changing it arbitrarily is not session cleanup. Follow the candidate's cold-rollout constraints. Pre-integration MQTT development data is not a supported direct upgrade source; use pre-MQTT data or a separately verified migration.

A single-node cluster also uses 256 hash slots. Multi-node deployments still need Slot quorum and internal communication. Load balancing does not replace takeover isolation proof. A new CONNECT can fail when the old Owner cannot be proved stopped, even if other Slots retain quorum.

The default listener is 0.0.0.0:1883; the tutorial restricts it to loopback. Public access needs upstream TLS termination and a protected plaintext backend network. mqtts:// targets the TLS proxy; mqtt:// targets raw TCP. Do not disable certificate verification. MQTT WebSocket uses a separate listener; the existing WKProto / JSON-RPC listener cannot accept MQTT packets.

Fields, environment variables and ranges are maintained in the configuration reference. Keep the authentication configuration as well.

Browser WebSocket access

Keep mqtt.enable=true and append this listener to your existing gateway.listeners list:

{ name = "mqtt-ws", network = "websocket", address = "127.0.0.1:1884", transport = "gnet", protocol = "mqtt", path = "/mqtt" }

This is one inline-table entry, not a complete TOML file. Use a unique address for each listener. WK_GATEWAY_LISTENERS replaces the entire list as JSON, so preserve the WKProto listeners you still need. MQTT WebSocket shares the same authentication, topics, limits and durable sessions as TCP; changing transport does not create a new MQTT namespace. It is not advertised by the IM /route response; your backend supplies its URL.

A browser application with the MQTT.js 5.16.0 package can connect with backend-issued credentials:

import mqtt from 'mqtt';

const client = mqtt.connect('ws://127.0.0.1:1884/mqtt', {
  protocolVersion: 5,
  clientId: 'alice-browser-01',
  username: credentials.uid,
  password: credentials.token,
  clean: true,
  reconnectPeriod: 0,
  properties: {
    sessionExpiryInterval: 0,
    userProperties: { 'wk.device_flag': '1' },
  },
});
client.on('error', () => console.error('MQTT connection failed'));

credentials comes from your trusted business backend, which registers the corresponding WEB device token. Install reception listeners before publishing and use the topic and acknowledgement rules in the quickstart. For persistent recovery, keep the ClientID stable and follow sessions and QoS.

MQTT.js offers the case-sensitive mqtt WebSocket subprotocol and sends binary data automatically. The listener returns mqtt, rejects missing or different subprotocols with HTTP 400, and rejects a wrong path with HTTP 404. Text data closes the connection; binary continuation frames, packets split across messages and multiple packets in one message are supported. Control ping/pong/close frames retain normal WebSocket behavior. These requirements follow MQTT 5 section 6.

Use wss:// behind your TLS proxy for HTTPS pages. Forward /mqtt, HTTP Upgrade and the subprotocol header; protect the plaintext backend listener. Native TLS and WebSocket compression are not provided by this transport.

Backup and migration

The JSONL format used by wkcli db export does not yet support persistent MQTT state: ClientID/UID bindings, subscriptions, unfinished exchanges, Wills, shared replay and capacity records. Export refuses sources containing that state before creating or overwriting the output directory. Disabling MQTT or waiting for Session expiry does not remove this restriction.

Preserve the source and use native backup and restore with matching versions. Do not delete MQTT tables, replay or recovery records to bypass refusal; they may still retain delivery obligations or identity bindings.

Quotas

BoundaryDefaultOperational meaning
Per-session logical backlog10000 messages / 64 MiBSlow ACKs, offline delivery and replay retain responsibility; not physical disk usage
Durable QoS 1 window64Further limited by the client's Receive Maximum
Per-node shared storage reservation8 GiBLogical reservation for shared originals and future replay copies
Aggregate cluster replica reservation64 GiBEvery storage node must agree, including nodes with MQTT listener disabled
Inbound packet1 MiBIncludes encoding overhead; client outbound size limit is separate
Offline session lifetimeAt most 24 hoursConfigurable downward; requests exceeding configured/hard limits fail

A body shared by multiple sessions is counted once per retaining replica. Ordinary history, WAL, compaction and physical amplification are outside these logical limits. Exhaustion restricts new responsibility while preserving accepted replay and uncertain outcomes. Reservation retires only after proof; deleting data or closing a connection cannot establish reclamation.

For large groups, observe member count, online client count, message rate, ACK latency, backlog and disk headroom together. A 100,000-member group does not imply 100,000 online connections. Demonstrations or partial results cannot become general capacity guarantees.

Diagnose by symptom

SymptomFirst checks
TCP refusal/timeoutMQTT enabled, listener, TLS proxy and network; browser incorrectly using TCP
WebSocket handshake rejectedExact path, case-sensitive mqtt subprotocol, TLS proxy Upgrade forwarding
CONNECT rejectedMQTT 5, UID/token/device category, ClientID, exactly one wk.device_flag
SUBACK rejectionCanonical base64url, own inbox, group membership, unsupported wildcards
Publication rejected/connection closeswk.client_msg_no, reserved properties, retain/QoS, permission, size and quotas
Successful PUBACK but no receptionReceiver connection/subscription, permission, QoS, expiry, window and ACK; commit is not reception
Reconnect lacks subscriptionsOriginal UID/ClientID/namespace, clean=false, nonzero expiry, Session Present
Duplicate messagesExpected with QoS 1; inspect MessageID deduplication and changed application keys on retry
Missing WillNormal DISCONNECT cancellation, delay/detection, current permission, generation, uncertain responsibility
CONNECT fails after partitionOld Owner isolation evidence, communication, Slot authority and recovery; avoid endless reconnect pressure

Retain time bounds, node, redacted ClientID, topic, Reason Code, Session Present, MessageID and public metrics. Do not log tokens or sensitive payloads. Business retries follow the idempotency contract.

Public observations

With metrics enabled, inspect /metrics for wukongim_mqtt_subscription_closures_total, wukongim_mqtt_storage_bytes, wukongim_mqtt_storage_events_total, wukongim_mqtt_owner_work and wukongim_mqtt_consumer_work. Counters can count repeated attempts; they are not unique connection/message counts or reclamation proof.

Correlate metrics and client observations before reading bounded logs. See Health and Monitoring and Troubleshooting.

On this page