Config file reference
The config file is one YAML document. Give it with --config or
MQGW_CONFIG. The gateway serves a protocol when the file has its block,
even an empty one (amqp: {}). Every other key has a default, so a file
can be as short as mqtt: {}.
Example
id: gw-1 # unique per gateway; also --id
log: { level: info, format: json }
monitor: { addr: ":8222" } # /livez, /readyz and /healthz
ldm: { duration: 2m, grace-period: 10s }
tls: # TLS on every MQTT and AMQP listener
cert: /etc/mqgw/tls.crt
key: /etc/mqgw/tls.key
nats:
url: nats://nats-1:4222,nats://nats-2:4222,nats://nats-3:4222
creds: /etc/mqgw/gateway.creds # the gateway's own NATS user
bindings: # subjects that a stream stores
- { subjects: [orders, orders.>], stream: ORDERS }
- { subjects: [plant1.*.*.alarms], stream: ALARMS }
mqtt:
listeners:
- { name: public, addr: ":1883" } # client's username and password
- { name: telemetry, addr: ":1884", auth: { mode: fixed, creds: /etc/mqgw/telemetry.creds } }
- { name: ws, addr: ":8080", ws-path: /mqtt }
bindings: [ORDERS, ALARMS] # QoS 1 and 2 on these streams
retained: { bucket: MQTT_RETAINED }
sessions: { bucket: MQTT_SESSIONS }
amqp: # one passthrough listener on :5672
bindings: [ORDERS] # unsettled transfers on ORDERS
delivery-counts: { bucket: AMQP_COUNTS }
routes:
- { subject: orders, template: "orders.{region}" }
This gateway serves MQTT on three ports and AMQP on one. MQTT stores
QoS 1 and 2 messages in both streams. AMQP stores unsettled transfers in
ORDERS only. Everything else travels over core NATS.
Gateway
| Key | Default | Description |
|---|---|---|
id | random | A NATS subject token that names the gateway in the admin subjects, unique per gateway |
log.level | info | debug, info, warn or error |
log.format | text | text, or json for one object per line, on standard error |
monitor.addr | none | HTTP address for the health endpoints |
ldm.duration | 2m | Time that lame duck mode takes to move all clients |
ldm.grace-period | 10s | Wait, shorter than ldm.duration, before lame duck mode closes the first connection |
tls.cert, tls.key | none | PEM certificate and key for every MQTT and AMQP listener |
tls.ca | none | PEM CA that must sign every client's certificate |
license.file | none | File with the license JWT, read only by a licensed build |
license.token | none | The license JWT, used when license.file is unset |
bindings | none | Streams that store subjects (see Routing) |
accounts | none | Tables and credentials per NATS account (see Accounts) |
mqtt
| Key | Default | Description |
|---|---|---|
mqtt.listeners | mqtt on :1883 | The MQTT ports (see Listeners) |
mqtt.connect-timeout | 5s | Time a client has to send its CONNECT |
mqtt.max-packet-size | 67108864 | Largest packet in bytes from a client, capped for v5 clients at the NATS payload limit |
mqtt.max-inflight | 1024 | Unacknowledged QoS 1 and 2 messages per client in each direction (1–65535) |
mqtt.ack-wait | 30s | Time before the gateway sends an unacknowledged delivery again to an MQTT 3.1.1 client (1s–1h). A v5 client gets no resend on the same connection |
mqtt.ack-batch | 64 | Deliveries the gateway acknowledges to the stream together (1 for each one) |
mqtt.topic-alias-max | 0 | Topic aliases a v5 client may use (0 for none) |
mqtt.server-keepalive | 0 | Keep-alive in seconds that replaces a v5 client's own (0 for the client's) |
mqtt.server-reference | none | Server Reference that v5 clients get when the gateway stops, often host:port |
mqtt.sessions.max-expiry | 24h | Longest time a persistent session outlives its connection (at least 1s) |
mqtt.sessions.sweep | 1m | Interval at which the gateway ends expired sessions and publishes due wills |
mqtt.sessions.bucket | none | KV bucket that keeps persistent sessions |
mqtt.sessions.domain | none | JetStream domain of the sessions bucket |
mqtt.bindings | none | Binding names that store QoS 1 and 2 messages |
mqtt.routes | none | Routes for v5 publishes (see Routing) |
mqtt.retained.bucket | none | KV bucket that keeps retained messages |
mqtt.retained.domain | none | JetStream domain of the retained bucket |
mqtt.sparkplug.aware | false | Retain Sparkplug B births on $sparkplug/certificates/ (needs mqtt.retained.bucket) |
Without mqtt.bindings, MQTT offers QoS 0 only. Without a retained
bucket, the gateway ignores RETAIN. Without a sessions bucket, each session
ends with its connection. Gateways that share a sessions bucket should use
the same mqtt.ack-wait. See Run Sparkplug B
clients for mqtt.sparkplug.aware.
amqp
| Key | Default | Description |
|---|---|---|
amqp.listeners | amqp on :5672 | The AMQP ports (see Listeners) |
amqp.connect-timeout | 5s | Time a client has for the SASL exchange, and again for its open |
amqp.max-frame-size | 131072 | Largest frame in bytes that a client may send (at least 512) |
amqp.max-inflight | 1024 | Credit for a sending link, and unsettled deliveries per queue (1–65535) |
amqp.ack-wait | 30s | Time before the stream sends an unsettled delivery again (1s–1h) |
amqp.queue-inactive | 1h | Time an address's queue stays with no receiver attached |
amqp.bindings | none | Binding names that store unsettled transfers |
amqp.routes | none | Routes for transfers (see Routing) |
amqp.delivery-counts.bucket | none | KV bucket that keeps delivery counts (required with amqp.bindings) |
amqp.delivery-counts.domain | none | JetStream domain of the delivery-counts bucket |
Without amqp.bindings, AMQP takes settled transfers only. A queue keeps
the amqp.ack-wait from when the gateway created it. A new value applies
after the queue expires.
nats
| Key | Default | Description |
|---|---|---|
nats.url | nats://localhost:4222 | NATS server URLs, comma-separated, without credentials |
nats.user, nats.password, nats.creds, nats.nkey, nats.jwt, nats.seed, nats.context | none | The gateway's own NATS user (see Credentials) |
nats.tls.cert, nats.tls.key | none | Client certificate and key for the gateway's NATS connections |
nats.tls.ca | none | CA that signed the NATS servers' certificates |
nats.ping-interval | 10s | Interval between pings on each NATS connection |
nats.max-pings-out | 2 | Unanswered pings before a connection moves to another server |
nats.reconnect.max | forever | Attempts per server before a connection closes, or forever |
nats.reconnect.wait | 2s | Pause before a connection tries a server again |
nats.embed | false | Run a NATS server with JetStream in the gateway process (not with nats.url) |
nats.host | 127.0.0.1 | Embedded server's client listen host |
nats.port | random | Embedded server's client port |
nats.store-dir | temporary | Embedded server's JetStream directory (the gateway deletes a temporary one at stop) |
nats.config | none | nats-server config file for the whole embedded server (not with nats.host, nats.port or nats.store-dir) |
A connection detects a lost server after about nats.ping-interval ×
nats.max-pings-out. When a client's NATS connection closes, the gateway
disconnects that client. When the gateway's own connection closes, only a
restart opens it again. nats.host, nats.port, nats.store-dir and
nats.config need nats.embed: true.
Listeners
Each entry of mqtt.listeners or amqp.listeners is one port. The list
replaces the protocol's default listener.
| Key | Default | Description |
|---|---|---|
addr | required | TCP listen address |
name | addr | Listener name in log lines |
ws-path | none | WebSocket path to serve the protocol on (wss:// with tls) |
auth.mode | passthrough | passthrough or fixed |
auth.user, auth.password, auth.creds, auth.nkey, auth.jwt, auth.seed, auth.context, auth.tls | none | The NATS credential for every client on a fixed port (see Credentials) |
On a passthrough port, the client's username and password become its
NATS user and password. A client that sends none connects with no
credential, so NATS refuses it or makes it the server's no_auth_user.
On a fixed port, every client connects with the listener's credential,
and the gateway ignores the client's own.
Configure listeners and auth
shows both.
Routing
A routing table binds NATS subjects to streams. Each protocol then lists
the bindings that it stores in. The top-level bindings and the routing
keys in mqtt and amqp form the table for every account that has no
table of its own.
| Key | Default | Description |
|---|---|---|
bindings[].subjects | required | NATS subjects that the stream stores, with * and > as wildcards |
bindings[].stream | required | An existing stream, which the gateway never creates or changes |
bindings[].name | the stream | Name that mqtt.bindings and amqp.bindings list |
bindings[].domain | none | JetStream domain of the stream, for a stream in another domain |
mqtt.routes[].subject, amqp.routes[].subject | required | A subject without wildcards that clients publish to |
mqtt.routes[].template, amqp.routes[].template | required | Subject to publish under instead, such as orders.{region} |
A {name} token in a template takes the value of the message's property
name. For MQTT, that is a v5 User Property. For AMQP, it is an
application property. A selector on that property then becomes a subject
filter that NATS applies. A subject routed in both blocks must have the
same template in each. A binding must cover the template's subjects, here
orders.>. Mapping to NATS shows how topics and
addresses become subjects, and which publishes a route refuses.
orders.> does not cover orders. To store what an MQTT orders/#
filter matches, list both, as the example does. mq-gateway binding
prints that entry from MQTT filters (see CLI).
Accounts
The gateway serves the account of its own NATS user. Each entry under
accounts adds an account, gives an account its own table, or both. The
key is the account name, or in operator mode its public key (A…).
mqtt: {}
accounts:
PLANT_A: # the gateway user's account, own table
bindings:
- { subjects: [uagv.v2.*.*.order], stream: VDA_ORDERS }
mqtt:
bindings: [VDA_ORDERS]
sessions: { bucket: VDA_SESSIONS }
PLANT_B: # another account, reached with its own user
creds: /etc/mqgw/gw-plant-b.creds
bindings:
- { subjects: [spBv1//0.*.NDEATH.*], stream: SPARKPLUG }
mqtt:
bindings: [SPARKPLUG]
| Key | Description |
|---|---|
accounts.<account>.user, .password, .creds, .nkey, .jwt, .seed, .context, .tls | The gateway's NATS user in the account, which adds the account |
accounts.<account>.bindings, .mqtt, .amqp | The account's own table, with routing keys only |
An account's own table replaces the top-level table. The gateway does not merge the two. An entry needs a credential, a table or both.
Credentials
The nats block, account entries and fixed listeners take a credential
in the same keys. Give exactly one kind. Add tls to any kind except
context, or use tls alone. The gateway refuses a NATS token.
| Key | Credential |
|---|---|
user, password | A user and its password |
creds | A credentials file with the user's JWT and nkey seed |
nkey | An nkey seed file |
jwt, seed | A user JWT and its nkey seed, in two files |
context | A saved nats CLI context, without its server URL |
tls.cert, tls.key, tls.ca | Client certificate files, for a server that maps certificates to users |
creds, nkey, jwt and seed are locations. password is a plain
value unless it is an env://, file:// or op:// location.
| Location | Read from |
|---|---|
/path or file:///path | The file |
env://NAME | The environment variable |
op://vault/item/field | op read, the 1Password CLI, on PATH |
nsc://Operator/Account/User | The creds file from nsc generate profile, on PATH |
The gateway reads a creds file again at each reconnect, so a rotated
file takes effect without a restart. It reads every other secret once, at
start. It never logs a secret.
Rules
- Unknown keys fail. The gateway refuses a key that it does not know, in any block. A typo stops the start, so it cannot leave a setting at its default.
- Durations are strings. Write
500ms,90s,2mor1h30m. The gateway refuses a bare number. - Flags and variables win.
id,nats.url,log.level,log.format,license.fileandlicense.tokenalso have a flag and anMQGW_variable. The order is flag, then variable, then file, then default. An empty variable counts as unset.--mqttand--amqpturn a protocol on or off. See CLI. - Bindings are NATS subjects. A binding subject uses
*and>, not+and#. It cannot start with a wildcard. It cannot overlap$JS.>,$JSC.>,$NRG.>,$SYS.>or$MQGW.>. Each subject appears once in a table, andmqtt.bindingsoramqp.bindingsmust list each binding. - Streams and buckets must exist. The gateway checks them at start, in
each account. It refuses the clients of an account that fails. Run
mq-gateway checkbefore you deploy, and see Provision streams and buckets. - Changes need a restart. The gateway reads the file only at start. An error in it stops the gateway with exit status 1 and names the file and key. See Change the config.
Fixed values
These values are not in the file:
| Value | Fixed at |
|---|---|
| Time a stream has to acknowledge a store | 5s |
| Wait for connections to close at shutdown | 10s |
| Lame duck mode overrun before connections close at once | 10s |
| AMQP idle timeout (the gateway advertises half) | 60s |
| Idle NATS connections kept per listener and credential | 8, for 5m |