Multiple Mosquitto instances on Debian with Systemd

I have been playing for quite a while MQTT to have my Arduino devices communicate with Node-Red for all kinds of automation. Due to the constraints of the Arduino’s, so far I have not been able to effectively secure this as SSL/TLS is not an option on these devices.

Mosquitto supports multiple listeners out of the box though aside from the listener-specific settings all listeners share the same global (e.g. authentication and plugin) settings. To secure my setup and to work on a more permanent solution I needed different settings per listener for which multiple Mosquitto instances are required (i.e. multiple instances of the mosquitto daemon with their configuration and listeners different IP addresses / ports). This was not difficult on my Debian server but as I did not find much documentation on this I will document the necessary steps in this post.

The Debian mosquitto package does not provide support for multiple instances. SystemD (the sysv-init replacement shipped in recent Debian versions) however makes it quite easy to do this anyway. The steps below describe what has to be done to setup an instance named mosquitto_dev (which I needed for development) but you can name it as you like (just replace mosquitto_dev below for whatever name you choose). Please note that this assumes that you have already installed and enabled the mosquitto package and not customized it too much.

The first step is to create the necessary directories for the configuration/state and copy the default mqtt config files with:

sudo install -d -m 0755 -o root -g root /etc/mosquitto/conf_dev.d/
sudo install -d -m 0755 -o mosquitto -g root /var/lib/mosquitto_dev
sudo cp -a /etc/mosquitto/mosquitto.conf /etc/mosquitto/mosquitto_dev.conf
sudo cp -a /etc/mosquitto/conf.d/mqtt.conf /etc/mosquitto/conf_dev.d/
sudo cp -a /etc/mosquitto/conf.d/listener_mqtt.conf /etc/mosquitto/conf_dev.d/

Open /etc/mosquitto/mosquitto_dev.conf with an editor and modify it so that it looks like this:

# Place your local configuration in /etc/mosquitto/conf_dev.d/
#
# A full description of the configuration file is at
# /usr/share/doc/mosquitto/examples/mosquitto.conf.example

pid_file /var/run/mosquitto_dev.pid

persistence true
persistence_location /var/lib/mosquitto_dev/

log_dest file /var/log/mosquitto/mosquitto_dev.log

include_dir /etc/mosquitto/conf_dev.d

