MQTT

EdgeMQ accepts data via MQTT in addition to HTTP. IoT devices, sensors, and gateways can publish telemetry using any standard MQTT client library, and EdgeMQ handles WAL buffering, schema-aware Parquet conversion, and automatic S3 delivery.

MQTT is available on all plans. Connection limits, QoS levels, and feature availability vary by plan — see pricing for details.

How It Works

MQTT Client ──CONNECT──▶ EdgeMQ Endpoint ──▶ WAL ──▶ S3
             ──PUBLISH──▶ (wss:// or mqtts://)      ├── segments/
                                                      ├── parquet/
                                                      └── views/
  1. Your device connects to your EdgeMQ endpoint via MQTT (WebSocket or TCP)
  2. It authenticates using your API key in the MQTT password field
  3. Each PUBLISH message is written to the WAL with the MQTT topic preserved
  4. Sealed segments are compressed and shipped to your S3 bucket
  5. Materialized views can filter and transform data by MQTT topic

MQTT data flows through the same pipeline as HTTP — same WAL, same renderers, same S3 destination. You can use both protocols on the same endpoint simultaneously.

Connecting

WebSocket is the recommended transport for production use. It works through Cloudflare's edge network, providing DDoS protection and global TLS termination on the same hostname as your HTTP endpoint.

wss://<your-endpoint>.ingest.edge.mq/mqtt
import paho.mqtt.client as mqtt
import json

client = mqtt.Client(transport=class="t-string">"websockets")
client.ws_set_options(path=class="t-string">"/mqtt")
client.username_pw_set(class="t-string">"my-device", class="t-string">"<your-api-key>")
client.tls_set()
client.connect(class="t-string">"<your-endpoint>.ingest.edge.mq", class="t-number">443)

client.publish(class="t-string">"sensors/temperature", json.dumps({
    class="t-string">"device_id": class="t-string">"sensor-class="t-number">42",
    class="t-string">"temp": class="t-number">22.5,
    class="t-string">"unit": class="t-string">"celsius"
}))
client.disconnect()

Node.js note: Node.js 22+ negotiates HTTP/2 by default when connecting to HTTPS servers. Since the ws package only supports WebSocket upgrades over HTTP/1.1, you need to set ALPNProtocols: ['http/1.1'] on the HTTPS agent as shown above. Python, Go, and browser clients are not affected.

MQTT-over-TCP (advanced)

Native MQTT over TCP/TLS on port 8883 is available for constrained devices that cannot use WebSocket (e.g., microcontrollers, embedded systems).

mqtts://<your-endpoint>.ingest.edge.mq:8883
Note

WebSocket (wss://) is recommended for most deployments as it includes DDoS protection and global TLS termination. TCP is best suited for embedded devices with limited networking stacks. If you need a direct IP connection for firewall allowlisting, contact us.

Arduino (PubSubClient):

#include <WiFiClientSecure.h>
#include <PubSubClient.h>

WiFiClientSecure espClient;
PubSubClient client(espClient);

void setup() {
  WiFi.begin(ssid, password);
  client.setServer(class="t-string">"<your-endpoint>.ingest.edge.mq", class="t-number">8883);
  client.connect(class="t-string">"sensor-class="t-number">42", class="t-string">"device", class="t-string">"<your-api-key>");
  client.publish(class="t-string">"sensors/temperature", class="t-string">"{\"temp\": class="t-number">22.5}");
}

Authentication

MQTT uses the same API keys as HTTP. Pass your API key in the MQTT CONNECT password field:

MQTT FieldValueNotes
usernameAny stringUsed as a display name in metrics (not for authentication)
passwordYour API key (ing_live_...)Same key used for HTTP X-API-Key header
clientIdDevice identifierUsed for connection tracking; should be unique per device

The API key must have the Ingest scope. Keys without this scope will receive a connection refused response.

See Authentication for details on creating and managing API keys.

Topics

MQTT topics are preserved as metadata in the WAL and exposed to materialized views. Use topics to organize your data by device type, location, or data category.

Recommended topic patterns:

sensors/{device_id}/temperature
devices/{device_type}/{device_id}/status
factory/{line}/{metric_type}
vehicles/{vin}/telemetry

Topic constraints:

  • Maximum length: 65,535 bytes (UTF-8)
  • Must not be empty
  • Must not contain null bytes

Using Topics in Views

When you define a materialized view, you can reference the MQTT topic using the topic metadata field:

Filter by topic:

name: temperature_readings
version: 1
format: json

definition:
  columns:
    - name: device_id
      expr:
        json_path: "$.device_id"
      type: varchar

    - name: temperature
      expr:
        json_path: "$.temp"
      type: double

    - name: mqtt_topic
      expr:
        meta: topic
      type: varchar

  transform:
    where:
      fn: like
      args:
        - meta: topic
        - lit: "sensors/%/temperature"

  output:
    format: parquet
    partition_by:
      - name: dt
        expr:
          fn: date
          args:
            - meta: timestamp_ms
        type: date

For HTTP-ingested data, topic is NULL. Views that reference topic work with both HTTP and MQTT data — HTTP rows simply don't match topic-based filters.

QoS Levels

QoSNameSupportedBehavior
0At most onceYesFire-and-forget. Message written to WAL, no acknowledgment. Same guarantee as HTTP 202.
1At least onceYes (Pro+)PUBACK sent after message is durably appended to WAL via writev. Client may retry if no PUBACK received.
2Exactly onceNoNot supported. Connection closed if attempted.

For most IoT telemetry (sensor readings, status updates, metrics), QoS 0 is sufficient. QoS 1 is useful when you need confirmation that the message reached the WAL.

Supported MQTT Packets

PacketSupportedNotes
CONNECTYesAuthenticate via API key in password field
PUBLISHYesQoS 0 and QoS 1. Topic + payload written to WAL
PINGREQYesKeepalive mechanism. Server responds with PINGRESP
DISCONNECTYesClean connection teardown
SUBSCRIBENoEdgeMQ is an ingestion pipeline, not a message broker
UNSUBSCRIBENoNot applicable (no subscriptions)

EdgeMQ supports both MQTT v3.1.1 and v5.0 CONNECT packets.

What MQTT Cannot Do

EdgeMQ's MQTT support is designed for data ingestion (device-to-cloud telemetry). It is not a full MQTT message broker:

  • No SUBSCRIBE — devices cannot receive messages from EdgeMQ via MQTT
  • No retained messages — use S3 queries for latest values
  • No will messages — detect device absence by querying for missing data
  • No device-to-device messaging — no topic routing or fan-out

If you need command/control (cloud-to-device messaging), use a dedicated MQTT broker (Mosquitto, HiveMQ, EMQX) alongside EdgeMQ for the ingestion side.

MQTT is available on all plans. Connection limits, QoS levels, and topic ACLs vary by plan — see the pricing page for details.

Segment Format

MQTT messages are stored in WAL segments using FMT=3 frames. The 17-byte header is identical to HTTP frames; the payload is prefixed with the MQTT topic:

Payload structure (FMT=3):
[TOPIC_LEN u16 BE][TOPIC UTF-8][JSON PAYLOAD]

Existing segment parsers that handle FMT=0 frames will read FMT=3 frames without error — the payload includes the topic prefix as part of the raw bytes. See Segment Format for the full frame specification.

Testing with the Data Generator

The ingest-data-gen tool supports MQTT transport for testing:

# Generate IoT sensor data and publish via MQTT WebSocket
node index.js \
  --transport mqtt \
  --mqtt-url wss://<your-endpoint>.ingest.edge.mq/mqtt \
  --api-key $API_KEY \
  --view-schema iot_sensors \
  --mqtt-topic "sensors/{schema}/{version}" \
  --interval 1 \
  --lines 10

FAQ

Can I use MQTT and HTTP on the same endpoint?

Yes. Both protocols write to the same WAL and use the same S3 destination. You can send some data via HTTP POST and other data via MQTT PUBLISH — they are interleaved in the WAL based on arrival order.

Does MQTT use the same API keys as HTTP?

Yes. The same API key works for both protocols. Pass it in the X-API-Key header for HTTP, or in the MQTT CONNECT password field.

How do I know if a message was delivered?

For QoS 0: a successful TCP/WebSocket send means the message reached the ingest node. Durability is confirmed when the segment's commit marker appears in S3.

For QoS 1: the PUBACK response confirms the message was appended to the WAL. Full S3 durability is confirmed by the commit marker.

What payload format should I use?

JSON is recommended for compatibility with materialized views. Binary payloads are accepted but cannot be parsed by views — they are stored as opaque bytes.