Every option of mercuriod's TOML configuration file, with its default. Every option is optional. An option or table the broker does not know, such as a misspelled one, stops it from starting with an error that names it.
The broker reads /etc/mercurio/config.toml, or the file given with -c. A commented file with every option is provided as mercuriod/contrib/config.toml.
Checking a configuration
mercuriod --check does what a start does short of listening and opening storage: it reads the configuration and its include_dir, checks the settings that depend on each other, loads the password, ACL, dynamic security and JWT key files of [auth] and of every listener, fetches their JWT key sets, loads the TLS certificates and keys, compiles and initialises the WebAssembly plugins, and sets up every bridge: its routes, its remote addresses and its TLS files. It then exits with status 0. A problem makes it exit with status 1 and print the error a start would. It does not connect to PostgreSQL, bind ports or sockets, or contact bridges.
# As the broker's user, so files are read with its permissions
sudo -u mercurio mercuriod --check -c /etc/mercurio/config.toml && sudo systemctl restart mercuriod
Top level
Option
Default
Meaning
include_dir
Directory whose *.toml files are merged into this one, in name order. Their tables extend this file's, their arrays ([[listeners]], [[bridges]]) are added to this file's, and their other values replace this file's.
# /etc/mercurio/config.toml
include_dir = "/etc/mercurio/conf.d"
# /etc/mercurio/conf.d/tenant1.toml adds a listener
[[listeners]]
port = 1885
mount_point = "tenant1/"
[server]
Main listener
Option
Default
Meaning
host
"127.0.0.1"
Address to listen on. "0.0.0.0" for every IPv4 interface, "::" for IPv6.
port
1883
Port of the main MQTT listener.
bind_interface
Only accept connections arriving on this network interface, such as "eth0". Linux only.
websocket_port
Also serve MQTT over WebSocket on this port, with the main listener's TLS settings.
http_dir
Serve the files of this directory to plain HTTP requests on websocket_port.
Limits
Option
Default
Meaning
max_connections
10000
Concurrent connections, across every listener. Further ones are closed right after being accepted.
max_packet_size
1048576
Largest packet accepted from a client, in bytes. Announced to MQTT 5 clients.
max_connect_packet_size
65536
Largest CONNECT accepted from a client, in bytes, and largest AUTH during enhanced authentication. These come before the client is authenticated, so they get less room than other packets; max_packet_size still caps it. A larger one is refused as Packet Too Large. On WebSocket listeners, a client may send twice this many bytes, framing included, before it is authenticated; past that, the connection is closed. At least 1.
connect_timeout
10
Seconds a new connection has, in all, to send its PROXY header, complete the TLS and WebSocket handshakes and send CONNECT, enhanced authentication included.
max_queued_messages
10000
Messages that may wait for one client, any QoS, before further ones are dropped.
max_queued_qos0_messages
1000
Queued messages beyond which QoS 0 messages are dropped.
max_queued_bytes
104857600
Payload bytes that may wait for one client before further messages are dropped.
shared_subscription_strategy
"round_robin"
How a shared subscription's group picks the member each message goes to: "round_robin", the member after the one that took the last; "random"; "sticky", the member that took the last one, for as long as it is connected and its queue has room; or "hash", a member picked by the publisher's client id, so the messages of one publisher reach one member in order while the group stays the same. With any strategy a connected member whose queue takes the message comes before the others, and a disconnected member only gets it when no connected one can. Messages the broker publishes itself, such as wills, go round robin under "hash".
shared_subscription_redelivery
"session_end"
When the messages a member of a shared subscription was given and hasn't processed go to another member of its group: those still queued for it, and those at QoS 1 it was sent and hasn't acknowledged. With "session_end" they go when the member's session ends, as its expiry passes, a clean start replaces it or it is deleted; until then they wait for the member, and a member whose session ends with its connection hands them over as it disconnects. With "disconnect" they also go as soon as the member loses its connection, to the members connected then; what none of them takes waits for the member. A QoS 2 message already sent stays with the member, as MQTT requires. A QoS 1 message the member processed but did not acknowledge before leaving reaches the application a second time, through the other member, so applications on shared subscriptions should handle duplicates. What no member takes when a session ends is lost and counted as no_share_member.
queue_overflow
"drop_newest"
Which message a full queue drops: "drop_newest", the one arriving, so a client that falls behind gets the start of what it missed; or "drop_oldest", the oldest waiting of the same kind (QoS 0, or QoS 1 and 2), as many as it takes, so it gets the latest. A QoS 0 message finding only QoS 1 and 2 ones waiting is still dropped, and a shared subscription's member pushes nothing out: the message goes to another member. Dropped messages count under publish/messages/dropped/queue_full_qos0 and queue_full either way.
receive_maximum
65535
QoS 2 messages an MQTT 5 client may have awaiting PUBREL at once.
max_inflight
64
QoS 1 and 2 messages sent to one client and not yet acknowledged, at most, whatever Receive Maximum the client announces. At least 1.
max_topic_alias
10
Highest topic alias MQTT 5 clients may use; 0 disables aliases.
max_keepalive
Longest keep-alive, in seconds, MQTT 5 clients may use. Clients asking for more, or for none, are told to use this one.
max_qos
2
Highest QoS clients may use. Subscriptions above it are granted this QoS; publishing above it closes the connection.
retain_available
true
With false, retained messages are refused, and so are wills that ask to be retained.
Client ids and sessions
Option
Default
Meaning
allow_zero_length_clientid
true
Accept clients that send an empty client id and assign them one.
auto_id_prefix
""
Prefix of the client ids the broker assigns.
clientid_prefixes
[]
When not empty, only client ids starting with one of these are accepted; others are refused as not authorized.
persistent_client_expiration
never
Seconds the session of an MQTT 3.x client connected without clean session outlives its connection. MQTT 5 clients choose their own expiry.
max_session_expiry
604800 (a week)
Most seconds any session may outlive its connection. MQTT 5 clients asking for longer, or for no expiry, are told to use this; it also bounds persistent_client_expiration, so MQTT 3.x sessions end a week after their client left too. "unlimited" keeps sessions for as long as their clients ask, forever included; each stored session takes memory and storage, so keep a bound where clients aren't trusted.
max_sessions
unbounded
Sessions that outlive their connection the broker may hold, connected or not. A client whose CONNECT would start one more is refused with Quota Exceeded (Server Unavailable for MQTT 3.x); resuming an existing session never is. Sessions that end with their connection, Clean Session in MQTT 3.x or a zero Session Expiry Interval in MQTT 5, don't count, as max_connections bounds them; a broker at its limit still serves such clients. Sessions restored at startup count, even beyond the limit.
max_retained_messages
unbounded
Topics that may hold a retained message at once, the broker's own $SYS topics not counted. Retaining a message on another topic is refused with Quota Exceeded; updating or clearing a retained topic is always allowed.
max_publish_rate, publish_rate_burst
unlimited, max_publish_rate
PUBLISH packets per second one client may send on average, and how many it may send at once after a quiet spell. A client going faster isn't read from until its rate allows another message, so TCP's flow control slows it down to the rate; nothing it sent is dropped or refused, and its keep-alive doesn't run out meanwhile. In-process clients, such as bridges, and clients of the admin socket are not limited.
max_publish_bytes_rate, publish_bytes_burst
unlimited, max_publish_bytes_rate
The same for the payload bytes of the PUBLISH packets one client sends.
publish_rate_grace
unset
Seconds a client may stay over max_publish_rate or max_publish_bytes_rate before it is disconnected with Message Rate Too High; MQTT 3.x clients are just disconnected. A client is over its rate from the first PUBLISH the rate holds back until the broker, reading from it again, finds nothing waiting. Unset, clients are only slowed down. Needs one of the rates.
A client address that fails to log in login_failure_attempts times within login_failure_window seconds is banned for login_failure_ban seconds. Wrong passwords, missing credentials where they are required and failed enhanced authentication count. Addresses count as for max_connections_per_ip, IPv6 by /64, so the ban covers the block. It is an ordinary address ban with an expiry and the reason automatic: …: mercurioctl ban list shows it, and removing it lets the address in again at once. Loopback addresses, in-process clients and clients of the admin socket are never banned. Behind a proxy, set proxy_protocol so the clients' own addresses count, not the proxy's. login_failure_attempts = 0 turns it off. Failures are tracked for 16384 addresses at most, so a client cycling through addresses can't exhaust memory.
max_connections_per_ip
unbounded
Connections one client address may hold at once, counted from the moment they are accepted, before any TLS or WebSocket handshake; further ones are closed. An IPv6 client counts as its /64 block, and an IPv4 client reaching an IPv6 socket as its IPv4 address. Behind a proxy sending PROXY headers, the address the header gives is counted, so set proxy_from too. Unbounded by default, as carrier NAT puts many devices behind one address.
plugin_timeout
5
Seconds one call of a plugin hook may take, for the built-in password file, ACL file, HTTP and dynamic security checks as for [[plugins]]. A slower call denies, or drops the message. The broker refuses to start when [auth.http]timeout_seconds or a plugin's timeout_ms is longer.
plugin_tick_interval
1
Seconds between calls of the plugins' on_tick.
Delivery
Option
Default
Meaning
upgrade_outgoing_qos
false
Send messages at the QoS of the subscription even when published with a lower one. Goes beyond the MQTT specification.
max_subscriptions
10000
Subscriptions one session may hold. Further ones are refused with Quota Exceeded; replacing one it holds is allowed.
max_topic_filter_length
1024
Longest topic filter, in bytes, a SUBSCRIBE may name. Longer ones are refused with Topic Filter Invalid (a failure for MQTT 3.x clients). Each filter is kept in memory and storage for as long as its session.
check_retain_source
false
Send a retained message to new subscribers only while the client that published it may still publish to its topic.
send_topic_aliases
false
Replace repeated topics with aliases for MQTT 5 clients that accept them. Off by default because some client libraries announce alias support they don't implement.
response_information
Topic prefix given to MQTT 5 clients that request Response Information; %c is replaced by the client id, as in "responses/%c".
sys_interval
10
Seconds between updates of the $SYS statistics; 0 disables them.
[server.redirect]
Sends every client to another server instead of serving it. MQTT 5 clients are told where to go; MQTT 3.x clients only learn that this server is unavailable.
Option
Default
Meaning
server_reference
required
Where to go, such as "broker2.example.com:1883".
permanent
false
Server Moved when true, Use Another Server otherwise.
[server.tls]
TLS for the main listener, and for websocket_port. [listeners.tls] takes the same options. See TLS.
Option
Default
Meaning
enabled
false
Serve TLS instead of plain TCP.
cert_path
Certificate chain, PEM.
key_path
Private key, PEM: PKCS#1, PKCS#8 or SEC1.
ca_path
Require clients to present a certificate signed by this CA.
require_certificate
true
When false, clients without a certificate connect and log in by user name and password; a certificate that is presented must still be valid. Needs ca_path.
crl_path
Refuse client certificates revoked in these CRLs, PEM. Every certificate of the client's chain needs a CRL from its issuer here, or the client is refused. Needs ca_path.
crl_end_entity_only
false
Check only the client's own certificate against crl_path, not the intermediate CAs that issued it. Needs crl_path.
crl_allow_unknown
false
Accept certificates whose issuer has no CRL in crl_path, instead of refusing them. Needs crl_path.
identity_as_username
false
Use the client certificate's Common Name as the user name. Needs ca_path.
subject_as_username
false
Use the whole certificate subject as the user name, in RFC 2253 form. Needs ca_path.
min_version
"1.2"
Oldest TLS version accepted: "1.2" or "1.3".
[logging]
Option
Default
Meaning
level
"info"
trace, debug, info, warn or error, or per module, as in "info,mercurio_server::session=warn". An empty or invalid level means info, with a warning in the log. setLogLevel changes it until the broker restarts.
file
Append logs to this file instead of standard output.
syslog
false
Send logs to the local syslog daemon through /dev/log.
connection_messages
true
Log clients connecting and disconnecting. With false, a level naming their target, mercurio::connections, still turns them on.
[storage]
Option
Default
Meaning
backend
"memory"
"memory", lost on restart, "sqlite" or "postgres".
path
The SQLite database file, with backend = "sqlite".
url
The PostgreSQL database, such as "postgres://mercurio:password@localhost/mercurio", with backend = "postgres". PostgreSQL 12 or later, connected to directly, without PgBouncer. The broker creates its tables on start. Connections to another host are encrypted, and fail if the server has TLS off; a server on the same host, over loopback or a Unix socket, may decline encryption unless ca_path is set. ?sslmode=require, verify-ca and verify-full insist on encryption and all verify the server's certificate and host name. ?sslmode=prefer or ?sslmode=disable allow plaintext, with a warning in the log for another host. The log says whether the connection is encrypted.
ca_path
Mozilla's root certificates
PEM file of the certificate authority that signed the PostgreSQL server's certificate, such as a cloud provider's bundle. The certificate and the host name are always verified. Setting it requires encryption, even to a server on the same host.
standby
false
With PostgreSQL, when another broker is using the database, wait and take over once it stops, instead of exiting. See Standby brokers.
pool_size
16
With PostgreSQL, the connections the broker keeps to the database. Each broker, standbys included, opens one more, which holds the database, so the server's max_connections must allow pool_size + 1 for each. Every client's writes and every lookup of retained messages share the pool.
pool_timeout
5
With PostgreSQL, the seconds a write waits for a free connection once all are in use. A write that times out is tried again, and is not lost.
Require clients to authenticate. Needed by password_file, dynamic_security, user_url and [auth.jwt]: the broker refuses to start when one of them is set and this is false. acl_file and acl_url work without it.
allow_anonymous
false
With authentication enabled, still accept clients that send no user name.
password_file
User names and password hashes.
acl_file
Topic access rules, in mosquitto's format.
dynamic_security
Dynamic security state file; replaces password_file and acl_file. Only in [auth].
scram_secret_file
File with 32 bytes of the secret that the SCRAM salts of unknown users come from. Created with random bytes and mode 0600 when missing, and the broker refuses to start when it has another length. Set it so the salts, and so the list of users, stay hidden across restarts. --check does not create it.
scram_require_channel_binding
false
Refuse SCRAM-SHA-256 without channel binding on TLS 1.3 connections, so clients there must use SCRAM-SHA-256-PLUS. Plain TCP, Unix sockets, TLS 1.2 and connections behind a TLS-terminating proxy cannot bind and are not affected, and plain user name and password CONNECTs on the same listener are still accepted. A refused client counts as a failed login. See SCRAM-SHA-256-PLUS.
Endpoint naming superusers, whom acl_url doesn't restrict and who may administer the broker over $CONTROL. Needs enabled = true.
timeout_seconds
5
Longest wait for an answer; a timeout denies. No longer than [server]plugin_timeout.
cache_seconds
30
How long topic answers are reused.
ca_path
PEM file of the CA certificates that https:// endpoints' certificates must chain to, in place of the bundled roots.
cert_path
PEM certificate chain presented to https:// endpoints that ask for one. Needs key_path.
key_path
PEM private key of cert_path.
[auth.jwt]
Clients log in with a JSON Web Token as their password. See JSON Web Tokens. Needs enabled = true; can't be combined with dynamic_security.
Option
Default
Meaning
secret_file
File holding the shared secret of HS256, HS384 and HS512, at least 32 bytes; one trailing newline is removed. Reread on reload.
public_key_file
PEM public key of RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 or EdDSA. Reread on reload.
jwks_url
JSON Web Key Set of the issuer, fetched at startup. https:// with a publicly trusted certificate, or http:// to a loopback address; redirects are not followed. Not with HMAC algorithms. Exactly one of secret_file, public_key_file and jwks_url is required.
algorithms
Required. The algorithms tokens may be signed with, such as ["RS256"], all taking the same kind of key. The token's header never chooses.
issuer
When set, tokens must carry this iss.
audience
When set, tokens must carry this aud.
username_claim
"sub"
Claim holding the user name, which must equal the CONNECT user name.
leeway_seconds
30
Clock skew allowed on exp and nbf.
disconnect_on_expiry
true
Disconnect a client with Not Authorized once its token expires, checked every [server]plugin_tick_interval.
jwks_refresh_seconds
300
Least time between two fetches of jwks_url, made when a token names a key the set lacks. At least 10, as any client can name a missing key.
[admin]
The admin socket, through which local administrators run every command without credentials. See Administration. Changes need a restart.
Option
Default
Meaning
socket
"/run/mercurio/admin.sock"
Path of the Unix socket; "" turns it off. Every directory on the path must be writable only by root or the broker's user. Linux only; elsewhere the section is ignored.
group
"mercurio"
Group whose members may use the socket, besides root and the broker's user, as its members were when the broker started. "" for nobody else.
With path, the group the socket belongs to. The broker's user must be a member, or root. Linux only.
mode
"0660" with group, "0600" without
With path, the socket's permission bits in octal. Clients need write permission to connect, so the default lets only the broker's user, and the members of group, in; "0666" lets in every local user. The socket is created owner-only and opened up once its group is set. A socket left by a broker that stopped is replaced; one another process serves stops the broker with an error. Linux only.
protocol
"mqtt"
"mqtt" or "websocket".
http_dir
Only with "websocket": serve this directory's files to plain HTTP requests.
health
false
Only with "websocket": answer GET /healthz with 200 while the broker serves. See Health and metrics.
metrics
false
Only with "websocket": answer GET /metrics with the statistics in the Prometheus text format. Anyone who reaches the listener learns how loaded the broker is, so use a listener of its own on a loopback address.
allowed_origins
any
Only with "websocket": the origins of the web pages that may connect, such as ["https://app.example.com"]. An upgrade with another Origin is refused with 403. Clients other than browsers send no Origin and are not affected.
proxy_protocol
false
Connections start with a PROXY protocol header, v1 or v2.
proxy_from
With proxy_protocol on a TCP listener, required: the addresses or CIDR blocks of the proxies allowed to send the header, such as ["10.0.0.0/8"], or ["0.0.0.0/0", "::/0"] for any peer. Connections from elsewhere are closed, as their header could claim any address.
mount_point
Prefixed to every topic the listener's clients use. Must end with / and hold no + or #.
max_connections
Concurrent connections on this listener, on top of the broker-wide limit.
bind_interface
Only accept connections arriving on this interface. Linux only.
[listeners.tls]
Same options as [server.tls].
plugins
all
Names of the [[plugins]] this listener's clients go through, such as ["deny"]. An unknown name is an error.
[listeners.auth]
Same options as [auth], except dynamic_security. Replaces [auth] for this listener.
[[bridges]]
Each entry connects to another broker. See Bridges.
Option
Default
Meaning
name
required
Names the bridge in logs and in its default client id.
host, port
port 1883
The remote broker.
addresses
Remote brokers as "host:port", tried in turn; replaces host and port.
protocol_version
"mqttv5"
"mqttv5" or "mqttv311".
client_id
"mercurio-bridge-<name>"
Client id on both brokers.
username, password
Credentials for the remote broker.
keepalive
60
Keep-alive with the remote broker, in seconds.
clean_start
false
Start a new session on the remote broker at each connection instead of resuming the one kept there.
session_expiry
86400
Seconds the remote broker keeps the session, and the messages for in topics, after the link drops.
notifications
true
Publish the bridge's state, retained, to notification_topic.
notification_topic
"$SYS/broker/connection/<client id>/state"
Where the state goes.
notifications_local_only
true
With false, publish the state on the remote broker too, which publishes 0 through the bridge's will when the link drops.
[bridges.tls]
enabled, ca_path, and cert_path with key_path for mutual TLS.
[[bridges.topics]]
Option
Default
Meaning
pattern
required
Topic filter to forward.
direction
"out"
"out" to the remote broker, "in" from it, or "both".
qos
0
QoS of the bridge's subscriptions.
local_prefix
""
Where pattern sits on this broker.
remote_prefix
""
Where pattern sits on the remote broker.
[[plugins]]
Each entry loads a WebAssembly plugin, run in the order written, after the built-in authentication and access control. See WebAssembly plugins. Two plugins can't have the same name.
Option
Default
Meaning
path
required
The component, a .wasm file. SIGHUP rereads it.
name
file name without .wasm
Names the plugin in logs, in $CONTROL/<name>/... and in listener plugins. It can't be empty, contain /, + or #, or be the name of a built-in plugin: dynamic-security, password-file, http-auth, acl-file, plain or credentials.
config
{}
Table of strings passed to the plugin's init.
allow_http
[]
Hosts it may send HTTP requests to, as "host" (port 443) or "host:port". The scheme isn't checked, so a plain-HTTP endpoint on the default port needs "host:80".
allow_sockets
[]
Addresses it may connect sockets to, as "address:port". It can never listen.
instances
number of CPUs
Copies that run calls at once.
timeout_ms
[server]plugin_timeout
Longest one call, or init, may run. No longer than plugin_timeout, which bounds every hook call too.
memory_mb
64
Linear memory each copy may use, all its memories together.
Only the file at path is reread on SIGHUP; other changes need a restart.