The actual configuration for the instance will now be under /etc/mosquitto/conf_dev.d. Review the settings in /etc/mosquitto/conf_dev.d/mqtt.conf (there is no real need to change it if it wasn’t changed after package installation to make the setup work.

Next edit /etc/mosquitto/conf_dev.d/listener_mqtt.conf and check that the value of the listener config parameter are unique (i.e. port/address combination is different from the main instance). To make the new instance listen to the non-standard port 1884 set this to:

listener 1884

To check the configuration, issue the following command to start the daemon with instance configuration in verbose mode:

sudo mosquitto -v -c /etc/mosquitto/mosquitto_dev.conf

This should result in an MQTT server running on the configured port and address and not give any error messages. If so, press CRTL-C to abort the daemon. Also check the logfile /var/log/mosquitto/mosquitto2.log to ensure there were no issues with the configuration or resolve them and retest before you continue with the next steps to automate it’s start/stop by systemd.

As already mentioned, the Debian package does not support multiple instances even though the setup turned out to be pretty simple. All that is required is a separate start-up script in /etc/init.d for each of the instances similar to the one that comes with the Debian package and have it picked up by systemd, which does that automatically. First, as root, create the file /etc/init.d/mosquitto_dev with the following content:

#! /bin/sh

# JFL 20190529: patched startup script for multiple mosquitto instances. Rename 
# to an instance name, e.g. mosquitto_dev to initiate an instance expecting its
# config in /etc/mosquitto/mosquitto_dev (i.e. based on this script's name)

### BEGIN INIT INFO
# Provides:		mosquitto
# Required-Start:	$remote_fs $syslog
# Required-Stop:	$remote_fs $syslog
# Default-Start:	2 3 4 5
# Default-Stop:		0 1 6
# Short-Description:	Additional mosquitto MQTT v3.1 message broker
# Description:
#  This is a message broker that supports version 3.1 of the MQ Telemetry
#  Transport (MQTT) protocol.
#
#  MQTT provides a method of carrying out messaging using a publish/subscribe
#  model. It is lightweight, both in terms of bandwidth usage and ease of
#  implementation. This makes it particularly useful at the edge of the network
#  where a sensor or other simple device may be implemented using an arduino for
#  example.
### END INIT INFO

set -e

NAME=`basename $0`
PIDFILE=/var/run/$NAME.pid
DAEMON=/usr/sbin/mosquitto

CONFFILE=/etc/mosquitto/$NAME.conf

# /etc/init.d/mosquitto: start and stop the mosquitto MQTT message broker

test -x ${DAEMON} || exit 0

umask 022

. /lib/lsb/init-functions

# Are we running from init?
run_by_init() {
    ([ "$previous" ] && [ "$runlevel" ]) || [ "$runlevel" = S ]
}

export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"

case "$1" in
  start)
	if init_is_upstart; then
	    exit 1
	fi
	log_daemon_msg "Starting network daemon:" "$NAME"
	if start-stop-daemon --start --quiet --oknodo --background  --make-pidfile --pidfile ${PIDFILE} --exec ${DAEMON} -- -c ${CONFFILE} ; then
	    log_end_msg 0
	else
	    log_end_msg 1
	fi
	;;
  stop)
	if init_is_upstart; then
	    exit 0
	fi
	log_daemon_msg "Stopping network daemon:" "$NAME"
	if start-stop-daemon --stop --quiet --oknodo --pidfile ${PIDFILE}; then
	    log_end_msg 0
	    rm -f ${PIDFILE}
	else
	    log_end_msg 1
	fi
	;;


  reload|force-reload)
	if init_is_upstart; then
	    exit 1
	fi
	log_daemon_msg "Reloading network daemon configuration:" "$NAME"
        if start-stop-daemon --stop --signal HUP --quiet --oknodo --pidfile $PIDFILE; then
            log_end_msg 0
        else
            log_end_msg 1
        fi
	;;

  restart)
	if init_is_upstart; then
	    exit 1
	fi
	log_daemon_msg "Restarting network daemon:" "$NAME"
	if start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile ${PIDFILE}; then
	    rm -f ${PIDFILE}
	fi
	if start-stop-daemon --start --quiet --oknodo --background --make-pidfile --pidfile ${PIDFILE} --exec ${DAEMON} -- -c ${CONFFILE} ; then
	    log_end_msg 0
	else
	    log_end_msg 1
	fi
	;;

  try-restart)
	if init_is_upstart; then
	    exit 1
	fi
	log_daemon_msg "Restarting Mosquitto message broker" "$NAME"
	set +e
	start-stop-daemon --stop --quiet --retry 30 --pidfile ${PIDFILE}
	RET="$?"
	set -e
	case $RET in
	    0)
		# old daemon stopped
		rm -f ${PIDFILE}
		if start-stop-daemon --start --quiet --oknodo --background --make-pidfile --pidfile ${PIDFILE} --exec ${DAEMON} -- -c ${CONFFILE} ; then
		    log_end_msg 0
		else
		    log_end_msg 1
		fi
		;;
	    1)
		# daemon not running
		log_progress_msg "(not running)"
		log_end_msg 0
		;;
	    *)
		# failed to stop
		log_progress_msg "(failed to stop)"
		log_end_msg 1
		;;
	esac
	;;

  status)
	if init_is_upstart; then
	    exit 1
	fi
	status_of_proc -p ${PIDFILE} ${DAEMON} mosquitto && exit 0 || exit $?
	;;

  *)
	log_action_msg "Usage: $0 {start|stop|reload|force-reload|restart|try-restart|status}"
	exit 1
esac

exit 0

This is a copy of the debian package’s /etc/init.d/mosquitto with minor modifications to use its own name to generate the config and pid file so that it can be used for multiple instances. Make this file owned by root and executable with:

sudo chown root.root /etc/init.d/mosquitto_dev
sudo chmod 755 /etc/init.d/mosquitto_dev

One of the neat things of SystemD (the System and Service Manager used by modern Debian installations) is that it will automatically pick-up service management scripts located in /etc/init.d upon startup or reload. To have it pick up our new mosquitto_dev instance, simply reload the systemd daemon with:

sudo systemctl daemon-reload

This will generate the necessary systemd configuration based on the added /etc/init.d/mosquitto_dev and use it start and stop the service. To check that the service is not running execute:

sudo service mosquitto_dev status

Which should show that the service exists but is not yet running, to start it run:

sudo service mosquitto_dev start

After which the service should be running (check by running the status check once more). Systemd will now start this service after a reboot and also ensure it will be restarted when needed. This approach can be repeated to define more instances when needed as long as they listen to different ports/addresses as other services.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.