XMQ
Credits:The XMQ web site is provided by Linotex.The XMQ and SPTK Windows installers are created with Advanced Installer free Open-Source license.
There were 0 unique visitors to this page

XMQ MQTT Server Documentation

XMQ User Manual

XMQ is an MQTT 5, 3.1.1, and 3.1 server. This manual covers installing it, tuning the host it runs on, configuring it from the configuration interface, and proving that the result works.

Supported operating systems

XMQ is built and packaged for the systems below. Each is a 64-bit x86 build, packaged in the format native to that system.

SystemVersionsPackage
Debiantrixie, forky.deb
Ubuntu25.04, 25.10, 26.04.deb
Fedora42, 43, 44.rpm
Oracle Linux10.rpm
Windows10, 11, Serverinstaller

Other Linux distributions of comparable vintage generally work when built from source, but only the versions listed here are tested and packaged.

Download and install binaries

The only official download location is here. Select the latest SPTK version and your operating system, and download xmq_server package.

Packages are named XMQ-server and install under /usr/localon Linux. The server, the utilities, the configuration interface, and the load-test scenarios all come from that one package.

The dependencies for Linux include brotli and pcre2 packages that are included in all major Linux distributions. The dependencies for Windows are installed with XMQ.

The package file name carries the version and the architecture, so the commands below use a wildcard. Run them in the directory the package was downloaded to.

Debian and Ubuntu
sudo apt install ./xmq-server_*.deb
Fedora and Oracle Linux
sudo dnf install ./XMQ-server-*.rpm

Installing through the package manager rather than dpkg -i orrpm -i lets it resolve the shared libraries the package depends on.

What gets installed
PathContents
/usr/local/binxmq_server and the xmq_pub, xmq_sub,xmq_con, xmq_scn utilities
/etc/xmq/xmq_server.confServer configuration. An existing file is never overwritten by an upgrade
/etc/xmq/xmq_server.conf.templateThe shipped defaults, refreshed on every upgrade, for comparison
/etc/xmq/certsTLS certificates and keys installed from the SSL Keys page
/var/log/xmqServer log
/usr/local/share/xmqLoad-test scenarios, host tuning files, helper scripts

On Windows the equivalents live under C:\ProgramData\xmq, with the program files in C:\Program Files\xmq.

Running the server

The package installs a systemd unit, so on Linux the server is managed the usual way:

sudo systemctl enable --now xmq_server
sudo systemctl status xmq_server
sudo journalctl -u xmq_server -f

On Windows, xmq_server --install-service registers it as a Windows service, and --uninstall-service removes it.

Useful options when running it by hand:

OptionMeaning
-s, --consoleStay in the foreground instead of self-daemonizing. This is what the systemd unit uses, so that systemd supervises the real process
-c, --configuration-fileUse a configuration file other than the default
-l, --log-fileWrite the log somewhere other than the configured path
--no-cpu-affinityStop pinning threads to physical cores. Affinity is the default and is Linux-only; it understands CPU sets, so it cooperates with containers
-d, --debugVerbose logging, regardless of the configured levels
--versionPrint the version and exit

Once the server runs, the configuration interface is reachable on port 18883 by default:http://<server>:18883. Sign in with an account from the Users page; a fresh installation ships with admin.

Change the shipped passwords before putting the server on a network anyone else can reach. The defaults are published in the template file, so they are not secret.

Environment tuning

A default Linux install is tuned for a few thousand sockets, not a few hundred thousand. Below roughly 10,000 concurrent connections the stock settings are fine and this section can be skipped. Above that, the limits below are the ones that bite, and each of them has been hit in real test runs.

Ready-made files are installed with the package under/usr/local/share/xmq/setup, along with a README describing the symptom each one addresses:

