Fast, persistent MQTT v5 broker for Rust-powered systems.
Pulse is a compact MQTT broker built on top of rs-netty. It is designed for the place where MQTT usually has to be boring in the best possible way: edge gateways, device backplanes, lab networks, local-first products, and internal message fabrics that need durable sessions without dragging in a giant service.
- MQTT v5 broker semantics with CONNECT, SUBSCRIBE, UNSUBSCRIBE, PUBLISH, PING, DISCONNECT, will messages, keep alive, topic aliases, reason strings, and packet-size limits.
- QoS 0, QoS 1, and QoS 2 delivery paths, including outbound inflight tracking, duplicate QoS 2 handling, redelivery after reconnect, and receive-maximum backpressure.
- Persistent sessions with clean start, session expiry, subscription recovery, offline queues, retained messages, and durable restart recovery.
- Storage choices for different stages of a project: in-memory by default on Unix-like builds, SQLite by default for Windows MSI installs, and MySQL when you want shared operational infrastructure.
- Shared subscriptions, subscription identifiers, retained replay behavior, no-local handling, message expiry, and retained-store limits.
- Prometheus metrics and structured tracing, ready for dashboards instead of print-debug archaeology.
- Graceful shutdown through
rs-nettyserver handles: Pulse listens for Ctrl-C, asks the server to stop accepting work, and waits for the server task to exit.
Run Pulse locally:
cargo run -- --bind 127.0.0.1:1883 --log infoUse the sample config:
cargo run -- --config Broker.tomlEnable SQLite persistence:
[storage]
sqlite = "pulse.db"Expose Prometheus metrics:
[observability]
metrics_bind = "127.0.0.1:9000"Enable MQTT over TLS:
[server]
bind = "0.0.0.0:8883"
[server.tls]
enabled = true
certificate_chain = "/etc/pulse/server-chain.pem"
private_key = "/etc/pulse/server-key.pem"Require client certificates for mTLS:
[server.tls]
enabled = true
certificate_chain = "/etc/pulse/server-chain.pem"
private_key = "/etc/pulse/server-key.pem"
client_auth = "required"
client_ca = "/etc/pulse/client-ca.pem"Then run:
cargo run -- --config Broker.tomlPulse reads configuration from Broker.toml by default when the file sits next
to the executable. You can also pass an explicit path:
cargo run -- --config ./Broker.tomlConfiguration can come from four places, applied in this order:
- Built-in defaults.
Broker.toml.- Environment variables such as
MQTT_RS_BIND,MQTT_RS_SQLITE,MQTT_RS_LOG,MQTT_RS_METRICS_BIND, andMQTT_RS_TLS_CERTIFICATE_CHAIN. - CLI flags such as
--bind,--sqlite,--mysql,--log, and--metrics-bind.
The MQTT_RS_* environment prefix is intentionally kept for compatibility
while the broker moves under the Pulse name.
Operational knobs include MQTT_RS_SHUTDOWN_DRAIN_TIMEOUT_MS /
--shutdown-drain-timeout-ms and MQTT_RS_INFLIGHT_RETRANSMIT_INTERVAL_MS /
--inflight-retransmit-interval-ms.
WAL persistence knobs are accepted by the configuration layer for deployments that want lightweight durable state without SQLite or MySQL:
[server]
worker_threads = 8
[storage]
engine = "wal"
wal_dir = "data/wal"
commit_policy = "balanced" # strict, balanced, or fast
[limits]
slow_consumer_policy = "throttle" # throttle, disconnect, or queue-offlineThe matching CLI flags are --worker-threads, --storage-engine, --wal-dir,
--storage-commit-policy, and --slow-consumer-policy.
TLS can also be enabled without editing the TOML file:
cargo run -- \
--bind 0.0.0.0:8883 \
--tls \
--tls-certificate-chain /etc/pulse/server-chain.pem \
--tls-private-key /etc/pulse/server-key.pemFor mTLS, add --tls-client-auth optional or
--tls-client-auth required plus --tls-client-ca /etc/pulse/client-ca.pem.
The matching environment variables are MQTT_RS_TLS_ENABLED,
MQTT_RS_TLS_CERTIFICATE_CHAIN, MQTT_RS_TLS_PRIVATE_KEY,
MQTT_RS_TLS_CLIENT_AUTH, and MQTT_RS_TLS_CLIENT_CA.
Enable MQTT over WebSocket on a separate listener:
[websocket]
enabled = true
bind = "0.0.0.0:8083"
path = "/mqtt"WebSocket clients must connect with Sec-WebSocket-Protocol: mqtt and send MQTT
control packets in binary frames. The legacy mqttv3.1 subprotocol is also
accepted. To serve wss://, set tls = true under [websocket]; Pulse reuses
the certificate settings from [server.tls].
Enable the static username/password and ACL backend:
[auth]
enabled = true
[[auth.users]]
username = "alice"
password = "secret"
[[auth.acl]]
username = "alice"
action = "publish"
topic_filter = "devices/alice/#"When auth.enabled = true, ACLs are default-deny and passwords are stored as
plain text in this v1 static backend. Keep using TLS or mTLS for transport
security and prefer controlled deployments until a hashed or external
authenticator is configured.
Windows MSI installs do not install a default Broker.toml. They use SQLite at
C:\ProgramData\Pulse\broker.db by default and create that directory on first
startup. To customize settings on Windows, place a Broker.toml next to
Pulse.exe, set MQTT_RS_* environment variables, or pass --config.
Pulse already covers the core broker paths:
- MQTT connection lifecycle and keep alive.
- Persistent and clean-start sessions.
- Subscription storage and wildcard matching.
- Shared subscriptions with round-robin dispatch.
- Retained messages with expiry and store limits.
- QoS 1 and QoS 2 handshakes, inflight state, and reconnect redelivery.
- Online QoS 1 and QoS 2 inflight retransmission timers.
- Offline queueing for persistent sessions.
- SQLite and MySQL-backed state recovery.
- Optional static username/password authentication and publish/subscribe ACLs.
- Prometheus metrics for connections, publishes, subscriptions, queues, retained messages, inflight packets, parse errors, and delivery failures.
src/main.rs server startup and graceful shutdown
src/settings.rs file/env/CLI configuration
src/protocol.rs MQTT reason codes and topic matching
src/broker/runtime/connection TCP and WebSocket MQTT handlers
src/broker/runtime/delivery publish routing, QoS, offline queues
src/broker/runtime/subscription_tree subscriptions and shared groups
src/broker/storage in-memory, SQLite, and MySQL state
src/observability tracing and Prometheus metrics
Run the full test suite:
cargo testRun formatting checks:
cargo fmt --checkThe test suite exercises broker behavior over real TCP connections, including CONNECT validation, malformed packets, will delivery, keep alive, persistent session recovery, retained replay, QoS handshakes, SQLite restart recovery, and message expiry.
Pulse includes a local benchmark harness in benchmark/. The benchmark tooling
is excluded from Cargo packages and starts both brokers on localhost with
temporary persistent state. Pulse is run against binary WAL storage in fast
commit mode, while Mosquitto is run with built-in persistence enabled,
autosave-on-change enabled, and relaxed queue/inflight limits.
This single local run used:
python3 benchmark/run.py --messages 10000 --timeout 60Environment: macOS 26.5 arm64, Python 3.9.6, Pulse 1.3.2 release build,
Pulse binary WAL temporary storage in fast commit mode, Mosquitto 2.1.2
temporary persistence, 128-byte payloads, 100 retained-fanout subscribers, and
10 ms RSS sampling.
RSS values are MiB for the broker process.
| Broker | Scenario | Count | Seconds | Rate/sec | Base RSS | Peak RSS | End RSS |
|---|---|---|---|---|---|---|---|
| Pulse-wal | qos0-throughput | 10000 | 0.1008 | 99236.87 | 3.48 | 4.00 | 4.00 |
| Pulse-wal | qos1-throughput | 10000 | 0.4124 | 24250.89 | 3.48 | 4.23 | 4.23 |
| Pulse-wal | qos2-throughput | 10000 | 0.8311 | 12031.69 | 3.48 | 4.23 | 4.23 |
| Pulse-wal | retained-fanout | 100 | 0.0113 | 8871.18 | 3.48 | 6.61 | 6.61 |
| Mosquitto-persist | qos0-throughput | 10000 | 0.1166 | 85742.37 | 4.44 | 5.41 | 5.41 |
| Mosquitto-persist | qos1-throughput | 10000 | 1.0624 | 9412.80 | 4.44 | 5.42 | 5.42 |
| Mosquitto-persist | qos2-throughput | 10000 | 1.6111 | 6206.93 | 4.44 | 5.42 | 5.42 |
| Mosquitto-persist | retained-fanout | 100 | 0.0097 | 10282.20 | 4.44 | 5.94 | 5.94 |
Pulse is still young. The next high-value areas are:
- Hashed password storage and external authentication providers.
- Interop testing with common MQTT v5 clients.
- Clear documentation of supported and intentionally unsupported MQTT v5 features.
Pulse aims to be the broker you can read, run, and reason about. Small enough to understand, serious enough to preserve sessions, and Rust-native all the way down.
