Open-source project
rmqtt/rmqtt avatar
rmqtt/rmqtt

RMQTT is a Rust MQTT broker that can also be embedded as a library

MQTT Server/MQTT Broker - Scalable Distributed MQTT Message Broker for IoT in the 5G Era

1,039 stars137 forksRustMIT

At a glance

What is it?
RMQTT is an MIT licensed MQTT broker written in Rust on tokio, supporting MQTT 3.1, 3.1.1 and 5.0 with QoS 0, 1 and 2, a Raft based distributed cluster, 26 built-in plugins and bridging to Kafka, Pulsar and NATS. It ships as a standalone server and as a crate you can embed in your own Rust service.
Who is it for?
RMQTT fits a Rust organisation that wants an MQTT broker it can compile, audit and embed, and that has a device fleet large enough to justify owning the broker rather than renting one, and it fits a team that needs the Kafka, Pulsar or NATS bridging in the same process as the broker. It does not fit a team that needs the widest MQTT ecosystem support, and it does not fit a Java shop that would rather use an existing broker.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A broker you can also link into your own binary

RMQTT is described as a fully open source, highly scalable, highly available distributed MQTT messaging broker for IoT, M2M and mobile applications that can handle millions of concurrent clients on a single service node.

The implementation claims are listed first and they are specific. 100% Rust safe code. Based on tokio. And a protocol list, MQTT v3.1, v3.1.1 and v5.0 support.

The unusual part is the second mode of use. In addition to running as a standalone MQTT Broker or Server, rmqtt also provides a Library Mode, which allows you to embed rmqtt directly into your Rust applications or services.

toml
[dependencies]
rmqtt = "0.21"

The dependency snippet is trivial, which is the point. The broker is a crate, not just a binary, so a Rust service that already owns its connections and its session state can run the broker in the same process rather than as a separate deployment. For a system where every device connects to one place and that place also does other work, that removes a network hop, a second deployment and a second failure domain.

The protocol support covers the three QoS levels, QoS0, QoS1 and QoS2, plus offline message support, retained message support and Last Will message support, each with its own documentation page. The will and offline features are the two that decide whether a broker is usable for a fleet of devices that come and go, because a device that loses connectivity has to either queue its messages on the server or announce its death to its subscribers.

The README is bilingual, with English and a Simplified Chinese version, and the project publishes crates.io and docs.rs badges alongside a GitHub release badge and a DeepWiki link, so the API documentation is generated from the same code as the broker.

The clustering story is Raft with two separate address sets

The distributed cluster feature is documented at docs/en_US/cluster-raft.md, and the multi node Docker run in the README shows the configuration in full, which is the best way to see how it works.

bash
docker run -d --name rmqtt1 -p 1884:1883 -p 8884:8883 -p 11884:11883 -p 6064:6060 \
  -v /app/log/rmqtt/1:/var/log/rmqtt  rmqtt/rmqtt:latest --id 1 \
  --plugins-default-startups "rmqtt-cluster-raft" \
  --node-grpc-addrs "[email protected]:5363" "[email protected]:5363" "[email protected]:5363" \
  --raft-peer-addrs "[email protected]:6003" "[email protected]:6003" "[email protected]:6003"

Two address sets, and they are not redundant. The node-grpc-addrs carry the id, the address and the port 5363, and they are the gRPC links between nodes. The raft-peer-addrs carry the same ids with the port 6003, and they are the Raft consensus addresses.

The split is the interesting design decision. Consensus traffic and the rest of the cluster traffic have different requirements, and putting them on separate ports means a slow or congested gRPC link between nodes does not stall the Raft heartbeat, and a Raft election does not have to queue behind application messages. The id@address:port notation makes the node identity explicit at every address, which is what a consensus membership list needs.

The plugin that makes this work is named on the command line, --plugins-default-startups "rmqtt-cluster-raft", so the cluster layer is a plugin rather than a build-time mode. The README also gives the two other ways to run a cluster, a docker-compose template downloadable from the rmqtt/templates repository and a compose file with static cluster instructions, and a health check endpoint to confirm it came up.

