Running ZoneMinder as a Master-Slave setup

ZoneMinder is a nice application for video monitoring for security purposes.

This article is not about how to set it up in general, but instead how to create a specific master-slave setup for situations where you want to have remote security monitoring, but have rather poor bandwidth to the monitored location. It is now (October 2021) based on ZoneMinder 1.36 running under Devuan 3 Beowulf (which means that the same instructions work on Debian 10 Buster if you really want to have systemd on your machines).

Background

Poor bandwidth means you can't have just a camera at the monitored location, sending images to a ZoneMinder instance where your personnel are - the frame rate would be badly affected by the low bandwidth, and the resultant monitoring would be very disappointing.

Running ZoneMinder at the location where the camera is means you can capture high frame rate images, but then looking at these (especially as video) from some other location gets frustrating.

Design

So, I created a master-slave setup such that there is a master ZoneMinder instance at the location to be monitored, and a slave instance where the people who want to see the images are, and a combination of either MySQL replication, Galera replication, or a shared database, plus lrsync file transfers, which keeps the two machines' files synchronised with each other, as quickly as the connectivity between them can support. The important point is that this synchronisation does not have to be real-time, and it can operate over a much lower-bandwidth link than either of the scenarios described above would work with.

There are two parts to this setup:

  1. A replicated or shared MySQL database
    1. If replicated, you can use Master-Master replication if you plan only to have one slave, or Galera replication for any number of slaves
    2. If shared, the ZoneMinder slave instances simply connect to the Master database
  2. A directory synchronisation facility, to get the images themselves (which are stored in the file system, not in the database) from the master to the slave/s.

My recommendation is that you set up MySQL replication and make sure that it's working before installing ZoneMinder. That way, when you do install ZoneMinder and it sets up its database tables, these will immediately get propagated to the slave/s, and be available for use by ZoneMinder when you install that on the slaves as well.

Standard MySQL Master-Slave replication does not transfer any data (database schemas, tables, or data) which already exists on the master before replication is configured. Galera replication (which is available as standard if you install MariaDB instead of MySQL, which Debian / Devuan does by default) does perform an initial data transfer when nodes are connected to each other, which makes the whole replication setup process quite a bit easier.

It doesn't really matter when during the setup process you install and configure lrsync, because it will ensure the image directories are synchronised when you first start it.

MySQL

There are three choices where the MySQL (or MariaDB) database is concerned:

  1. for a master and a single slave you can configure MySQL on both machines with Master-Master replication
  2. for a master and any number of slaves you can configure Galera replication
  3. for a master and any number of slaves you can have a single MySQL instance on the master, which is then accessed by the slave/s as well

Master-Master replication

Install MySQL on the machine which will be the ZoneMinder master (before you have installed ZoneMinder), and configure it to be a MySQL replication master.

MySQL documentation explains the entire process, and there are other tutorials on setting this up around the Internet, but in summary, the differences from a standard MySQL setup are:

  • each server needs a different ID number in its configuration file (for a single MySQL server, this ID is not needed at all)
  • the master server needs a replication user which has the REPLICATION_SLAVE privilege
    • the slave server/s use this replication user to connect to the master and fetch new data as it gets written to the database/s you've configured for replication (in our case, this will be zm)
  • you need to find the "replication coordinates" from the master, and then configure these on the slaves, so that they know where to start replicating from
  • once everything is set up and you've started the replication process, you can check that it's working with the SHOW SLAVE STATUS command

You don't have to set up the MySQL slave before installing ZoneMinder, because once you have the master replication coordinates, you can feed these into the slave at any time afterwards, and it will catch up with whatever's happened in the interim.

Galera replication

Documentation in progress :)

Single database instance

This is the simplest setup - you just install ZoneMinder on the Master server and configure it, then install it on the slave/s and perform two changes:

  1. On the Master, adjust the zmuser in MySQL so that it can connect from anywhere, not just localhost
  2. On the Slave/s, change ZM_DB_HOST=localhost in /etc/zm/zm.conf to point to the Master server instead: ZM_DB_HOST=Zone.Minder.Master.

Timezone - important

NOTE: You must have both master and slave machines set to the same timezone for this to work (with ZoneMinder, not for MySQL replication in general), otherwise you will get replication errors and the databases will not stay in sync. It makes sense to keep the slave/s on the same timezone as the master (where the cameras are) anyway, so that you see timestamps for the location where the events occurred.

File system synchronisation

There are several ways of synchronising the files from the Master so that they exist on the Slave/s as well. I have tried lrsync, lsyncd and clsync.

They're all based on the excellent inotify kernel API, which provides a highly-efficient (fast, and light on resources) notification mechanism for any application which needs to know about file system changes, and rsync, which then propagates the files.

Whichever you choose needs to be installed on the ZoneMinder Master server, and it needs public-key SSH connectivity to the Slave/s.

lrsync is based on clsync, and is essentially just a wrapper to make the configuration easier. It's not available as a package for Debian (or for any other Linux distribution as far as I know), so you will have to build it from source (it's written in C) if you want to use this. You can simply use clsync without the wrapper if you wish, though. I've done that for an earlier setup I created, and the configuration is shown below.

I also tried using lsyncd; I don't think there's much to choose between them - feel free to try either and settle with what you feel comfortable with.

