Skip to Content

Rules, actions and the wire out to everything else

the broker opens the envelope
EMQX Guide · Part 5

Rules, actions and the wire out to everything else

Up to here EMQX has been a very careful post office: a message goes in on one topic and comes out to whoever subscribed to it, unchanged. The Rule Engine is the point where that stops being true. You write a few lines of SQL, and the broker starts reading the messages passing through it — picking fields out of them, filtering on a condition, renaming things, and then doing something: publishing the result somewhere else, printing it for debugging, or pushing it out to an HTTP service that has never heard of MQTT. This part covers the SQL, the two built-in actions, and the Connector-and-Sink pair that carries data off the broker entirely.

3 stages
Source, transformation, action. Every rule you ever write is those three
2 connectors
HTTP and MQTT — all the Open Source edition supports
1 pair of quotes
The topic in FROM must carry them. Double or single, but not none
Why the broker reads it

A forwarder, or the hub of a pipeline

Mosquitto hands every topic's messages to its subscribers untouched. Nothing gets picked over, nothing gets reshaped. That is a perfectly good broker and for a lot of setups it is all you need.

It stops being enough the moment you want the broker itself to make a decision. Strip the noise out of a sensor reading before anything downstream sees it. Move a reading from the topic the device insists on publishing to, onto the topic your dashboard is subscribed to. Send a notification the second a client drops off the network. None of that is forwarding. All of it is processing the data itself, and that is what EMQX's Rule Engine is for.

In plain terms

A post office moves sealed envelopes. It does not care what is inside one, and that is the whole point of a post office. What you have now is a mail room that opens the envelope, reads the first line, throws away the junk, copies the useful part onto a second sheet, and drops that in a different pigeonhole. Same building, same letters. It has just started reading them.

At its core the Rule Engine is three stages, source → transformation → action, and it is worth fixing those in your head before any syntax, because every problem you will have later belongs to exactly one of them.

  • Data source — where the rule gets its data. It can be MQTT messages, from one topic or several; an MQTT event such as $events/client_connected; or data handed back by Data Integration.
  • Transformation — the rule SQL decides which records to take, which fields to keep, and what conditions and functions to apply. FROM names the source, WHERE adds the condition, and SELECT decides the output.
  • Action — what happens once a rule matches. EMQX 5.8.9 offers republish, console (print to the console or the log) and Forwarding to Sinks, which sends it to an external system.