FileInstall toPurpose
sysctl.d/mqtt.conf/etc/sysctl.d/File descriptor ceiling, socket buffers, accept queue depth, TCP memory
sysctl.d/port_range.conf/etc/sysctl.d/Widen the ephemeral port range to 10000-65535
limits.d/mqtt.conf/etc/security/limits.d/nofile for login sessions
modules-load.d/nf_conntrack.conf/etc/modules-load.d/Load nf_conntrack before systemd-sysctl runs
nftables.d/xmq-notrack.nft/etc/nftables.d/Exempt the MQTT ports from connection tracking
cd /usr/local/share/xmq/setup
sudo install -m 0644 sysctl.d/mqtt.conf       /etc/sysctl.d/mqtt.conf
sudo install -m 0644 sysctl.d/port_range.conf /etc/sysctl.d/port_range.conf
sudo install -m 0644 limits.d/mqtt.conf       /etc/security/limits.d/mqtt.conf
sudo modprobe nf_conntrack
sudo sysctl --system

Then log out and back in, so the nofile limit applies to your shell.

The three that catch people out
  • One descriptor per connection, plus a handful. The server idles at 17 descriptors, so a limit of exactly 1,000,000 fails at 999,983 connections. The shipped files set 2,000,000, which leaves room.
  • Services do not read limits.d. That file applies to login sessions only. A systemd service gets DefaultLimitNOFILE instead, commonly 524288. The XMQ unit sets LimitNOFILE explicitly for this reason; any unit you write yourself must do the same.
  • A ulimit -n in your shell profile wins. If~/.profile or ~/.bashrc lowers it, an unprivileged process cannot raise it again, and the limit files appear to have no effect.

Both ends need this. Every limit here is per host, and tuning only the server while leaving the client machine at defaults is the most common mistake. Thepreflight.sh script in /usr/local/share/xmq checks a host and exits non-zero if a required setting is missing:

./preflight.sh                          # check this host as a client
./preflight.sh -H broker -r server -p 1883   # check a remote server host

Basic MQTT setup

Everything in this section can be set from the configuration interface, and is written back to/etc/xmq/xmq_server.conf. The user data is stored in the file/etc/xmq/xmq_users.conf. Hand-editing these files is still possible; if you do, restart the server afterwards, since a running server rewrites the files when settings change and would overwrite your edit.

The XMQ configuration interface is available through the browser at http://xmq_host:18883. The xmq_host here is the host name where XMQ server is running. The default administrative user credentials are: The username is "admin" and the password is also "admin". It's highly recommended to change the default password.

Port numbers

Listeners are configured on the Listeners page. Each one is a port, a protocol, and a thread count. The shipped configuration defines two:

PortProtocolPurpose
1883MQTTPlain TCP, the standard MQTT port
8883MQTT+SSLTLS, the standard secure MQTT port
18883HTTPThe configuration interface and its control API

These are the standard MQTT ports, so most clients need no port setting at all. If another broker on the same host already holds them, either stop it or move one of the two: the Listeners page changes XMQ's ports, and the change takes effect as soon as the listener is saved.

A listener also carries a bind address. 0.0.0.0 accepts on every interface; naming one address restricts the listener to it, which is a simple way to keep a port on an internal network. Disabling a listener keeps its settings but closes its port.

The service port itself is set on the Service page. Changing it takes effect after a restart, and the configuration interface then answers on the new port only.

SSL certificates

An MQTT+SSL listener needs a server key and certificate. Install them from the SSL Keys page: choosing a file uploads its contents, and the server writes it into /etc/xmq/certs(C:\ProgramData\xmq\certs on Windows) and records the path. A previous file of the same name is kept beside the new one with an .old suffix.

  • Server key and server certificate are required for TLS.
  • CA certificate is needed only to verify client certificates.
  • Verify depth of 0 means clients are not asked for a certificate: the TLS listener proves the server's identity and no more. Above 0, a client must present a certificate that the CA certificate validates, within that many intermediate signers.

If the keys are missing or fail to load, the server logs an error, skips the MQTT+SSL listener, and carries on serving the plain one. That is worth checking in the log after installing certificates for the first time.

Logging