For reasons I don't understand, the clsync package is not available in the current "stable" version of Debian ("Buster"), although it is available in the previous version ("Stretch") and also the next one ("Bullseye"). In a situation like this, my preference is to install from the next version up (unless there are too many dependency problems, in which case by all means try the previous one to see whether that's any better), in order to avoid having old packages on your system, which can be more problematic when you come to do an upgrade. lsyncd, on the other hand is available for Buster, so for the time being (until Bullseye becomes "stable") you might prefer to use that.

I installed clsync from the Bullseye repository onto a Buster system without any problem.

ZoneMinder stores its image files (which the database tables point to) under /var/cache/zoneminder (it used to be /usr/share/zoneminder), so this is the directory you need to replicate from the master to the slave/s.

An example command line for lrsync which should* do what you need is:

lrsync --delay-sync 5 -av /var/cache/zoneminder root@Zone.Minder.Slave:/var/cache

If you use clsync instead, you'll want to modify /etc/default/clsync along the lines of:

CLSYNC_ENABLE=true
CLSYNC_IONICE_CLASS=3
CLSYNC_ARGS='-M rsyncdirect --output=syslog -L /dev/shm/clsync -x 23 -W /var/cache/zoneminder -S /usr/bin/rsync -D root@Zone.Minder.Slave:/var/cache/zoneminder'

lsyncd is pretty odd - it will not start without a config file /etc/lsyncd/lsyncd.conf.lua, and yet installing the package doesn't even create the directory /etc/lsyncd, never mind a configuration file inside it. A working config for zoneminder is:

settings {
  logfile = "/var/log/lsyncd.log",
  statusFile = "/tmp/lsyncd.status"
}

sync {
  default.rsync,
  source = "/var/cache/zoneminder",
  target = "root@Zone.Minder.Slave:/var/cache/zoneminder",
  delay = 5,
  rsync = {
    owner = true,
    group = true
  }
}

These examples assume that the root user has public key passwordless SSH access from the Master to the Slave/s.

* Note that as I no longer have access to the master server which I set up some years ago, this is just my best guess at the command line you would need to use. It is not a known-working example - sorry about that.

The clsync and lsyncd configurations shown above have been tested on a more recent (October 2021) setup.

ZoneMinder

Finally, once both MySQL, and file synchronisation are working, you should install and configure ZoneMinder, which once again (as for clsync above) is not available as a package for the current stable version of Debian (when I set my first system up, Stretch was the stable version, with ZoneMinder included, therefore I didn't run into these problems). I have no idea why the Debian people fail to include packages in some releases like this - it's in the next stable-to-be release, and it's in testing ("Sid"), so it's not like it's being dropped altogether…

Fortunately the ZoneMinder project maintains its own .deb repository, so you can install zm 1.36 (note: even numbers after the dot are stable releases, odd numbers are unstable/testing, and 36 is an even number - it's not just the 3 that matters) on Debian Buster / Devuan Beowulf easily enough. Note also that the installation instructions refer to ZoneMinder 1.34, however 1.36 was released in September 2021.

It's nice to see that the ZoneMinder project has re-instated the sysvinit startup script in 1.36, so this no longer needs to be fetched from an older version.

I used the following commands to install ZM 1.36 on Devuan 3 Beowulf:

# echo "deb https://zmrepo.zoneminder.com/debian/release-1.36/ buster/" >/etc/apt/sources.list.d/zoneminder.list
# aptitude install mariadb-server apache2 php7.3 libapache2-mod-php7.3
# aptitude install apt-transport-https gnupg2
# wget -O - https://zmrepo.zoneminder.com/debian/archive-keyring.gpg | apt-key add -
# aptitude update
# aptitude install zoneminder  vlc-plugin-base php7.3-gd
# a2enmod cgi
# a2enmod rewrite
# a2enconf zoneminder
# /etc/init.d/apache2 restart
# /etc/init.d/zoneminder start

Note that if you plan to have a single shared database, you should then also run the commands:

# mysql -e "grant usage on *.* to 'zmuser'@'%' identified by 'zmpass'"
# mysql -e "grant select, insert, update, delete, create, drop, references, index, alter, lock tables, execute, create routine, alter routine, trigger on zm.* to 'zmuser'@'%'"

If you really want to install ZoneMinder on a Debian 10 system running systemd, there is documentation for this as well.

Once you've got ZoneMinder installed, you can start configuring it - but on the master only! The slave has no cameras, therefore most of the setup process makes no sense. Once you've configured the camera/s on the master, they will appear as devices on the slave/s (since this configuration is held in the MySQL database), but they will show as "not capturing". This is because you can't have more than one ZoneMinder instance connecting to a camera (and you don't want the Slave/s to try pulling video across the network connection anyway - avoiding that is the whole point of this Master-Slave setup).

You cannot view live video on the Slave/s, but you can view stored events, images and videos on the both the Master and the Slave/s.

Personal notes

This section won't be of any interest to you unless you happen to be using the same Sanyo HD2100P / HD2500P cameras as I am. I've put these notes here just to remind me how to configure them in ZoneMinder.

  • General
    • Source Type: Libvlc (seems to work best with ZM 1.34) / ffmpeg (seems to work best with ZM 1.36)
  • Source
    • Source Path: rtsp://username:password@198.51.100.42:554/VideoInput/1/h264/1
    • Method: TCP
    • Capture Resolution: 1280x720 720p
      • Other resolutions work too; choose whichever seems best for your bandwidth / storage capacity / requirements
  • Timestamp
    • Timestamp Label Format: %F %A%n%T %N
    • Font Size: Large

For the next camera/s: Select, use Clone, adjust name and IP address, done.


Go up
Return to main index.