bash
curl "http://127.0.0.1:6066/api/v1/health/check"

The examples directory carries the matching configurations, with examples/cluster-raft-3/ and examples/cluster-raft-5/ for a three node and a five node cluster, and examples/cluster-broadcast/ for the other cluster plugin. The Broadcast plugin is the one that sits alongside Raft in the plugin table, so the cluster has two independent mechanisms, a consensus layer and a broadcast layer, which is the usual arrangement for replicated MQTT where the session state must be consistent but not every message needs to pass through consensus.

Twenty-six plugins in six categories

The plugin system is the architecture, and the README gives the table.

| Category | Plugins | |----------|---------| | Auth | ACL, HTTP Auth, JWT Auth | | Storage | Retainer, Message Store, Session Store | | Cluster | Raft, Broadcast | | Bridge | MQTT (in/out), Kafka (in/out), Pulsar (in/out), NATS (in/out), ReductStore (out), Bridge Origin | | API | HTTP API, WebHook, Sys Topic | | Utility | Counter, Auto Subscription, Topic Rewrite, P2P Messaging, Shared Subscription |

The Auth category shows how authorisation is meant to be handled in an MQTT deployment, and there are three different mechanisms because they solve different problems. The built-in ACL plugin is the local one, the HTTP Auth plugin calls out to a service you run, and the JWT Auth plugin validates a token the client presents. The HTTP one is the common case in a company that already has an identity service.

The Storage category is the three pieces of state a broker has to keep. The Retainer holds retained messages, the Message Store holds messages for offline sessions, and the Session Store holds the session information itself. Splitting them means a deployment that does not need offline messages does not pay for the store, and the retained message handling, which is on the hot path for every publish to a topic with subscribers, is separate from the bulk store.

The Bridge category is the largest and the most interesting, and the ingress and egress distinction is the key. Ingress mode means foreign messages come into the broker, so a Kafka topic becomes an MQTT topic. Egress mode means MQTT messages go out, so an MQTT publish becomes a Kafka record. Doing both for three brokers is eight plugins, and ReductStore is egress only, which is consistent with it being a time series and log store rather than a message bus.

The Bridge Origin plugin is a shared piece of the bridging machinery, which is what keeps the six bridge plugins from each reimplementing the same translation logic.

The Utility category holds the subscription features, and the list in the features section gives the syntax for the less common ones. A shared subscription uses $share/{Group}/{TopicFilter}, an exclusive subscription uses $exclusive/{TopicFilter}, a limit subscription uses $limit/{LimitQuantity}/{TopicFilter} and a delayed publish uses $delayed/{DelayInterval}/{TopicName}. These are protocol extensions with a dollar-prefixed topic, which is the MQTT convention for a server-defined namespace, and they are how a broker implements a queue group, a single-consumer topic and a delayed delivery without the client needing to know which broker it is talking to.

The workspace separates the codec, the network layer and the plugins

The Cargo.toml workspace is where the crate structure is visible.

toml
[workspace]
resolver = "2"
members = [
    "rmqtt",
    "rmqtt-plugins",
    "rmqtt-plugins/rmqtt-*",
    "rmqtt-bin",
    "rmqtt-macros",
    "rmqtt-codec",
    "rmqtt-net",
    "rmqtt-conf",
    "rmqtt-utils",
    "rmqtt-test",
    "rmqtt/examples/*",
]

The split is by concern. rmqtt-codec is the MQTT protocol encoding and decoding, rmqtt-net is the transport, which is where the TLS, WebSocket, WebSocket-TLS and QUIC support lives, since the features list names all four. rmqtt-conf is configuration, rmqtt-utils is shared helpers, rmqtt-macros is the proc macro crate, and rmqtt-bin is the server binary.

The patch section is what makes the library mode work, and it is the part to look at if you are embedding it.

toml
[patch.crates-io]
rmqtt = { path = "rmqtt" }
rmqtt-codec = { path = "rmqtt-codec" }
rmqtt-net = { path = "rmqtt-net" }
rmqtt-conf = { path = "rmqtt-conf" }
rmqtt-macros = { path = "rmqtt-macros" }
rmqtt-utils = { path = "rmqtt-utils" }