The Logging page sets the log file and the level per subject. Theminimum log level is a ceiling rather than a floor: each subject has its own level, and the server clips it to this one. Turning the ceiling down toINFO quietens everything without touching the individual subjects, and turning it up to DEBUG lets them speak at their configured level.

Levels are PANIC, ERROR, WARNING,NOTICE, INFO, DEBUG, from quietest to most verbose. Two subjects deserve care: Publish and Acks produce a line per message, not per client. At the rates XMQ is built for, DEBUGthere can outweigh everything else the server logs and cost real throughput. Leave them at ERROR unless you are chasing something specific.

Log rotation is left to the system. Session errors are always logged atERROR and are not configurable.

Users and anonymous access

Accounts on the Users page serve both MQTT clients and the configuration interface. An administrator may change the configuration; a non-administrator can connect but not administer. Allow anonymous lets MQTT clients connect with no credentials at all; it does not affect the configuration interface, which always requires signing in.

Persistence

Without persistence the server keeps everything in memory and nothing survives a restart. With it, client sessions, their subscriptions, and undelivered QoS 1 and QoS 2 messages are stored in Redis. Redis is a separate service, not part of XMQ: install and start it yourself, then enable persistence on the Persistencepage.

If the server cannot reach Redis at startup it logs the error and keeps running with in-memory storage. The server stays up and nothing is persisted, so the log is worth a look after enabling it.

One setting there is a genuine trade-off. Max queued writes at 0 makes every message wait for its own record to become durable before it is sent: the safest setting, and the slowest. Above 0, record writes pipeline instead, roughly doubling throughput, and the value is approximately how many messages could be lost if the server were killed outright.

Advanced setup

MQTT bridges

A bridge is a client connection XMQ makes to another MQTT broker, carrying an agreed set of topics. The remote broker does not have to be XMQ: bridging to Mosquitto, EMQX, or anything else speaking MQTT works the same way. Bridges are configured on the Bridges page.

A bridge has a direction, and so does each of its topics:

ModeMeaning
outMessages published on this server are forwarded to the remote one
inThis server subscribes to the remote one and republishes what it sends
inoutBoth directions

Each topic entry carries a pattern, a direction of its own, and the QoS to subscribe with. A topic's direction narrows the bridge's: a topic markedout on an inout bridge travels outbound only. Topics keep the name they were published with; there is no rewriting between the two servers. A bridge with no topics carries nothing.

Things worth getting right
  • Client ID must be stable. The remote broker keys the bridge's stored subscriptions and queued messages by it. An id that changes between reconnects leaves orphaned sessions accumulating on the remote. Left empty, XMQ derives one from the two node names, which is stable.
  • Leave clean session off. The remote then keeps the subscriptions and queues messages while the link is down, so a brief outage does not lose them. With it on, the session starts empty each time and anything published while disconnected is gone.
  • Loops. XMQ marks its bridge connections with an origin node, so it will not echo back a message that arrived over the bridge. Bridging through a broker that does not do the same, in both directions, on overlapping topics, can still produce a loop.
  • Encrypted bridges use key files already present on the XMQ host, given by path. That is the opposite of the SSL Keys page, which uploads content. The remote broker must be listening on its TLS port.
Bridging two XMQ servers, step by step

This walks through joining two XMQ servers so that a client on either one sees traffic published on the other. Call them mq-a and mq-b; substitute your own host names throughout. One bridge is enough, defined on mq-a and pointing at mq-b, in inout mode - that single bridge carries both directions. Do not define a second one on mq-b pointing back; see the end of this section for why.

The shipped configuration defines no bridges and no cluster nodes, so the Bridges page is empty on a fresh installation. Everything below is added by hand.

Before you start

  • Both servers installed, running, and reachable from each other on their MQTT port - a bridge is an ordinary outbound MQTT connection, so a firewall between the hosts needs that port open in both directions.
  • Decide which topics to carry. This example uses test/#. Prefer a specific prefix to #: it keeps the link's traffic to what you actually meant to share.
  • An account on each server for the other's bridge to log in as. The bridge authenticates like any client, so it needs a real account on theremote server. A non-administrator account is enough; create it on theUsers page if you do not already have one.