flowchart LR
  A["MQTT messages
from one topic or several,
such as t/#"] --> S["The rule SQL
FROM names the source
WHERE adds the condition
SELECT decides the output"] B["An MQTT event,
such as
$events/client_connected"] --> S C["Data handed back
by Data Integration"] --> S S --> D["Republish
to another MQTT topic"] S --> E["Console output
to the console or the log"] S --> F["Forwarding to a Sink,
out to an external system"]
Source, transformation, actionThree sources down the left all feed one SQL box in the middle, and three actions lead away from it on the right. Every rule in this article is one path through that shape.

Where do rules live? EMQX 5.x files them under Data Integration, and you create or edit one from the Dashboard sidebar at Integration → Rules. Note that the Rule Engine handles more than messages: it also handles events, such as a client connecting or disconnecting.

The words, before you meet them in a form field

Eleven terms carry the whole of this part. They come from three different chapters of the source guide and they sound alike on purpose, which is exactly why they get mixed up.

TermPlain EnglishIn one line
Rule Enginethe rule processorThe engine that describes data flow handling in SQL
Data Sourcewhere a rule reads fromThe topic, event or external data a rule reads
SELECT / FROM / WHEREwhat to pick / where from / on what conditionThe three main clauses of the SQL
Actionwhat the rule does nextThe output behavior that runs once a rule matches
MQTT Eventa broker-side eventConnect, disconnect, subscribe and the like; the topic starts with $events/
Data Integrationthe integration areaWhere EMQX 5.x groups rules, sources, connectors and sinks together
Republishpublish it again elsewherePublishes the rule result to another MQTT topic
Console Outputprint it to the consolePrints the result to the console or the log, for debugging
${key}placeholderReferences a field of the rule output inside an action parameter
ConnectorconnectionThe low-level connection channel to an external system
Sinkoutput targetThe external target EMQX writes its data out to
The one distinction to hold on to: a rule is the SQL — the data source plus the transformation. An action is what the rule does with the result. They are configured on the same page and people talk about them as one thing, but when something breaks you will need to know which of the two you are looking at.
The three clauses

Rule SQL: SELECT, FROM, WHERE

If you have written a database query before, this will look familiar and then behave slightly differently, so read it as its own thing. The basic shape is:

SELECT <fields and expressions> FROM <data source> [WHERE <condition>]

The square brackets around WHERE mean it is optional. Plenty of useful rules never have one.

In plain terms

Think of a stockroom. FROM is which shelf you are standing at. WHERE is the sentence that decides whether a particular box comes down off it. SELECT is what you write on the label of the box you carry out. Three separate decisions, made in that order, and each one can be got wrong on its own.

FROM — naming the source

FROM comes in one of two main shapes.

A topic. For example, this handles every message whose topic matches t/#:

SELECT * FROM "t/#"

Several topics are separated with commas:

FROM "t/1", "t/2"

A topic must be wrapped in double or single quotes. That is the single most common thing people leave off, and it is why it has its own line in the summary at the top of this page.

An event. For example, this handles the event of a client connecting successfully:

SELECT clientid FROM "$events/client_connected" WHERE clientid = 'c1'

Event topics start with $events/ and cannot be read with an ordinary subscription. They are not hidden topics you could have subscribed to if only you had known the name; they exist for a rule's FROM clause and nowhere else. If you want that data outside EMQX, a rule has to republish it for you.

WHERE — the extra filter

WHERE narrows what the rule acts on. A field in the condition can be either of two things:

  • Metadata, such as clientid or username.
  • A field inside the payload, reached with dot syntax such as payload.x.y — as long as the payload is JSON or a Map.

Conditions combine with and and or.

SELECT — the output, and renaming

SELECT picks the fields to output, and it can rename them on the way through:

SELECT clientid, payload.clientid as myclientid FROM "t/#"

Read that one twice: there are two different clientid values in play. The bare clientid is the metadata one — who actually published the message. The payload.clientid is whatever the device wrote inside its own JSON, which may be something else entirely. Renaming it to myclientid is how you keep both without a collision.

The output of the SQL is a JSON object. Once the SQL has run, a later action can reference its fields with ${field}. That handoff — SQL produces a named field, action reads it back by name — is the joint that the whole of the next section turns on.

flowchart TD
  A["A message is published
on topic t/1"] --> B{"Does the topic match
the FROM pattern?"} B -->|"no"| X["The rule never fires.
Nothing is logged,
nothing is wrong"] B -->|"yes"| C{"Is there a WHERE clause,
and does it hold?"} C -->|"no, it does not hold"| X C -->|"yes, or there is none"| D["SELECT builds the output:
a JSON object of
the fields you named"] D --> E["The action reads those fields
back by name, as placeholders"]
One message through one ruleBoth refusals lead to the same box on the left, which is the point: a rule that does not fire looks exactly like a rule that is not there. Neither the topic test nor the condition test leaves a trace.

Expressions and the escape hatch

Expressions support arithmetic (+, -, *, div, mod), comparison (=, <>, <, > and so on) and logic (and, or), on top of a long list of built-in functions.

There is also a separate FOREACH statement that can emit several results at once — splitting an array of sensor readings in the payload into one message each, for instance. It is genuinely useful and it is not a beginner's tool. Learn it when you get to a payload that needs it, not before.

Do not open with #. A FROM of # works, but then every single message that reaches EMQX goes through rule matching, and that costs performance. EMQX recommends a narrower topic pattern. Start specific; widen it only when you find you have to.
Fields you can reach

Two layers of fields, and the functions over them

Which fields a rule can reference depends on which data source it reads. Get this wrong and the symptom is an empty output rather than an error message, which is a lot more confusing than a crash would have been.

For an MQTT message, the metadata gives you these common fields:

FieldWhat it means
topicThe source topic
qosThe message's QoS level
clientidThe publisher's client ID
usernameThe publisher's username
payloadThe payload. When it is JSON, dot syntax reaches inside it
peerhostThe publisher's IP address
timestampWhen the event fired, in milliseconds

An event source such as $events/client_connected adds fields of its own: clientid, username, keepalive, is_bridge and connected_at. And $events/client_disconnected also carries reason, which says why the client dropped — the single most useful field in the whole set when you are chasing a device that keeps falling off. The official documentation lists every event and its fields.

Metadata and payload are two separate layers. clientid is metadata; payload.clientid is a field the device happened to put inside its own JSON. They are not the same field and one existing does not mean the other does. When a rule's output comes back empty or with the wrong values, this is the first thing to check, ahead of anything cleverer.

Built-in functions

Functions can be used inside both SELECT and WHERE. The common groups:

Mathabs, floor, ceil, round, power, sqrt and more
Typeis_array, is_bool, is_null and more
Stringupper, lower, tokens, concat and more
Othernow_timestamp, now_rfc3339, uuid_v4, date conversion, mapping and array operations, and the advanced jq processor

They go straight into the clause, and the as rename works on the result:

SELECT upper(clientid) as cid FROM "t/#"
If the payload is not JSON, dot syntax will not reach into it. You have to cast it or run it through a function first. For non-JSON formats generally, EMQX has a Schema Registry guide — that is the proper route rather than trying to force the dot syntax.
Your first rule

From t/# to a/1, with a test before it goes live

The fastest way to learn this is the republish rule from the official EMQX tutorial: everything arriving on t/# gets a copy published to a/1. It does nothing useful, which is exactly what you want the first time — every part of the mechanism is visible and nothing important breaks if it goes wrong.

  1. Step 1

    Open the rules page

    In the EMQX Dashboard sidebar click Integration → Rules, then Create at the top right. The Create Rule page opens, with an SQL Editor filling most of it and an actions panel down the right-hand side.

  2. Step 2

    Fill in the rule details and write the SQL

    Give the rule a name and a note, then type this into the SQL Editor:

    SELECT * FROM "t/#"

    That selects every message whose topic matches t/#. The quotes around the topic are not optional.

  3. Step 3

    Test the SQL with Try It Out, before anything is created

    Turn on the Try It Out switch on the Create Rule page. Choose the matching Data Source, then fill in the simulated data — Client ID, Username, Topic, QoS, Payload and so on — and click Run Test.

    You are looking for Test Passed, and for Output Result on the right to show what the SQL actually produced. Read that output rather than glancing at it. It is the only place you will see the shape of the JSON object your action is about to receive, and half the problems in the troubleshooting section further down are visible here first.

  4. Step 4

    Add an action

    Click Add Action on the right and choose Republish. Set Target Topic to a/1, QoS to 0 and Retain to false. Then, in the Payload field, type the placeholder:

    ${payload}

    That keeps the original payload intact on the copy. Then click Add to return to the Create Rule page.

  5. Step 5

    Create the rule and trigger it

    Back on the Create Rule page, click Create at the bottom to finish. Now prove it: subscribe to a/1, publish a message on topic t/1, and the republished message should arrive on a/1.

Subscribe first, then publish. MQTT delivers to whoever is subscribed at the moment the message arrives, so a subscription set up afterwards sees nothing and the rule looks broken when it is not. Have the a/1 subscription open and waiting before you publish to t/1.

That is the whole loop, and it is the loop you will run for every rule you ever write: SQL, test it in place, attach an action, create, trigger it by hand.

Where the result goes

Republish, Console Output, and the same thing in a config file

Data that stops at the SQL output is data you cannot use. The action is what turns the result into behavior. In a rule's actions you can configure one or more actions, and the rule runs them in order.

EMQX 5.8.9 has three built-in actions:

  • Message Republishing — publishes the rule result to an MQTT topic.
  • Console Output — prints the result to the console or the log, mainly for debugging.
  • Forwarding to Sinks — hands the result to a Sink, which is the next section.

The data an action uses comes from the SQL result. With placeholder syntax such as ${field} you push a field of the rule output into an action parameter — putting the payload or clientid the SQL selected into the republish topic or payload, for example.

Republish, parameter by parameter

Republish is the everyday “reshape the data and reroute it” action. In the official example, messages matching t/# are republished to a/1.

The important thing about it, and the thing most people assume wrongly: republishing does not intercept the original message. A client subscribed to t/1 still receives it exactly as usual. It simply gets one extra copy on a/1. Nothing is taken away from anybody.

ParameterWhat it means
TopicThe republish target topic. Use ${…} to build a dynamic topic
QoSThe QoS level of the republished message
RetainWhether to publish it as a retained message
PayloadThe republished payload template, commonly ${payload}
MQTT 5.0 propertiesOptional: Payload Format, Expiry, Content Type, Response Topic, Correlation Data
Direct DispatchDelivers straight to subscribers and avoids rule recursion

The MQTT 5.0 properties live behind a switch labeled MQTT 5.0 Message Properties on the action form. Turning it on lets you set Payload Format Indicator, Message Expiry Interval, Content Type, Response Topic, Correlation Data and other properties. Leave it off until you have a reason.

The recursion trap

Direct Dispatch deserves its own paragraph, because the failure it prevents is the one genuinely nasty mistake available in this whole chapter. Turning it on delivers the message straight to subscribers, which keeps it from triggering other rules and falling into recursion.

Here is the shape of the accident. Your rule reads FROM "t/#". You set the republish target to something that also matches t/# — t/2, say. The republished message now matches the rule that produced it, so the rule fires again, republishes again, and it does not stop.

flowchart TD
  A["The rule reads FROM t/#
and republishes"] --> C{"Does the target topic
itself match t/#?"} C -->|"a/1, outside the pattern"| D["One extra copy on a/1.
Subscribers of t/1 still
get the original"] C -->|"t/2, inside the pattern"| E["The republished message
matches the same rule again"] E --> F["It fires again,
without end"] F --> G["Turn on Direct Dispatch,
or change the target topic"]
Why the target topic matters more than it looksThe safe branch ends immediately at the box on the left. The unsafe one runs down three more boxes before it reaches a fix, and in the meantime the loop is live on your broker.

Console Output, and where the text comes out

The Console Output action prints the rule result to the console or the log, in the form [rule action] ruleID + Action Data + Envs. Where you read that depends entirely on how EMQX was started:

ForegroundStarted in the foreground, which is the Docker default — the output goes to the console
systemdStarted under systemd — the output goes to the journal, which you read with journalctl
The official docs warn plainly that using console in production can slow things down. It is a debugging tool. Add it while you are working out why a rule is not doing what you meant, and take it off again when you have the answer. In production, republish or send the data to a Sink.
In plain terms

Console output is the receipt printer next to the till. Perfect while you are working out why the day's takings do not add up — you can watch every line come out. Nobody leaves it running through the Christmas rush, because printing every transaction is work the till has to do instead of serving the queue.

The same rule, declared in a config file

Rules and actions do not have to be clicked together in the Dashboard. They can also go into emqx.conf, where they take effect before EMQX starts — which is what you want if the rule is part of a deployment rather than something you set up by hand on one machine.

They are defined under the rule_engine namespace with rules.<id>, where the id is a name you choose. This is the official example:

rule_engine {
rules.my_republish_rule {
sql = "SELECT qos, payload.x as y FROM \"t/a\""
actions = [
{
function = republish
args = {
topic = "t/b"
qos = "${qos}"
payload = "y: ${y}"
}
}
]
}
}

The braces carry the structure, so indenting the nested lines in your own file is for your eyes only and changes nothing — which is why they are shown flush left here. Two details are worth pointing at. The inner quotes around the topic are escaped as \"t/a\", because the whole SQL string is already inside quotes — and the topic still has to be quoted, exactly as in the Dashboard. And ${qos} and ${y} are the same placeholders as in the UI, referencing fields of the SQL output.

Trace that example end to end, because it shows the handoff in miniature. Topic t/a receives {"x":1}. The rule picks out qos and renames the payload's x to y. Then it republishes to t/b with the payload y: 1. The y in the payload template is the y that SELECT invented one line earlier.

Two more config shapes are worth knowing. To name an external integration Sink as a rule's action, put the bridge ID straight into the actions array, such as "mqtt:my_egress_mqtt_bridge". And a console action is written as:

actions = [{function = console}]

Event rules can be declared in a config file just the same — a client coming online, $events/client_connected, for instance.

Off the broker entirely

Connectors and Sinks

Republish moves data to another MQTT topic, which is still inside EMQX. Getting it out to something that has never heard of MQTT — a phone notification service, an SMS gateway, a cloud API, a relational database — is a separate problem, and EMQX 5.x splits it into two halves.

  • A Connector is the low-level connection channel from EMQX to an external data system. It is responsible only for how to connect, never for which data gets processed.
  • A Sink decides which external target a rule's output goes to and in what format. It sits in the rule's Action Outputs, where you pick a Connector and fill in its parameters.
In plain terms

The Connector is the phone line to the warehouse: the number, and whatever you have to say to get past the switchboard. The Sink is the standing instruction about what to tell them when you call. One line, several instructions — and if the warehouse changes its number, you update the line once and every instruction carries on working.

That split buys you three concrete things, and the third one is the one you will appreciate at two in the morning:

  • Settings stay separate. Connection details — server address, credentials — sit apart from data settings such as rules, topic mapping and payload templates. Changing the connection does not affect the rules.
  • Reuse. An external system that several Sinks and Sources talk to needs only one Connector.
  • Observable state. The Dashboard shows each Connector's connection state — Connecting, Connected, Disconnected, Inconsistent — which tells you in one glance whether a problem is the connection or the rule.
flowchart LR
  R1["Rule one
its own SQL"] --> K1["Sink: HTTP Server,
method POST"] R2["Rule two
different SQL"] --> K2["Sink: HTTP Server,
a different body"] K1 --> C["One Connector: my_httpserver
address and credentials only.
State: Connected"] K2 --> C C -->|"data out"| OUT["The external
HTTP service"] OUT -->|"data in"| C C --> SRC["A Source, bringing data
from outside into EMQX"] SRC --> R3["A rule inside EMQX,
reading what came in"]
One connector, both directionsThe two sinks feed the connector box from the left. Out of it, the arrow marked “data out” reaches the external HTTP service; the one marked “data in” runs back the other way, and that inbound path carries on into the Source and then to a rule inside EMQX. Three things hang off that single box — two sinks and one source — which is why a connector in use will not delete.

What you can actually reach, and on which edition

EMQX 5.8.9 offers many external targets you can use as a Sink, but the Open Source edition can reach only a few of them. The official Connector documentation states that EMQX Open Source supports only two connectors, HTTP and MQTT. Kafka, PostgreSQL, MySQL, AWS and Azure targets are Enterprise edition features.

Read the edition column before you plan anything around a target, not after.

TargetBest forEdition
WebhookSending MQTT data straight to an HTTP service, with no rule processingOpen Source
HTTP ServerFiltering or reshaping with a rule before sending it out over HTTPOpen Source
MQTT BrokerBridging across brokers, which Part 6 of this series coversOpen Source
KafkaPushing an event stream into a Kafka message queueEnterprise
PostgreSQL/MySQLWriting device data straight into a relational databaseEnterprise

Webhook or HTTP Server

Both are Open Source, both send data over HTTP, and the difference between them is mainly whether a rule (SQL) processes the data first — mainly, and not only, because the two also differ in how much of the outgoing request you get to build.

Webhook — simpler

No rule processing. It suits a simple integration where you only need to hand the message you received to an external HTTP endpoint, and the target is clear. To send a single status notification to one API, this is the lightest option, and the official advice is to use it directly when no rule processing is needed.

HTTP Server Integration — more advanced

The Rule Engine extracts and transforms the data first, then it goes to the HTTP service. It can take a rule's output and build the request header, the body and even the URL from it. Create the HTTP Server Connector, with its URL and method, then add the rule and the HTTP Sink action.

An HTTP Sink can use POST, PUT and other methods, and you can set the request body and the credentials — the ${var} placeholder works there too. EMQX shows each Sink's connection state and request count, so confirming the data is actually going out takes a glance rather than an investigation.

Hands-on: t/# out to a local HTTP service

This follows the official tutorial's HTTP Server Sink example: send the MQTT messages on t/# to a Flask HTTP service running on the local machine.

  1. Step 1

    Create the HTTP Server Connector

    In the Dashboard sidebar go to Integration → Connectors, click Create, and pick the HTTP Server connector. Name it my_httpserver and set the URL to:

    http://localhost:5000

    Click Test Connectivity before you save. Ten seconds here saves you debugging a rule that was never the problem.

  2. Step 2

    Create the rule

    Go to Integration → Rules → Create and enter the SQL:

    SELECT * FROM "t/#"
  3. Step 3

    Add the HTTP Sink action

    Click + Add Action and set Type to HTTP Server. Pick my_httpserver from the Connector dropdown, set Method to POST, and fill in the name and description.

  4. Step 4

    Create the rule

    Back on the Create Rule page, confirm the Sink is listed under Action Outputs, then click Create to finish. If the Sink is not listed there, the action was not saved and nothing further will work.

  5. Step 5

    Trigger it with a message

    Use MQTTX to publish to t/1, for example:

    {"msg":"hello HTTP Server"}

    Then look at the Flask server for the POST request it received. That request landing is the proof; the Dashboard's request count on the Sink is the second opinion.

Changing a Connector interrupts traffic. Updating a Connector that is in use reloads its Sinks and Sources, which can interrupt the flow of data briefly. Make that kind of change off peak, not while the thing it feeds is being watched.
And a Connector that is in use will not delete. That is deliberate, and it is a safety net rather than an obstacle — you cannot cut the line out from under a rule that is still using it. Delete the Sinks and Sources that depend on it first. The Dashboard lists the Sinks attached to each Connector, so you can see what has to go before it will.
On credentials in a Sink: an HTTP Sink's credentials are part of the Connector or the Sink configuration, which is exactly where they belong — not typed into a rule's SQL or a payload template where they would travel with the data. Keep the same discipline as everywhere else in this series: the connection settings hold the secret, the rule holds the logic.
When nothing arrives

Working backwards from silence

Almost every failure in this part presents the same way: you publish a message and nothing happens at the other end. No error, no red box, just silence. The work is figuring out which of the three stages swallowed it.

flowchart TD
  A["You published,
and nothing arrived
at the other end"] --> B{"Is the rule enabled?"} B -->|"no"| B1["Enable it"] B -->|"yes"| C{"Does the FROM pattern match
the topic you published to?"} C -->|"no: wildcard or letter case"| C1["Fix the topic pattern"] C -->|"yes"| D{"Does Try It Out
report Test Passed?"} D -->|"no"| D1["The SQL is the problem.
Check the field names"] D -->|"yes"| E{"Republish, or a Sink?"} E -->|"republish"| F["Check the target topic and QoS,
and that the action was saved"] E -->|"sink"| G["Check the Connector reads Connected,
and the URL and Method"]
Four questions, in this orderEvery no branch hangs to the left of the trunk and ends in one box. The order matters more than the questions do: checking the Connector before you have confirmed the rule fires at all is how an afternoon disappears.

The rule side

  1. 1

    The rule never fires

    Check that the topic pattern in FROM matches the topic being published to — watch the wildcards and the letter case, both of which are exact. Check that the rule is enabled. And check the event name is not misspelled: $events/client_connected, for example, has to be that string precisely.

  2. 2

    The SQL output is empty, or the fields are wrong

    Check the official field reference and confirm the field name actually exists on that data source. Remember that metadata and payload are two separate layers of fields — a name that exists on one is not automatically available on the other.

  3. 3

    The payload is not JSON but you want a field inside it

    Dot syntax only works on JSON or a Map. For anything else, use a cast function, or run it through a Schema first.

  4. 4

    The rule matches but the action does not run

    Use Test the Rule in Try It Out to simulate the matching event or publish, or publish a test message directly. Then read the Action error in that run's log — the rule and the action fail separately and the log tells you which one gave up.

The republish and console side

  1. 5

    No republished message arrives

    Four things, in order: that the rule is enabled; that the SQL's FROM pattern matches the topic you publish to; that the action was actually saved; and that the target topic and QoS are right. Simulate it first with Test the Rule rather than guessing.

  2. 6

    Nothing shows up in the console

    This is nearly always about where you are looking rather than what EMQX is doing. When EMQX starts in the background the output goes to the journal, which you read with journalctl. When it runs in the Docker foreground, look at the console.

  3. 7

    The payload is not in the format you expected

    The republish payload is a string template, so you have to type out the shape you want. That means ${payload} on its own for a straight copy, or text mixed with ${field} for anything else. It will not guess.

  4. 8

    The rule triggers itself and recurses

    If the Republish target topic also matches the same rule's FROM, it fires again without end. Avoid that with Direct Dispatch, or change the target topic so it falls outside the pattern.

The connector and sink side

  1. 9

    The external HTTP service receives nothing

    Check that the Connector state reads Connected, that the URL and Method are correct, that the Sink has actually been added to the rule, and that the rule is enabled. The Connector state is the fastest of those to read, so start there.

  2. 10

    Kafka, PostgreSQL and MySQL connectors are not in the list

    They are not missing and nothing is broken. Open Source supports only the HTTP and MQTT connectors; those targets are Enterprise.

  3. 11

    Data stops after you change a Connector

    Updating a Connector that is in use reloads its Sinks and Sources, which can interrupt traffic briefly. Expected behavior, which is why the advice is to make the change off peak.

  4. 12

    A Connector will not delete

    A Connector that is in use cannot be deleted. Delete the Sinks and Sources that depend on it first; the Dashboard lists the Sinks attached to it, so you can work out what is holding it.

Questions people ask

The ones that come up every time

What is the difference between a rule and an action?
A rule is made of SQL — the data source plus the transformation — and an action, the output behavior. The SQL decides which data to handle and how to pick it; the action decides where the result is published, printed or sent.
Can I subscribe to the $events/… event topics directly?
An ordinary client cannot subscribe to MQTT event topics. They exist for a rule's FROM clause, for example $events/client_connected. To receive event data from outside, republish it with a rule, or subscribe to EMQX's system topics instead.
What happens if the SQL's FROM uses a broad #?
It works, but every message that reaches EMQX then goes through rule matching, which costs more in performance. EMQX recommends a narrower topic pattern rather than opening with #.
How does “Data Integration” differ from the older 4.x names?
EMQX 5.x is organized around Data Integration — Rules, Connectors, Actions, Sources, Sinks — and Extensions. The names differ from 4.x, which used Modules and Data Bridge. When older documentation uses the 4.x names, remember those are the legacy locations for the same ideas.
Does Republish block the original message?
No. Republish only sends one extra copy to the target topic. A message published to t/1 still reaches the clients subscribed to t/1 as usual.
Is Console Output suitable for production?
No. The official docs mark it as a debugging tool, and heavy output slows things down. In production use republish, or send the data to a Sink.
Can I configure several actions at once?
Yes. A rule's actions is an array, so you can add one action after another in the UI and list them as an array in the config file. The rule runs them in order.
When do I use republish, and when do I use a Sink?
If you are only moving the data to a different topic, use republish. To send it to an external system — a database, HTTP, Kafka, a remote MQTT broker — use Forwarding to Sinks.
What is the difference between a Connector and a Sink?
A Connector is the low-level connection channel: which external system you connect to. A Sink is the setup that sends a rule's output to that target, and it lives in the rule's Action. One Connector can serve several Sinks and Sources.
How do I choose between Webhook and HTTP Server?
If the data needs no rule processing, Webhook is the simplest. If you need to strip noise out first and then redirect it, use an HTTP Server Sink together with a rule. The official advice is to use Webhook directly when no rule processing is needed.
Which external targets can Open Source connect to?
The Open Source edition supports only two connectors, HTTP and MQTT. Kafka, PostgreSQL, MySQL and similar targets are Enterprise edition features.
Do I need Enterprise to send data to a database?
The native database Sinks — PostgreSQL, MySQL and so on — are available only in the Enterprise edition. On Open Source you have two ways round it: use an HTTP Sink against your own database API, or bridge over MQTT to a separate data processing service.
Next

Where to go from here

data both ways

The broker can now read, reshape and send.

Everything so far has pushed data outward. Part 6 turns the arrow around: Sources, which bring data from an external system into EMQX, bridging one broker to another over MQTT, and wiring the whole thing into Home Assistant.

Open the full guide

Part 5 of the WoowTech EMQX Complete Guide series on the Apporo blog.

Adapted from the WoowTech EMQX Complete Guide, produced by WoowTech and released under CC BY 4.0. This adaptation is published by Apporo under the same licence.

Light · Air · Water · Control · apporo

in EMQX
Getting in is not the same as being allowed