Migrating from mosquitto

Mercurio reads mosquitto's password and ACL files as they are and publishes the same $SYS topics. This page maps each mosquitto option to its Mercurio equivalent.

Step by step

  1. Point password_file and acl_file at your existing files. Hashes written by mosquitto_passwd, $7$ and $6$, are recognised.
  2. Translate mosquitto.conf into TOML with the table below.
  3. Start mercuriod on another port, point a few clients at it, then move the rest.

Retained messages and persistent sessions are not imported from mosquitto's database; clients with persistent sessions start new ones, and retained messages need to be published again.

A typical configuration

mosquitto.confconfig.toml
listener 1883 127.0.0.1

listener 8883
certfile /etc/mosquitto/server.crt
keyfile /etc/mosquitto/server.key

allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl

persistence true
persistence_location /var/lib/mosquitto/
[server]
host = "127.0.0.1"
port = 1883

[[listeners]]
host = "0.0.0.0"
port = 8883
[listeners.tls]
enabled = true
cert_path = "/etc/mosquitto/server.crt"
key_path = "/etc/mosquitto/server.key"

[auth]
enabled = true
allow_anonymous = false
password_file = "/etc/mosquitto/passwd"
acl_file = "/etc/mosquitto/acl"

[storage]
backend = "sqlite"
path = "/var/lib/mercurio/mercurio.db"

In mosquitto 2, per_listener_settings decides whether authentication is global or per listener. In Mercurio, [auth] applies everywhere, and a listener with its own [listeners.auth] uses that instead.

Option mapping

Listeners

mosquittoMercurio
listener PORT [ADDRESS][server] port/host, or a [[listeners]] entry
listener 0 /path/to/socket[[listeners]] path
protocol websocketsprotocol = "websocket", or [server] websocket_port
http_dirhttp_dir
bind_interfacebind_interface
socket_domainthe address in host, such as "::"
mount_pointmount_point
max_connections[server] max_connections, and per listener
enable_proxy_protocolproxy_protocol = true, with proxy_from naming the proxies

TLS

mosquittoMercurio, in [server.tls] or [listeners.tls]
certfile, keyfilecert_path, key_path, with enabled = true
cafile with require_certificate trueca_path
cafile with require_certificate falseca_path with require_certificate = false
crlfilecrl_path, with crl_end_entity_only = true to check only the client's own certificate as mosquitto does; by default its intermediate CAs need CRLs too
use_identity_as_usernameidentity_as_username
use_subject_as_usernamesubject_as_username
tls_version tlsv1.3min_version = "1.3"
psk_hint, psk_filenot supported

Security

mosquittoMercurio
allow_anonymous[auth] enabled = true with allow_anonymous
password_file, acl_filethe same, in [auth]
dynamic security plugin[auth] dynamic_security, with the same commands and the same order of roles, groups and priorities; convert mosquitto's dynamic-security.json, passwords included, with mercuriod dynsec-import, or start a new file with mercuriod dynsec-init
mosquitto_ctrl dynsec …mercurioctl dynsec …, with the same commands in kebab-case; mosquitto_ctrl dynsec init is mercuriod dynsec-init
mosquitto-go-auth HTTP backend[auth.http]
none: mosquitto has no built-in enhanced authentication methodSCRAM-SHA-256, offered with password_file and dynamic_security. Passwords from mosquitto_passwd or dynsec-import have no SCRAM keys: they keep working for plain logins and get keys when set again
check_retain_sourcecheck_retain_source
allow_zero_length_clientid, auto_id_prefix, clientid_prefixesthe same, in [server]

Limits and behaviour

mosquittoMercurio, in [server]
max_queued_messages, max_queued_bytesmax_queued_messages, max_queued_bytes; also max_queued_qos0_messages
max_packet_size, message_size_limitmax_packet_size
max_keepalivemax_keepalive, for MQTT 5 clients only; see below
max_qos, retain_availablethe same
max_inflight_messagesmax_inflight, for messages to clients, lowered further by the client's own Receive Maximum; receive_maximum for QoS 2 from clients
upgrade_outgoing_qosupgrade_outgoing_qos
use_username_as_clientiduse_username_as_clientid, in [[listeners]]; the main listener has none, so serve such clients on an extra listener
persistent_client_expiration 1wpersistent_client_expiration = 604800, in seconds
sys_intervalsys_interval
persistence, persistence_location[storage] backend = "sqlite", path; written as changes happen
include_dirinclude_dir, with *.toml files

Logging

mosquittoMercurio, in [logging]
log_dest stdoutthe default
log_dest file PATHfile = "PATH"
log_dest syslogsyslog = true
log_typelevel, per module if needed
connection_messagesconnection_messages

Bridges

mosquittoMercurio, in [[bridges]]
connection NAMEname
address host:port [host:port …]host/port, or addresses
topic PATTERN DIRECTION QOS LOCAL REMOTEa [[bridges.topics]] entry: pattern, direction, qos, local_prefix, remote_prefix
bridge_protocol_version mqttv311protocol_version = "mqttv311"
remote_clientid, remote_username, remote_passwordclient_id, username, password
bridge_cafile, bridge_certfile, bridge_keyfile[bridges.tls] ca_path, cert_path, key_path
notificationsnotifications
notification_topicnotification_topic
notifications_local_onlynotifications_local_only; true by default, where mosquitto's default is false, as Mercurio and other brokers refuse client publishes to the default $SYS topic
keepalive_intervalkeepalive
cleansessionclean_start
restart_timeoutnone: the wait between attempts doubles from a second up to a minute, with jitter

MQTT 5 bridges also take session_expiry, the seconds the remote broker keeps the session, a day by default. Not supported yet: bridge_attempt_unsubscribe, bridge_bind_address, bridge_capath, bridge_insecure, bridge_alpn, bridge_identity and bridge_psk, bridge_max_packet_size, bridge_outgoing_retain, bridge_reload_type, bridge_require_ocsp, bridge_tls_version, idle_timeout, local_cleansession, local_clientid, local_username and local_password (the local side connects in process), round_robin (addresses are always tried in turn), start_type (bridges always start with the broker), threshold and try_private.

Differences to know