Step 1 - add the bridge on mq-a

Open http://mq-a:18883, sign in as an administrator, and go to theBridges page. It is a master-detail page: the bridge list on top, and the settings of the selected bridge below. Press Add, fill the settings in, and press Save Bridge:

FieldValue on mq-aNotes
Node namemq-bThe name this server knows the remote by. It appears in the log and marks messages that arrived over the link, which is how loops are prevented. A name already used by another bridge is rejected.
Host / portmq-b / 1883The remote MQTT listener, not its configuration port. A second bridge to the same host and port is rejected: it would carry every message twice.
ModeinoutBoth directions.
Usernamethe account on mq-bCredentials for the remote server, not this one.
Passwordits password
Client IDleave emptyXMQ then derives a stable id from the two node names. Set one only if the remote requires a particular id, and never one that varies.
Enabledticked
Clean sessionuntickedSo mq-b keeps the subscriptions and queues messages while the link is down.
EncrypteduntickedTick it only when bridging to the remote's TLS port; it reveals the key file fields, which are paths on this host.

Then fill the Topics table below the settings. Press its Add and give the topic a Pattern of test/#, a Direction ofinout, and a QoS of 1. A bridge with no topics connects and carries nothing, so this table is not optional.

Press Save Bridge. The bridge appears in the list with its topic count, and the configuration file is written immediately.

Step 2 - nothing to do on mq-b

mq-b needs no bridge of its own. It needs only the account the bridge signs in with, which it already has if you used one of its existing users. Nothing on mq-b records that a bridge exists: to it, mq-a's bridge is an ordinary client that subscribes and publishes.

Step 3 - press Apply on each server

This step is easy to miss. Saving a bridge stores it, but does not start the connection. Apply, on the Bridges page, rebuilds the connections from the configuration as it now stands: the bridges that were running are stopped, and the configured ones are started. Press it on mq-a.

Neither server has to be restarted, and mq-b does not have to be up yet: a bridge whose remote is not answering retries on its own, the wait between attempts growing to half a minute and dropping back as soon as it connects.

Restarting the server has the same effect, since bridges are started with it, and remains the way to apply a change made by editing the configuration file directly:

sudo systemctl restart xmq_server

Step 4 - confirm the link came up

With Connections at DEBUG on the Logging page, mq-a's log carries two lines for the bridge:

Bridge to mq-b forwarding 1 outbound topic(s).
Bridge to mq-b (mq-b:1883) connected, 1 inbound topic(s).

Both counts matter. connected with 0 inbound topics means the bridge reached the remote but carries nothing - almost always an empty or mistyped Topics field.

Step 5 - prove a message crosses

Subscribe on one server and publish on the other. The subscriber has no other way to see the message, so receiving it is itself the proof the bridge carried it:

xmq_sub -h mq-b -p 1883 -u user -P secret -t test/bridge -q 1 -v -C 1 -W 15

xmq_pub -h mq-a -p 1883 -u user -P secret -t test/bridge -q 1 -m "over the bridge"

Then swap the two hosts and repeat, to check the other direction: oneinout bridge carries both, but the two use different halves of it, and it is quite possible for one to work while the other does not.

For a measured run rather than a single message, xmq_scn can publish to one server and subscribe on the other:

xmq_scn -s Basic/Point-To-Point-1K-1K-1K-1K.json \
        --host mq-a --port 1883 --subscriber-host mq-b --subscriber-port 1883

Every message it counts has crossed the bridge, so the reported latency includes the extra broker hop and the round trip between the hosts. Compare it against the same scenario run without --subscriber-host to see what the link costs.