Every internal crate is patched to the local path, so a downstream application that depends on rmqtt and wants to reach into its internals builds against the same source rather than against a published version of a sub-crate. The commented-out line for rmqtt-plugin-template shows the intended extension point, a template crate for a new plugin that a downstream project can fork and add to the workspace.

The repository top level listing shows the rest of the build, with a Dockerfile.amd64 and a Dockerfile.aarch64 rather than a single multi-arch one, a Makefile with a release target per architecture, a rmqtt.toml at the root which is the default configuration, a server-subs.js which is a JavaScript subscriber used for manual testing, and examples/ with the three cluster configurations. The Makefile's release targets build a static musl binary per architecture, cargo build -p rmqttd --release --target x86_64-unknown-linux-musl, and the merge target uses docker buildx imagetools create to build a multi-architecture manifest from the two per-architecture images.

Single node, compose, or a static cluster template

The installation section gives three routes and the choice depends on how much you want to configure yourself.

The single node Docker run is one command.

bash
mkdir -p /app/log/rmqtt
docker run -d --name rmqtt -p 1883:1883 -p 8883:8883 -p 11883:11883 -p 6060:6060 \
  -v /app/log/rmqtt:/var/log/rmqtt  rmqtt/rmqtt:latest

Four ports, and the pattern repeats in every configuration, so they are worth decoding. 1883 is the plain MQTT port. 8883 is the TLS one. 11883 is the WebSocket port, and 6060 is the management or metrics port.

The compose file for a single node is given in full and it shows the configuration file being mounted.

yaml
services:
  rmqtt:
    container_name: rmqtt
    image: rmqtt/rmqtt:latest
    volumes:
      - /etc/localtime:/etc/localtime
      - /app/rmqtt-single/etc/:/app/rmqtt/etc
      - /app/rmqtt-single/log:/var/log/rmqtt
    ports:
      - "1883:1883"
      - "8080:8080"
      - "8883:8883"
      - "11883:11883"
      - "6060:6060"
    restart: always
    command: -f ./etc/rmqtt.toml --id 1
    privileged: true

The command flag -f ./etc/rmqtt.toml points at the configuration file inside the mounted etc directory, and --id 1 gives the node its cluster identity, which is the same id that appears in the gRPC and Raft address lists in the multi node case.

The static cluster route downloads a compose template from the rmqtt/templates repository as a zip, runs docker-compose up -d, and then checks the cluster with the health endpoint on port 6066.

There is also a ZIP binary package route for Linux, macOS and Windows, downloaded from the releases page, with single node and multi node install documents. The cross-platform claim in the README is that RMQTT can be deployed on x86_64 architecture servers and ARM devices like Raspberry Pi, and the two Dockerfiles in the repository are the amd64 and aarch64 builds of the same thing.

Where RMQTT is the wrong choice

The first wrong case is a team that wants the widest MQTT ecosystem support. The protocol list covers v3.1, v3.1.1 and v5.0 with the three QoS levels, which is the core, and the subscription extensions are broker specific dollar-prefixed topics. A team whose devices are certified against a particular broker's behaviour, or that needs a specific MQTT 5 feature not listed here, should check the protocol documentation page before committing.

The second wrong case is a team that does not want to run a broker. The library mode is the alternative, and it is a genuinely attractive option for a Rust service, but it means the broker's failures are the service's failures and the service's deployment is the broker's deployment. For a fleet large enough to need the clustering, the standalone broker is the right shape.

The third wrong case is a team that needs a managed or hosted broker with a support contract. RMQTT is a project with releases and a community, and the last push was on 2026-10-15 with the newest tag 0.24.0 from 2026-09-19, so it is moving, but it is not a vendor.

The fourth wrong case is a team that needs the bridging in separate processes. The Bridge plugins run in the broker, so a bridge to Kafka means the broker process holds Kafka clients and their failure modes. For a deployment where the broker must be isolated from the message bus it fronts, the bridging has to happen outside, which means this project does not solve that problem.

