MQTT vs NATS is a comparison that comes up when a team likes the simplicity of NATS for microservices and wonders whether it can also serve its devices. Both are lightweight publish/subscribe systems with wildcard subscriptions and low latency, yet they were shaped by very different clients. This guide compares device support, delivery guarantees, persistence and ecosystem, and explains where a managed MQTT broker fits next to NATS.
Two Lightweight Pub/Sub Systems
MQTT is an open OASIS standard protocol. Any compliant client can talk to any compliant broker, and there are many implementations of both. It was built for telemetry from constrained devices over unreliable networks, which is why it has features such as Last Will, retained messages and persistent sessions.
NATS is an open-source messaging system and protocol from the CNCF ecosystem, with one primary server implementation. It was built for cloud-native service-to-service communication: fast request/reply, fan-out and load-balanced work distribution inside and across data centers. Core NATS is deliberately fire-and-forget; JetStream, built into the NATS server, adds persistence, replay and stronger delivery guarantees.
Both are good at what they were designed for. The question is which clients you are connecting.
MQTT vs NATS at a Glance
| Aspect | MQTT | NATS |
|---|---|---|
| Nature | OASIS standard protocol, many brokers | Open-source system, one primary server |
| Addressing | Topics with / levels, wildcards + and # |
Subjects with . tokens, wildcards * and > |
| Delivery (core) | QoS 0, 1, 2 | At most once in core NATS |
| Persistence | Retained last value, session queues | JetStream streams with replay and retention |
| Work distribution | Shared subscriptions (MQTT 5) | Queue groups |
| Request/reply | Response topic and correlation data (MQTT 5) | Built in with inbox subjects |
| Presence | Keep alive and Last Will | Connection events via system account, no Last Will |
| Last-known value | Retained messages | JetStream last-per-subject or KV store |
| Device libraries | Mature options for ESP32, Arduino, embedded C, RTOS | Focus on server languages; few microcontroller options |
| Browser access | MQTT over WebSocket | NATS over WebSocket |
| Industrial ecosystem | Sparkplug B, Unified Namespace, PLC and gateway support | Limited |
| Typical home | Devices, gateways, HMIs, dashboards | Microservices, edge-to-cloud backends, control planes |
Device Support Is the Deciding Factor
For IoT, the most practical difference is client availability. MQTT clients exist for practically every platform a device engineer touches: ESP32 and Arduino libraries such as PubSubClient, embedded C libraries for RTOS targets, cellular modules with MQTT in the modem firmware, PLCs and industrial gateways with MQTT built in, and SCADA and historian software that speaks Sparkplug B. Off-the-shelf sensors and gateways often ship with MQTT as a configuration option.
NATS clients are excellent for Go, Rust, Java, .NET, JavaScript, Python and C on Linux-class hardware. A Raspberry Pi or an industrial PC can run them comfortably. On microcontrollers and in vendor firmware, NATS support is rare. If you need a commercial device to send data without writing firmware, it will almost certainly speak MQTT, not NATS.
Delivery Guarantees and Persistence
MQTT bakes delivery guarantees into every message. A device can publish an alarm at QoS 1 and a high-rate vibration reading at QoS 0 on the same connection. Persistent sessions hold QoS 1 and 2 messages for a subscriber that disconnects, and retained messages hold the last value per topic so a dashboard that opens later sees current state immediately. For details see MQTT QoS levels explained and MQTT retained messages.
Core NATS delivers to whoever is connected at that moment and nothing more. That is fast and simple, and it is the right model for many service calls. When you need durability, JetStream stores messages in streams with retention policies, lets consumers acknowledge and replay, and can deduplicate by message ID. JetStream is more capable than MQTT sessions for history and replay, but it is a separate set of concepts (streams, consumers, acknowledgment policies) that someone has to design and operate.
Presence and Device State
IoT systems constantly need to answer "is this device online, and what was its last reading?" MQTT answers both natively. A device sets a Last Will such as offline on site1/pump7/status with the retain flag, publishes online after connecting, and the broker publishes the will if the connection drops ungracefully. See the MQTT Last Will and Testament guide for the full pattern.
In NATS you would typically build presence with heartbeats from each device, or watch connection events from the server's system account, and store last values in a JetStream key-value bucket. It works, but you are assembling it rather than configuring it.
Subjects vs Topics
The naming models are similar enough to translate. An MQTT topic factory1/line2/cnc1/temperature maps naturally to a NATS subject factory1.line2.cnc1.temperature. MQTT + matches one level like NATS *, and MQTT # matches the remainder like NATS >. The NATS server even offers an MQTT interface that maps topics to subjects in this way and can accept MQTT 3.1.1 clients, which is useful when NATS is already the backbone. It does not support MQTT 5 features, so check the NATS documentation against the features your devices rely on.
A Practical Hybrid Architecture
Plenty of teams use both, each at the layer it suits:
- Devices, PLC gateways and HMIs connect to an MQTT broker over TLS.
- A bridge service subscribes to device topics and republishes to NATS subjects for backend microservices.
- Services use NATS request/reply and queue groups internally, and JetStream where they need durable streams.
- Commands go back through the bridge and are published to the device's MQTT command topic.
A minimal one-way bridge in Go looks like this, using the Eclipse Paho Go client and the official NATS client.
package main
import (
"crypto/tls"
"log"
"strings"
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/nats-io/nats.go"
)
func main() {
nc, err := nats.Connect(nats.DefaultURL)
if err != nil {
log.Fatal(err)
}
defer nc.Drain()
opts := mqtt.NewClientOptions().
AddBroker("tls://broker.connectmq.com:8883").
SetClientID("YOUR_CLIENT_ID").
SetUsername("YOUR_USERNAME").
SetPassword("YOUR_PASSWORD").
SetTLSConfig(&tls.Config{MinVersion: tls.VersionTLS12}).
SetAutoReconnect(true)
opts.SetOnConnectHandler(func(c mqtt.Client) {
// Subscribe in the connect handler so it is restored after reconnects
c.Subscribe("factory1/+/+/telemetry", 1, func(_ mqtt.Client, m mqtt.Message) {
subject := strings.ReplaceAll(m.Topic(), "/", ".")
if err := nc.Publish(subject, m.Payload()); err != nil {
log.Printf("nats publish failed: %v", err)
}
})
})
client := mqtt.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
log.Fatal(token.Error())
}
log.Println("bridging MQTT to NATS")
select {}
}
The paho Go client uses MQTT 3.1.1, which ConnectMQ accepts alongside MQTT 5. To try it without credentials, use tcp://broker.connectmq.com:1883, any unique client ID and a topic prefix such as connectmq/demo/<random-id>/ on the public MQTT test broker. Watch out for characters that are legal in MQTT topics but special in NATS subjects, such as . or spaces, and sanitize them in the bridge.
When to Choose Which
Choose MQTT when your clients are devices, gateways, PLCs or third-party hardware, when connectivity is unreliable, or when you need presence, retained state and industrial conventions such as Sparkplug B and a Unified Namespace.
Choose NATS when your clients are services you write, running on reliable networks, and you want fast request/reply, queue groups and optional JetStream persistence with minimal infrastructure.
Choose both when you have a device fleet and a microservice backend that already standardizes on NATS.
For the device layer, a managed MQTT broker removes the work of running and securing an internet-facing service. ConnectMQ offers MQTT 3.1.1 and 5.0, TLS, shared subscriptions on the paid Serverless plan, per-plan quotas and multi-tenant isolation, without a NATS bridge built in, so the bridge stays your own small service as shown. If you are comparing other backends too, see MQTT vs Kafka, and for fundamentals the MQTT tutorial.
Get Started with ConnectMQ
Give your devices a standard, well-supported MQTT endpoint and keep NATS for your services. Create a free Serverless broker at app.connectmq.com, no credit card required, connect a device and your bridge, and watch both sides in the live topic explorer.
Frequently Asked Questions
Is NATS faster than MQTT?
Both are low-latency, and real-world speed depends on the server, network and delivery settings more than the protocol. Choose based on clients and features rather than raw speed.
Can IoT devices connect to NATS directly?
Linux-class devices can use NATS clients. Microcontrollers and commercial sensors rarely support NATS, which is why MQTT dominates at the device layer.
Does NATS support MQTT clients?
The NATS server provides an MQTT 3.1.1 interface backed by JetStream. It does not cover MQTT 5 features, so a dedicated MQTT broker is usually a better fit for large mixed fleets.
What is the NATS equivalent of an MQTT retained message?
The closest equivalents are a JetStream stream configured to keep the last message per subject, or a JetStream key-value bucket. Both must be set up explicitly.