If nothing crosses

  • No bridge line in the log at all - Apply was not pressed after the bridge was saved, or the bridge is not enabled. Saving stores the bridge; it does not start it.
  • Repeated connect failures - the host or port is wrong, the port is blocked, or the credentials name an account that does not exist on theremote server. The log names the reason.
  • Connected, nothing carried - the topic pattern does not cover the topic being published. Patterns are matched as MQTT subscriptions:test/# covers test/bridge, while teston its own does not.
  • One direction only - check the mode on both bridges. A bridge inout mode forwards but never subscribes.
  • Everything arrives twice - a bridge is defined on both servers, each covering the same topics. One inout bridge carries both directions by itself, so the second one only moves the same message a second way; remove it. The subscribers on the publishing server still see one copy, which is what makes this look like a delivery fault rather than a configuration one. This is explained under Why one bridge, and not one on each server below.

Why one bridge, and not one on each server

An inout bridge already carries both directions on its own: it subscribes on the remote for what comes in, and subscribes locally for what goes out. A second bridge, defined on the other server and pointing back, adds nothing but a second copy - the message arrives once because this server pulled it, and again because the other server pushed it. Both copies are legitimate deliveries, so nothing detects or suppresses them: a subscriber simply receives everything twice.

Define the bridge on one of the two servers only. Which one does not matter.

Bridged traffic is not sent back the way it came - a message is marked with the node it arrived from, and a bridge subscription is never given a marked message. That marking is XMQ's own, so it only covers the bridges XMQ itself makes. When the broker at the other end is doing the bridging, what protects you is described next.

More than two servers

Bridged traffic is not passed on. A message that reached this server over one bridge is delivered to its own subscribers, but it is not handed to a second bridge - that is the same rule that stops a message going back where it came from, and it cannot tell "back" from "onward".

So the shapes that look natural do not work. In a chainA - B - C, B sees everything from both, but A and C never see each other. In a star, the hub sees every spoke and no spoke sees another.

Give every pair its own bridge instead. Three servers need three:A-B, A-C and B-C, each defined once, on either end. Every server then has a direct link to every other, so nothing needs relaying - and because nothing is relayed, nothing is duplicated either: each message arrives exactly once at each server.

The cost is that the number of bridges grows as the square of the number of servers: three servers need three bridges, four need six, five need ten. Bridging suits a handful of servers, and the arithmetic is what limits it.

Bridging to a broker that is not XMQ

A bridge to Mosquitto, EMQX, or anything else speaking MQTT is configured exactly as above; only the remote's own settings differ. One of them is worth getting right, because it decides whether messages can circulate endlessly between the two brokers.

Configure the remote's bridge to use MQTT 5. In Mosquitto that is one line in its bridge block:

connection xmq
address xmq-host:1883
topic test/# both 0 "" ""
remote_username user
remote_password secret
bridge_protocol_version mqttv50

MQTT 5 defines two subscription options that exist for precisely this purpose.No Local tells the broker not to send a subscriber back what that same connection published, which is what stops a message going round; and Retain As Published keeps a retained message retained as it crosses. A bridge that subscribes with them cannot be echoed to, and XMQ honours both, whoever set them. XMQ's own bridges set them too, which is why a bridge between two XMQ servers needs nothing configured for this.

Mosquitto defaults to mqttv311 instead. Such a bridge still connects and still carries messages, but the loop protection is absent: MQTT 3.1.1 has no subscription options to carry it, and the non-standard "bridge" protocol variant Mosquitto tries first - a protocol level with the high bit set - is not something XMQ accepts. Mosquitto notices the refusal and reconnects as an ordinary client, so the only visible cost is one rejected connection attempt, and the invisible one is that XMQ cannot tell that client is a bridge.

With a 3.1.1 remote bridge, then, keep the topic patterns from overlapping in both directions - carry a/# one way and b/# the other, rather than # both ways - or drive the link entirely from the XMQ side, where the origin marking applies.

Server limits

The Server Limits page holds the thread counts and the per-session bounds. The rule for threads is short: XMQ pins its threads to physical cores, and no thread group should be larger than the host's physical core count. The groups share those cores, so count the send threads, the receive threads, the persistence threads and the per-listener threads together rather than each on its own. Adding threads past that buys contention, not throughput.