How it differs from the other Rust MQTT brokers

The comparison worth making is with the other Rust brokers in this space, and the difference in approach is in what each one treats as its centre.

What is distinctive here is the combination of the plugin system with the library mode. The plugin architecture is not a bolt-on, it is how the feature list is organised, and the command line shows it, with the cluster layer itself being a plugin selected by --plugins-default-startups. That means a deployment that does not want clustering does not load it, and a deployment that wants a different consistency model can supply a different cluster plugin.

The second difference is the breadth of the bridge matrix. MQTT, Kafka, Pulsar and NATS each with ingress and egress, plus a time series store outbound, is a lot of surface for one broker, and it is the feature that distinguishes a broker meant to sit in the middle of a company from a broker meant to sit at the edge of a device fleet.

The third difference is the safety claim. 100% Rust safe code is stated as the first feature, and for a broker that holds device credentials and routes messages to and from a Kafka cluster, memory safety is a meaningful property rather than a slogan, since a broker process crashing on malformed input is a different kind of incident from a client process crashing.

The fourth difference is the operational shape. A single binary and a single configuration file, with the Docker image published per architecture and a multi-arch manifest assembled by the Makefile, and the Raft and gRPC ports separated so consensus is not behind application traffic. These are the details that decide whether a broker can be run by a team that did not write it.

Editorial conclusion

RMQTT fits a Rust organisation that wants an MQTT broker it can compile, audit and embed, and that has a device fleet large enough to justify owning the broker rather than renting one, and it fits a team that needs the Kafka, Pulsar or NATS bridging in the same process as the broker. It does not fit a team that needs the widest MQTT ecosystem support, and it does not fit a Java shop that would rather use an existing broker. Before adopting it, note that the Docker image is built per architecture with a merge target that produces a multi-arch manifest, that the single node compose example maps five ports, and that the current release is 0.24.0 from 2026-09-19 with the last push on 2026-10-15.

Frequently asked questions

Which MQTT protocol versions and QoS levels does RMQTT support?

MQTT v3.1, v3.1.1 and v5.0, with QoS0, QoS1 and QoS2 message support, plus offline message support, retained message support and Last Will message support. There is also a document for each of those in the docs/en_US directory.

How do I run RMQTT in a multi node cluster?

Start each node with the rmqtt-cluster-raft plugin enabled, give each a node id, and pass both the node-grpc-addrs and the raft-peer-addrs lists in id@address:port form. The README's example uses ids 1, 2 and 3 at 172.17.0.3 through .5, with gRPC on port 5363 and Raft on port 6003, and there is also a compose template and example configurations for three and five node clusters.

Can I embed RMQTT in my own Rust application?

Yes. The library mode lets you add rmqtt = "0.21" to your Cargo.toml and use its APIs like a regular Rust library, without running it as a standalone server. The workspace patches every internal crate to a local path so a downstream build reaches the same source.

What plugins does RMQTT ship with?

26 built-in plugins in six categories. Auth with ACL, HTTP Auth and JWT Auth. Storage with Retainer, Message Store and Session Store. Cluster with Raft and Broadcast. Bridge with MQTT, Kafka, Pulsar and NATS in both ingress and egress modes, ReductStore outbound and Bridge Origin. API with HTTP API, WebHook and Sys Topic. Utility with Counter, Auto Subscription, Topic Rewrite, P2P Messaging and Shared Subscription.

Which MQTT subscription extensions does RMQTT support?

Shared subscription with the syntax $share/{Group}/{TopicFilter}, exclusive subscription with $exclusive/{TopicFilter}, limit subscription with $limit/{LimitQuantity}/{TopicFilter} and delayed publish with $delayed/{DelayInterval}/{TopicName}. These are broker-defined topics in the MQTT dollar-prefixed namespace.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. rmqtt/rmqtt on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rmqtt-rmqtt.svg)](https://hysenlabs.com/projects/rmqtt-rmqtt)

Was this useful?