MQTT testing suite

xmq_scn runs load-test scenarios described by JSON files. It is broker-neutral: it speaks plain MQTT and can be pointed at Mosquitto, EMQX, NanoMQ or anything else, which is what makes its numbers comparable across brokers.

Scenarios ship in /usr/local/share/xmq. List them with:

xmq_scn --list-scenarios

A scenario file names the client groups, their counts, the topics, and the pacing. The shipped set covers connection ramps, fan-in, fan-out, and point-to-point, at sizes from 10,000 to 1,000,000 connections. Writing a new one is a matter of copying the closest and changing the numbers.

xmq_scn -s Point-To-Point-50K-50K-50K-50K.json --progress
xmq_scn -s 1M-Connections-5K-rate.json -h broker -p 1883
OptionMeaning
-s, --scenarioScenario file. A relative path is looked up in the current directory first, then in the installed scenario directory
-m, --payload-sizeMessage size, overriding the scenario
-r, --publish-rateMessages per second per publisher. 0 means unpaced: as fast as possible
-d, --durationHow long to run
-q, --qosQuality of service to publish with
--max-inflightCap on a publisher's un-acknowledged QoS 1 backlog, so it throttles against its own round-trip time rather than publishing blindly at the configured rate
--id-prefixClient id prefix, so several client hosts don't collide
--progressShow a progress bar

Reaching the larger sizes needs the tuning above on both hosts, and enough source addresses on the client: a connection is identified by its source address and port, so one address runs out of ports long before a million connections. The tested 1,000,000-connection runs used 30 client addresses, about 33,000 connections each. The make_ip_addresses.sh helper in the same directory adds them, andremove_ip_addresses.sh takes them away again:

./make_ip_addresses.sh -s <first three octets> -p <prefix> -f <first host octet> -c 29

Scenarios spread their clients across whatever addresses the host has, so the--id-prefix option keeps client ids distinct when several client machines drive the same broker.

Run the load generator on a different machine from the broker. Sharing a host means measuring the two competing for the same cores, which flatters neither.

Testing your setup

Three small utilities are enough to prove an installation works. All of them take-h host, -p port, -u username,--password, and --version.

Is the server up?
systemctl status xmq_server
tail -f /var/log/xmq/xmq_server.log

The log names each listener it opened at startup, and says which storage it is using: a Redis address, or memory when persistence is off or Redis could not be reached.

Subscribe and publish

In one terminal, subscribe. In another, publish, and watch it arrive:

xmq_sub -h localhost -p 1883 -u user --password secret -t "test/#" -q 1 -v

xmq_pub -h localhost -p 1883 -u user --password secret -t test/hello -m "it works"

Useful xmq_pub options: -r repeats the message a number of times, -g paces it to a rate, -f takes the payload from a file, -s reads it from standard input, and -l stamps a send timestamp into the payload so latency can be measured at the far end.

Testing TLS
xmq_sub -h localhost -p 8883 -u user --password secret \
        -t "test/#" -q 1 -v --cafile /etc/xmq/certs/ca.crt

If this fails while the plain port works, the certificates are the place to look: the server log says whether it managed to load them at startup.

Many connections at once

xmq_con opens a number of sessions and holds them, which is the quickest way to see whether the host limits are in place:

xmq_con -h localhost -p 1883 -u user --password secret -n 10000 --show-counters

If it stops short of the number asked for, the file descriptor limit is the usual cause, on whichever host ran out first. Check both, then see the tuning section.

Checking a bridge

Subscribe on the remote broker, publish on XMQ, and confirm the message crosses:

xmq_sub -h remote-broker -p 1883 -t "bridged/#" -q 1 -v

xmq_pub -h localhost -p 1883 -u user --password secret -t bridged/test -m "over the bridge"

Nothing arriving usually means the bridge is disabled, its topic pattern does not cover the topic, or its direction excludes it. The server log, withConnections at DEBUG, shows the bridge connecting and how many topics it subscribed to.