<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://wiki.staging.zoneminder.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kernelkurtz</id>
	<title>ZoneMinder Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.staging.zoneminder.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kernelkurtz"/>
	<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/Special:Contributions/Kernelkurtz"/>
	<updated>2026-05-03T13:37:56Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.37.1</generator>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Multi_Port&amp;diff=17933</id>
		<title>Multi Port</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Multi_Port&amp;diff=17933"/>
		<updated>2026-03-23T17:18:26Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Mention iptables compatibility layers&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;All browsers currently have a limitation where there can only be 6 connections to a server. This can be gotten around by using multi port, or by adjusting a Firefox about:config setting. &lt;br /&gt;
&lt;br /&gt;
==Apache Settings for Multi Port==&lt;br /&gt;
This is the optimal approach as it will work on any browser. If you are in a commercial setting, or have other users besides yourself, it is almost required to setup Multi Port through Apache. If on the other hand you are on your own, you may consider simply using Firefox. Multi port, does just what it says: It will redirect multiple server connections through different ports, which is a workaround for the browser security limitation. &lt;br /&gt;
&lt;br /&gt;
Here are some quick configs for 50 cameras. Note: The below config has been confirmed to work with only HTTP. HTTPS may need additional configuration. If you get an error about missing a &amp;gt; bracket on the 000-default.conf, make it so all these _default_:#### are on one line. Then it will work. It&amp;#039;s not easy to read it in that format, so it has been justified for viewing here.&lt;br /&gt;
&lt;br /&gt;
====/etc/apache2/sites-available/000-default.conf:====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost _default_:80 _default_:30000&lt;br /&gt;
        _default_:30001 _default_:30002 _default_:30003 _default_:30004&lt;br /&gt;
        _default_:30005 _default_:30006 _default_:30007 _default_:30008 _default_:30009 _default_:30010&lt;br /&gt;
_default_:30011 _default_:30012 _default_:30013 _default_:30014&lt;br /&gt;
_default_:30015 _default_:30016 _default_:30017 _default_:30018&lt;br /&gt;
_default_:30019 _default_:30020 _default_:30021 _default_:30022&lt;br /&gt;
_default_:30023 _default_:30024 _default_:30025 _default_:30026&lt;br /&gt;
_default_:30027 _default_:30028 _default_:30029 _default_:30030&lt;br /&gt;
_default_:30031 _default_:30032 _default_:30033 _default_:30034&lt;br /&gt;
_default_:30035 _default_:30036 _default_:30037 _default_:30038&lt;br /&gt;
_default_:30039 _default_:30040 _default_:30041 _default_:30042&lt;br /&gt;
_default_:30043 _default_:30044 _default_:30045 _default_:30046&lt;br /&gt;
_default_:30047 _default_:30048 _default_:30049 _default_:30050&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====/etc/apache2/ports.conf:====&lt;br /&gt;
Note:this doesn&amp;#039;t include everything in the file. Make sure to insert into the existing ports file where changes are appropriate and not simply clobber the original file with below.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        Listen 80&lt;br /&gt;
        Listen 30000&lt;br /&gt;
        Listen 30001&lt;br /&gt;
        Listen 30002&lt;br /&gt;
        Listen 30003&lt;br /&gt;
        Listen 30004&lt;br /&gt;
        Listen 30005&lt;br /&gt;
        Listen 30006&lt;br /&gt;
        Listen 30007&lt;br /&gt;
        Listen 30008&lt;br /&gt;
        Listen 30009&lt;br /&gt;
        Listen 30010&lt;br /&gt;
        Listen 30011&lt;br /&gt;
        Listen 30012&lt;br /&gt;
        Listen 30013&lt;br /&gt;
        Listen 30014&lt;br /&gt;
        Listen 30015&lt;br /&gt;
        Listen 30016&lt;br /&gt;
        Listen 30017&lt;br /&gt;
        Listen 30018&lt;br /&gt;
        Listen 30019&lt;br /&gt;
        Listen 30020&lt;br /&gt;
        Listen 30021&lt;br /&gt;
        Listen 30022&lt;br /&gt;
        Listen 30023&lt;br /&gt;
        Listen 30024&lt;br /&gt;
        Listen 30025&lt;br /&gt;
        Listen 30026&lt;br /&gt;
        Listen 30027&lt;br /&gt;
        Listen 30028&lt;br /&gt;
        Listen 30029&lt;br /&gt;
        Listen 30030&lt;br /&gt;
        Listen 30031&lt;br /&gt;
        Listen 30032&lt;br /&gt;
        Listen 30033&lt;br /&gt;
        Listen 30034&lt;br /&gt;
        Listen 30035&lt;br /&gt;
        Listen 30036&lt;br /&gt;
        Listen 30037&lt;br /&gt;
        Listen 30038&lt;br /&gt;
        Listen 30039&lt;br /&gt;
        Listen 30040&lt;br /&gt;
        Listen 30041&lt;br /&gt;
        Listen 30042&lt;br /&gt;
        Listen 30043&lt;br /&gt;
        Listen 30044&lt;br /&gt;
        Listen 30045&lt;br /&gt;
        Listen 30046&lt;br /&gt;
        Listen 30047&lt;br /&gt;
        Listen 30048&lt;br /&gt;
        Listen 30049&lt;br /&gt;
        Listen 30050&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You will also need to enable multi port in Zoneminder options. Please read the medium guide below for more information.&lt;br /&gt;
&lt;br /&gt;
===Resources===&lt;br /&gt;
* https://medium.com/zmninja/multi-port-storage-areas-and-more-d5836a336c93 - A developer of Zoneminder wrote up this helpful guide.&lt;br /&gt;
&lt;br /&gt;
==Firefox About:Config==&lt;br /&gt;
 &lt;br /&gt;
In Firefox you can make a change to the configuration to get cameras to display. First go to [https://support.mozilla.org/en-US/kb/about-config-editor-firefox about:config ].  And search for, then adjust [http://kb.mozillazine.org/Network.http.max-persistent-connections-per-server Network.http.max-persistent-connections-per-server]. The max persistent connections per server must be &amp;gt; 6 in order to see &amp;gt; 6 camera streams. It can be set to any number, so perhaps you might set it to 100 or 150.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Firefox About Config Edit.png|900px|||]]&lt;br /&gt;
&lt;br /&gt;
===Resources===&lt;br /&gt;
The forum will have a number of posts regarding this change. It has been discussed extensively. Such as:&lt;br /&gt;
* [https://forums.zoneminder.com/viewtopic.php?f=32&amp;amp;t=23417&amp;amp;p=89249  ZM Forums: Problems when two people are watching live]&lt;br /&gt;
&lt;br /&gt;
[[Category:Dummies_Guide]]&lt;br /&gt;
&lt;br /&gt;
==Alternative firewall based method==&lt;br /&gt;
&lt;br /&gt;
If you are unable or prefer not to modify your webserver configuration, you can also use Linux firewall functionality to redirect a range of incoming high ports to the configured webserver listening port.  This will work with both Apache and Nginx based Zoneminder installs.&lt;br /&gt;
&lt;br /&gt;
The following rules are are for iptables based firewalls and work on my RHEL and Ubuntu based servers.  If you use another firewall or frontend such as nftables, firewalld, ufw, etc., you may have to install a compatibility layer, or otherwise translate them to work with your setup accordingly.  AI is very helpful for tasks such as this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;iptables -t nat -A PREROUTING -p tcp -m multiport --dports 30000:30100 -j REDIRECT --to-ports 443&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are running a headless Zoneminder install the above rule is all you will need.  Replace 30000:30100 with the range of ports you want to redirect.  Of course this has to match the multiport starting port in Zoneminder&amp;#039;s network settings.  You can also use port 80 as destination if you don&amp;#039;t use https, or whatever other port your webserver listens on.  You can quickly check it is working by browsing to http(s)://yourzomeminderserver:30099 (or some other redirected port).&lt;br /&gt;
&lt;br /&gt;
If your ZM server has a graphical Linux install and a directly connected display, you will also need some additional configuration to make this work as localhost traffic does not use the same prerouting rules.  First you need to enable routing on the localnet network.  Only do this if you trust the other devices on your local LAN, don&amp;#039;t do this if you connect directly to the internet.  Add the following line to /etc/sysctl.conf (or the appropriate location for your distribution). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;net.ipv4.conf.all.route_localnet = 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can apply it immediately with sysctl -p.  Then you need to add a second firewall rule to redirect the loopback address ports.  Use the same port range and destination port as the previous rule.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;iptables -t nat -I OUTPUT -p tcp -o lo --dport 30000:30100 -j REDIRECT --to-ports 443&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&amp;#039;s it.  Be sure to make any rules you add persistent across reboots with whatever firewall management method you are using.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17924</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17924"/>
		<updated>2026-02-22T20:27:16Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a destination.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   exclude = { &amp;#039;*.jpg&amp;#039; },&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     prune_empty_dirs = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.  By default the packaged lsyncd service logs to the systemd journal, which also by default echoes to /var/log/messages.  Since the application logs are sufficient I&amp;#039;ve added the &amp;#039;StandardOutput=null&amp;#039; option to the lsyncd service&amp;#039;s unit file to prevent this over-duplication, but I&amp;#039;ve left StandardError with the existing default logging to the journal.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  According to the docs &amp;quot;In production mode it is recommended to have insist on.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;exclude = { &amp;#039;*.jpg&amp;#039; }&amp;#039;&amp;#039;  These are generally used by the Zoneminder GUI but are not really needed as standalone files in my case (though some people may save .jpgs on purpose, in which case you will want to remove this line).  I&amp;#039;m only interested in having backups of the videos, so not syncing these reduces the number of uploaded files significantly. You can break them out by type &amp;#039;*snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039; , &amp;#039;*capture*.jpg&amp;#039; etc., or just exclude them all as shown.  Functions like zmNinja&amp;#039;s 24 Hour Review may create hundreds of snapshots all at once, which lsyncd will immediately sync, so you can avoid that with these filters.  The log snippet below did not include this filter.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;prune_empty_dirs = true&amp;#039;&amp;#039; Because they are watched by inotify, rsync will create empty remote directories even for events Zonmeminder deletes locally.  If you are not syncing deletes you will want to use this flag, see &amp;#039;&amp;#039;delete&amp;#039;&amp;#039; below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. &amp;#039;&amp;#039;Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing.&amp;#039;&amp;#039; There are also options besides true and false so read the docs for more info. It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for errors with journalctl (see note about logging above).  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17923</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17923"/>
		<updated>2026-02-22T20:26:15Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Add exclude all jpegs&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a destination.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   exclude = { &amp;#039;*.jpg&amp;#039; },&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     prune_empty_dirs = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.  By default the packaged lsyncd service logs to the systemd journal, which also by default echoes to /var/log/messages.  Since the application logs are sufficient I&amp;#039;ve added the &amp;#039;StandardOutput=null&amp;#039; option to the lsyncd service&amp;#039;s unit file to prevent this over-duplication, but I&amp;#039;ve left StandardError with the existing default logging to the journal.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  According to the docs &amp;quot;In production mode it is recommended to have insist on.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;exclude = { &amp;#039;*.jpg&amp;#039; }&amp;#039;&amp;#039;  These are generally used by the Zoneminder GUI but are not really needed as standalone files in my case (though some people may save .jpgs on purpose, in which case you will want to remove this line).  I&amp;#039;m only interested in having backups of the videos, so not syncing these reduces the number of uploaded files significantly. You can break them out by type &amp;#039;*snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039; , &amp;#039;*capture*.jpg&amp;#039; etc., or just exclude them all as shown.  Functions like zmNinja&amp;#039;s 24 Hour Review may create hundreds of snapshots all at once, which lsyncd will immediately sync, so you can avoid that with these filters.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;prune_empty_dirs = true&amp;#039;&amp;#039; Because they are watched by inotify, rsync will create empty remote directories even for events Zonmeminder deletes locally.  If you are not syncing deletes you will want to use this flag, see &amp;#039;&amp;#039;delete&amp;#039;&amp;#039; below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. &amp;#039;&amp;#039;Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing.&amp;#039;&amp;#039; There are also options besides true and false so read the docs for more info. It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for errors with journalctl (see note about logging above).  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17922</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17922"/>
		<updated>2026-02-20T17:05:20Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a destination.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   exclude = { &amp;#039;snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039; },&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     prune_empty_dirs = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.  By default the packaged lsyncd service logs to the systemd journal, which also by default echoes to /var/log/messages.  Since the application logs are sufficient I&amp;#039;ve added the &amp;#039;StandardOutput=null&amp;#039; option to the lsyncd service&amp;#039;s unit file to prevent this over-duplication, but I&amp;#039;ve left StandardError with the existing default logging to the journal.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  According to the docs &amp;quot;In production mode it is recommended to have insist on.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;exclude = { &amp;#039;snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039; }&amp;#039;&amp;#039;  These are used by the Zoneminder GUI but are less useful as standalone files.  I&amp;#039;m only interested in having backups of the videos, so not syncing these means uploading a single file per event rather than three, but this directive is of course optional. Functions like zmNinja&amp;#039;s 24 Hour Review may create hundreds of snapshots all at once, which lsyncd will immediately sync.  If you just want to exclude these dynamically generated files, use &amp;#039;snapshot-*.jpg&amp;#039; instead of &amp;#039;snapshot*.jpg&amp;#039; which will only catch the ones with dimensions in the filename eg.&amp;#039;snapshot-712x400.jpg&amp;#039;.  The log snippet below did not include this option.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;prune_empty_dirs = true&amp;#039;&amp;#039; Because they are watched by inotify, rsync will create empty remote directories even for events Zonmeminder deletes locally.  If you are not syncing deletes you will want to use this flag, see &amp;#039;&amp;#039;delete&amp;#039;&amp;#039; below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. &amp;#039;&amp;#039;Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing.&amp;#039;&amp;#039; There are also options besides true and false so read the docs for more info. It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for errors with journalctl (see note about logging above).  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17921</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17921"/>
		<updated>2026-02-19T19:54:57Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Reduce systemd logging&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a destination.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   exclude = { &amp;#039;snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039; },&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     prune_empty_dirs = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.  By default the packaged lsyncd service logs to the systemd journal, which also by default echoes to /var/log/messages.  Since the application logs are sufficient I&amp;#039;ve added the &amp;#039;StandardOutput=null&amp;#039; option to the lsyncd service&amp;#039;s unit file to prevent this over-duplication, but I&amp;#039;ve left StandardError with the existing default logging to the journal.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;exclude = { &amp;#039;snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039; }&amp;#039;&amp;#039;  These are used by the Zoneminder GUI but are less useful as standalone files.  I&amp;#039;m only interested in having backups of the videos, so not syncing these means uploading a single file per event rather than three, but this directive is of course optional. Functions like zmNinja&amp;#039;s 24 Hour Review may create hundreds of snapshots all at once, which lsyncd will immediately sync.  If you just want to exclude these dynamically generated files, use &amp;#039;snapshot-*.jpg&amp;#039; instead of &amp;#039;snapshot*.jpg&amp;#039; which will only catch the ones with dimensions in the filename eg.&amp;#039;snapshot-712x400.jpg&amp;#039;.  The log snippet below did not include this option.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;prune_empty_dirs = true&amp;#039;&amp;#039; Because they are watched by inotify, rsync will create empty remote directories even for events Zonmeminder deletes locally.  If you are not syncing deletes you will want to use this flag, see &amp;#039;&amp;#039;delete&amp;#039;&amp;#039; below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. &amp;#039;&amp;#039;Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing.&amp;#039;&amp;#039; There are also options besides true and false so read the docs for more info. It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for errors with journalctl (see note about logging above).  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17920</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17920"/>
		<updated>2026-02-18T05:11:28Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a destination.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   exclude = { &amp;#039;snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039; },&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     prune_empty_dirs = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;exclude = { &amp;#039;snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039; }&amp;#039;&amp;#039;  These are used by the Zoneminder GUI but are less useful as standalone files.  I&amp;#039;m only interested in having backups of the videos, so not syncing these means uploading a single file per event rather than three, but this directive is of course optional. Functions like zmNinja&amp;#039;s 24 Hour Review may create hundreds of snapshots all at once, which lsyncd will immediately sync.  If you just want to exclude these dynamically generated files, use &amp;#039;snapshot-*.jpg&amp;#039; instead of &amp;#039;snapshot*.jpg&amp;#039; which will only catch the ones with dimensions in the filename eg.&amp;#039;snapshot-712x400.jpg&amp;#039;.  The log snippet below did not include this option.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;prune_empty_dirs = true&amp;#039;&amp;#039; Because they are watched by inotify, rsync will create empty remote directories even for events Zonmeminder deletes locally.  If you are not syncing deletes you will want to use this flag, see &amp;#039;&amp;#039;delete&amp;#039;&amp;#039; below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info. It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17919</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17919"/>
		<updated>2026-02-18T05:05:33Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a destination.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   exclude = { &amp;#039;snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039; },&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     prune_empty_dirs = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;exclude = { &amp;#039;snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039; }&amp;#039;&amp;#039;  These are used by the Zoneminder GUI but are less useful as standalone files.  I&amp;#039;m only interested in having backups of the videos, so not syncing these means uploading a single file per event rather than three, but this directive is of course optional. Functions like zmNinja&amp;#039;s 24 Hour Review may create hundreds of snapshots all at once, which lsyncd will immediately sync.  If you just want to exclude these dynamically generated files, use &amp;#039;snapshot-*.jpg&amp;#039; instead of &amp;#039;snapshot*.jpg&amp;#039; which will only catch the ones with dimensions in the filename eg.&amp;#039;snapshot-712x400.jpg&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;prune_empty_dirs = true&amp;#039;&amp;#039; Because they are watched by inotify, rsync will create empty remote directories even for events Zonmeminder deletes locally.  If you are not syncing deletes you will want to use this flag, see &amp;#039;&amp;#039;delete&amp;#039;&amp;#039; below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info. It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17918</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17918"/>
		<updated>2026-02-18T05:04:46Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a destination.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   exclude = { &amp;#039;snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039; },&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     prune_empty_dirs = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;exclude = { &amp;#039;snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039;}&amp;#039;&amp;#039;  These are used by the Zoneminder GUI but are less useful as standalone files.  I&amp;#039;m only interested in having backups of the videos, so not syncing these means uploading a single file per event rather than three, but this directive is of course optional. Functions like zmNinja&amp;#039;s 24 Hour Review may create hundreds of snapshots all at once, which lsyncd will immediately sync.  If you just want to exclude these dynamically generated files, use &amp;#039;snapshot-*.jpg&amp;#039; instead of &amp;#039;snapshot*.jpg&amp;#039; which will only catch the ones with dimensions in the filename eg.&amp;#039;snapshot-712x400.jpg&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;prune_empty_dirs = true&amp;#039;&amp;#039; Because they are watched by inotify, rsync will create empty remote directories even for events Zonmeminder deletes locally.  If you are not syncing deletes you will want to use this flag, see &amp;#039;&amp;#039;delete&amp;#039;&amp;#039; below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info. It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17917</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17917"/>
		<updated>2026-02-18T05:04:11Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Added exclude snapshots&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a destination.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   exclude = { &amp;#039;snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039;},&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     prune_empty_dirs = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;exclude = { &amp;#039;snapshot*.jpg&amp;#039; , &amp;#039;alarm.jpg&amp;#039;}&amp;#039;&amp;#039;  These are used by the Zoneminder GUI but are less useful as standalone files.  I&amp;#039;m only interested in having backups of the videos, so not syncing these means uploading a single file per event rather than three, but this directive is of course optional. Functions like zmNinja&amp;#039;s 24 Hour Review may create hundreds of snapshots all at once, which lsyncd will immediately sync.  If you just want to exclude these dynamically generated files, use &amp;#039;snapshot-*.jpg&amp;#039; instead of &amp;#039;snapshot*.jpg&amp;#039; which will only catch the ones with dimensions in the filename eg.&amp;#039;snapshot-712x400.jpg&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;prune_empty_dirs = true&amp;#039;&amp;#039; Because they are watched by inotify, rsync will create empty remote directories even for events Zonmeminder deletes locally.  If you are not syncing deletes you will want to use this flag, see &amp;#039;&amp;#039;delete&amp;#039;&amp;#039; below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info. It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17879</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17879"/>
		<updated>2026-01-15T03:25:29Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Remove unnecessary option compress&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a destination.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     prune_empty_dirs = true&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;prune_empty_dirs = true&amp;#039;&amp;#039; Because they are watched by inotify, rsync will create empty remote directories even for events Zonmeminder deletes locally.  If you are not syncing deletes you will want to use this flag, see &amp;#039;&amp;#039;delete&amp;#039;&amp;#039; below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Multi_Port&amp;diff=17878</id>
		<title>Multi Port</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Multi_Port&amp;diff=17878"/>
		<updated>2026-01-14T17:29:01Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;All browsers currently have a limitation where there can only be 6 connections to a server. This can be gotten around by using multi port, or by adjusting a Firefox about:config setting. &lt;br /&gt;
&lt;br /&gt;
==Apache Settings for Multi Port==&lt;br /&gt;
This is the optimal approach as it will work on any browser. If you are in a commercial setting, or have other users besides yourself, it is almost required to setup Multi Port through Apache. If on the other hand you are on your own, you may consider simply using Firefox. Multi port, does just what it says: It will redirect multiple server connections through different ports, which is a workaround for the browser security limitation. &lt;br /&gt;
&lt;br /&gt;
Here are some quick configs for 50 cameras. Note: The below config has been confirmed to work with only HTTP. HTTPS may need additional configuration. If you get an error about missing a &amp;gt; bracket on the 000-default.conf, make it so all these _default_:#### are on one line. Then it will work. It&amp;#039;s not easy to read it in that format, so it has been justified for viewing here.&lt;br /&gt;
&lt;br /&gt;
====/etc/apache2/sites-available/000-default.conf:====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost _default_:80 _default_:30000&lt;br /&gt;
        _default_:30001 _default_:30002 _default_:30003 _default_:30004&lt;br /&gt;
        _default_:30005 _default_:30006 _default_:30007 _default_:30008 _default_:30009 _default_:30010&lt;br /&gt;
_default_:30011 _default_:30012 _default_:30013 _default_:30014&lt;br /&gt;
_default_:30015 _default_:30016 _default_:30017 _default_:30018&lt;br /&gt;
_default_:30019 _default_:30020 _default_:30021 _default_:30022&lt;br /&gt;
_default_:30023 _default_:30024 _default_:30025 _default_:30026&lt;br /&gt;
_default_:30027 _default_:30028 _default_:30029 _default_:30030&lt;br /&gt;
_default_:30031 _default_:30032 _default_:30033 _default_:30034&lt;br /&gt;
_default_:30035 _default_:30036 _default_:30037 _default_:30038&lt;br /&gt;
_default_:30039 _default_:30040 _default_:30041 _default_:30042&lt;br /&gt;
_default_:30043 _default_:30044 _default_:30045 _default_:30046&lt;br /&gt;
_default_:30047 _default_:30048 _default_:30049 _default_:30050&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====/etc/apache2/ports.conf:====&lt;br /&gt;
Note:this doesn&amp;#039;t include everything in the file. Make sure to insert into the existing ports file where changes are appropriate and not simply clobber the original file with below.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        Listen 80&lt;br /&gt;
        Listen 30000&lt;br /&gt;
        Listen 30001&lt;br /&gt;
        Listen 30002&lt;br /&gt;
        Listen 30003&lt;br /&gt;
        Listen 30004&lt;br /&gt;
        Listen 30005&lt;br /&gt;
        Listen 30006&lt;br /&gt;
        Listen 30007&lt;br /&gt;
        Listen 30008&lt;br /&gt;
        Listen 30009&lt;br /&gt;
        Listen 30010&lt;br /&gt;
        Listen 30011&lt;br /&gt;
        Listen 30012&lt;br /&gt;
        Listen 30013&lt;br /&gt;
        Listen 30014&lt;br /&gt;
        Listen 30015&lt;br /&gt;
        Listen 30016&lt;br /&gt;
        Listen 30017&lt;br /&gt;
        Listen 30018&lt;br /&gt;
        Listen 30019&lt;br /&gt;
        Listen 30020&lt;br /&gt;
        Listen 30021&lt;br /&gt;
        Listen 30022&lt;br /&gt;
        Listen 30023&lt;br /&gt;
        Listen 30024&lt;br /&gt;
        Listen 30025&lt;br /&gt;
        Listen 30026&lt;br /&gt;
        Listen 30027&lt;br /&gt;
        Listen 30028&lt;br /&gt;
        Listen 30029&lt;br /&gt;
        Listen 30030&lt;br /&gt;
        Listen 30031&lt;br /&gt;
        Listen 30032&lt;br /&gt;
        Listen 30033&lt;br /&gt;
        Listen 30034&lt;br /&gt;
        Listen 30035&lt;br /&gt;
        Listen 30036&lt;br /&gt;
        Listen 30037&lt;br /&gt;
        Listen 30038&lt;br /&gt;
        Listen 30039&lt;br /&gt;
        Listen 30040&lt;br /&gt;
        Listen 30041&lt;br /&gt;
        Listen 30042&lt;br /&gt;
        Listen 30043&lt;br /&gt;
        Listen 30044&lt;br /&gt;
        Listen 30045&lt;br /&gt;
        Listen 30046&lt;br /&gt;
        Listen 30047&lt;br /&gt;
        Listen 30048&lt;br /&gt;
        Listen 30049&lt;br /&gt;
        Listen 30050&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You will also need to enable multi port in Zoneminder options. Please read the medium guide below for more information.&lt;br /&gt;
&lt;br /&gt;
===Resources===&lt;br /&gt;
* https://medium.com/zmninja/multi-port-storage-areas-and-more-d5836a336c93 - A developer of Zoneminder wrote up this helpful guide.&lt;br /&gt;
&lt;br /&gt;
==Firefox About:Config==&lt;br /&gt;
 &lt;br /&gt;
In Firefox you can make a change to the configuration to get cameras to display. First go to [https://support.mozilla.org/en-US/kb/about-config-editor-firefox about:config ].  And search for, then adjust [http://kb.mozillazine.org/Network.http.max-persistent-connections-per-server Network.http.max-persistent-connections-per-server]. The max persistent connections per server must be &amp;gt; 6 in order to see &amp;gt; 6 camera streams. It can be set to any number, so perhaps you might set it to 100 or 150.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Firefox About Config Edit.png|900px|||]]&lt;br /&gt;
&lt;br /&gt;
===Resources===&lt;br /&gt;
The forum will have a number of posts regarding this change. It has been discussed extensively. Such as:&lt;br /&gt;
* [https://forums.zoneminder.com/viewtopic.php?f=32&amp;amp;t=23417&amp;amp;p=89249  ZM Forums: Problems when two people are watching live]&lt;br /&gt;
&lt;br /&gt;
[[Category:Dummies_Guide]]&lt;br /&gt;
&lt;br /&gt;
==Alternative firewall based method==&lt;br /&gt;
&lt;br /&gt;
If you are unable or prefer not to modify your webserver configuration, you can also use Linux firewall functionality to redirect a range of incoming high ports to the configured webserver listening port.  This will work with both Apache and Nginx based Zoneminder installs.&lt;br /&gt;
&lt;br /&gt;
The following rules are are for iptables firewalls and are working on my RHEL based server.  If you use another firewall or frontend such as nftables, firewalld, ufw, etc., you will have to translate them accordingly.  AI is very helpful for tasks such as this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;iptables -t nat -A PREROUTING -p tcp -m multiport --dports 30000:30100 -j REDIRECT --to-ports 443&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are running a headless Zoneminder install the above rule is all you will need.  Replace 30000:30100 with the range of ports you want to redirect.  Of course this has to match the multiport starting port in Zoneminder&amp;#039;s network settings.  You can also use port 80 as destination if you don&amp;#039;t use https, or whatever other port your webserver listens on.  You can quickly check it is working by browsing to http(s)://yourzomeminderserver:30099 (or some other redirected port).&lt;br /&gt;
&lt;br /&gt;
If your ZM server has a graphical Linux install and a directly connected display, you will also need some additional configuration to make this work as localhost traffic does not use the same prerouting rules.  First you need to enable routing on the localnet network.  Only do this if you trust the other devices on your local LAN, don&amp;#039;t do this if you connect directly to the internet.  Add the following line to /etc/sysctl.conf (or the appropriate location for your distribution). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;net.ipv4.conf.all.route_localnet = 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can apply it immediately with sysctl -p.  Then you need to add a second firewall rule to redirect the loopback address ports.  Use the same port range and destination port as the previous rule.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;iptables -t nat -I OUTPUT -p tcp -o lo --dport 30000:30100 -j REDIRECT --to-ports 443&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&amp;#039;s it.  Be sure to make any rules you add persistent across reboots with whatever firewall management method you are using.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17877</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17877"/>
		<updated>2026-01-14T04:57:45Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a destination.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     prune_empty_dirs = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;prune_empty_dirs = true&amp;#039;&amp;#039; Because they are watched by inotify, rsync will create empty remote directories even for events Zonmeminder deletes locally.  If you are not syncing deletes you will want to use this flag, see &amp;#039;&amp;#039;delete&amp;#039;&amp;#039; below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17876</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17876"/>
		<updated>2026-01-14T04:55:18Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Added prune empty dirs flag&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a destination.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     prune_empty_dirs = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;prune_empty_dirs = true&amp;#039;&amp;#039; Because they are watched by inotify, rsync will create empty remote directories even for events Zonmeminder deletes locally.  If you are not syncing deletes you will want to use this flag, see below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Multi_Port&amp;diff=17874</id>
		<title>Multi Port</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Multi_Port&amp;diff=17874"/>
		<updated>2026-01-09T22:45:23Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;All browsers currently have a limitation where there can only be 6 connections to a server. This can be gotten around by using multi port, or by adjusting a Firefox about:config setting. &lt;br /&gt;
&lt;br /&gt;
==Apache Settings for Multi Port==&lt;br /&gt;
This is the optimal approach as it will work on any browser. If you are in a commercial setting, or have other users besides yourself, it is almost required to setup Multi Port through Apache. If on the other hand you are on your own, you may consider simply using Firefox. Multi port, does just what it says: It will redirect multiple server connections through different ports, which is a workaround for the browser security limitation. &lt;br /&gt;
&lt;br /&gt;
Here are some quick configs for 50 cameras. Note: The below config has been confirmed to work with only HTTP. HTTPS may need additional configuration. If you get an error about missing a &amp;gt; bracket on the 000-default.conf, make it so all these _default_:#### are on one line. Then it will work. It&amp;#039;s not easy to read it in that format, so it has been justified for viewing here.&lt;br /&gt;
&lt;br /&gt;
====/etc/apache2/sites-available/000-default.conf:====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost _default_:80 _default_:30000&lt;br /&gt;
        _default_:30001 _default_:30002 _default_:30003 _default_:30004&lt;br /&gt;
        _default_:30005 _default_:30006 _default_:30007 _default_:30008 _default_:30009 _default_:30010&lt;br /&gt;
_default_:30011 _default_:30012 _default_:30013 _default_:30014&lt;br /&gt;
_default_:30015 _default_:30016 _default_:30017 _default_:30018&lt;br /&gt;
_default_:30019 _default_:30020 _default_:30021 _default_:30022&lt;br /&gt;
_default_:30023 _default_:30024 _default_:30025 _default_:30026&lt;br /&gt;
_default_:30027 _default_:30028 _default_:30029 _default_:30030&lt;br /&gt;
_default_:30031 _default_:30032 _default_:30033 _default_:30034&lt;br /&gt;
_default_:30035 _default_:30036 _default_:30037 _default_:30038&lt;br /&gt;
_default_:30039 _default_:30040 _default_:30041 _default_:30042&lt;br /&gt;
_default_:30043 _default_:30044 _default_:30045 _default_:30046&lt;br /&gt;
_default_:30047 _default_:30048 _default_:30049 _default_:30050&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====/etc/apache2/ports.conf:====&lt;br /&gt;
Note:this doesn&amp;#039;t include everything in the file. Make sure to insert into the existing ports file where changes are appropriate and not simply clobber the original file with below.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        Listen 80&lt;br /&gt;
        Listen 30000&lt;br /&gt;
        Listen 30001&lt;br /&gt;
        Listen 30002&lt;br /&gt;
        Listen 30003&lt;br /&gt;
        Listen 30004&lt;br /&gt;
        Listen 30005&lt;br /&gt;
        Listen 30006&lt;br /&gt;
        Listen 30007&lt;br /&gt;
        Listen 30008&lt;br /&gt;
        Listen 30009&lt;br /&gt;
        Listen 30010&lt;br /&gt;
        Listen 30011&lt;br /&gt;
        Listen 30012&lt;br /&gt;
        Listen 30013&lt;br /&gt;
        Listen 30014&lt;br /&gt;
        Listen 30015&lt;br /&gt;
        Listen 30016&lt;br /&gt;
        Listen 30017&lt;br /&gt;
        Listen 30018&lt;br /&gt;
        Listen 30019&lt;br /&gt;
        Listen 30020&lt;br /&gt;
        Listen 30021&lt;br /&gt;
        Listen 30022&lt;br /&gt;
        Listen 30023&lt;br /&gt;
        Listen 30024&lt;br /&gt;
        Listen 30025&lt;br /&gt;
        Listen 30026&lt;br /&gt;
        Listen 30027&lt;br /&gt;
        Listen 30028&lt;br /&gt;
        Listen 30029&lt;br /&gt;
        Listen 30030&lt;br /&gt;
        Listen 30031&lt;br /&gt;
        Listen 30032&lt;br /&gt;
        Listen 30033&lt;br /&gt;
        Listen 30034&lt;br /&gt;
        Listen 30035&lt;br /&gt;
        Listen 30036&lt;br /&gt;
        Listen 30037&lt;br /&gt;
        Listen 30038&lt;br /&gt;
        Listen 30039&lt;br /&gt;
        Listen 30040&lt;br /&gt;
        Listen 30041&lt;br /&gt;
        Listen 30042&lt;br /&gt;
        Listen 30043&lt;br /&gt;
        Listen 30044&lt;br /&gt;
        Listen 30045&lt;br /&gt;
        Listen 30046&lt;br /&gt;
        Listen 30047&lt;br /&gt;
        Listen 30048&lt;br /&gt;
        Listen 30049&lt;br /&gt;
        Listen 30050&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You will also need to enable multi port in Zoneminder options. Please read the medium guide below for more information.&lt;br /&gt;
&lt;br /&gt;
===Resources===&lt;br /&gt;
* https://medium.com/zmninja/multi-port-storage-areas-and-more-d5836a336c93 - A developer of Zoneminder wrote up this helpful guide.&lt;br /&gt;
&lt;br /&gt;
==Firefox About:Config==&lt;br /&gt;
 &lt;br /&gt;
In Firefox you can make a change to the configuration to get cameras to display. First go to [https://support.mozilla.org/en-US/kb/about-config-editor-firefox about:config ].  And search for, then adjust [http://kb.mozillazine.org/Network.http.max-persistent-connections-per-server Network.http.max-persistent-connections-per-server]. The max persistent connections per server must be &amp;gt; 6 in order to see &amp;gt; 6 camera streams. It can be set to any number, so perhaps you might set it to 100 or 150.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Firefox About Config Edit.png|900px|||]]&lt;br /&gt;
&lt;br /&gt;
===Resources===&lt;br /&gt;
The forum will have a number of posts regarding this change. It has been discussed extensively. Such as:&lt;br /&gt;
* [https://forums.zoneminder.com/viewtopic.php?f=32&amp;amp;t=23417&amp;amp;p=89249  ZM Forums: Problems when two people are watching live]&lt;br /&gt;
&lt;br /&gt;
[[Category:Dummies_Guide]]&lt;br /&gt;
&lt;br /&gt;
==Alternative firewall based method==&lt;br /&gt;
&lt;br /&gt;
If you are unable or prefer not to modify your webserver configuration, you can also use Linux firewall functionality to redirect a range of incoming high ports to the configured webserver listening port.  This will work with both Apache and Nginx based Zoneminder installs.&lt;br /&gt;
&lt;br /&gt;
The following rules are are for iptables firewalls and are working on my RHEL based server.  If you use another firewall such as nftables, firewalld, ufw, etc., you will have to translate them accordingly.  AI is very helpful for tasks such as this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;iptables -t nat -A PREROUTING -p tcp -m multiport --dports 30000:30100 -j REDIRECT --to-ports 443&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are running a headless Zoneminder install the above rule is all you will need.  Replace 30000:30100 with the range of ports you want to redirect.  Of course this has to match the multiport starting port in Zoneminder&amp;#039;s network settings.  You can also use port 80 as destination if you don&amp;#039;t use https, or whatever other port your webserver listens on.  You can quickly check it is working by browsing to http(s)://yourzomeminderserver:30099 (or some other redirected port).&lt;br /&gt;
&lt;br /&gt;
If your ZM server has a graphical Linux install and a directly connected display, you will also need some additional configuration to make this work as localhost traffic does not use the same prerouting rules.  First you need to enable routing on the localnet network.  Only do this if you trust the other devices on your local LAN, don&amp;#039;t do this if you connect directly to the internet.  Add the following line to /etc/sysctl.conf (or the appropriate location for your distribution). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;net.ipv4.conf.all.route_localnet = 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can apply it immediately with sysctl -p.  Then you need to add a second firewall rule to redirect the loopback address ports.  Use the same port range and destination port as the previous rule.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;iptables -t nat -I OUTPUT -p tcp -o lo --dport 30000:30100 -j REDIRECT --to-ports 443&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&amp;#039;s it.  Be sure to make any rules you add persistent across reboots with whatever firewall management method you are using.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Multi_Port&amp;diff=17866</id>
		<title>Multi Port</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Multi_Port&amp;diff=17866"/>
		<updated>2026-01-09T16:46:27Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Added test&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;All browsers currently have a limitation where there can only be 6 connections to a server. This can be gotten around by using multi port, or by adjusting a Firefox about:config setting. &lt;br /&gt;
&lt;br /&gt;
==Apache Settings for Multi Port==&lt;br /&gt;
This is the optimal approach as it will work on any browser. If you are in a commercial setting, or have other users besides yourself, it is almost required to setup Multi Port through Apache. If on the other hand you are on your own, you may consider simply using Firefox. Multi port, does just what it says: It will redirect multiple server connections through different ports, which is a workaround for the browser security limitation. &lt;br /&gt;
&lt;br /&gt;
Here are some quick configs for 50 cameras. Note: The below config has been confirmed to work with only HTTP. HTTPS may need additional configuration. If you get an error about missing a &amp;gt; bracket on the 000-default.conf, make it so all these _default_:#### are on one line. Then it will work. It&amp;#039;s not easy to read it in that format, so it has been justified for viewing here.&lt;br /&gt;
&lt;br /&gt;
====/etc/apache2/sites-available/000-default.conf:====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost _default_:80 _default_:30000&lt;br /&gt;
        _default_:30001 _default_:30002 _default_:30003 _default_:30004&lt;br /&gt;
        _default_:30005 _default_:30006 _default_:30007 _default_:30008 _default_:30009 _default_:30010&lt;br /&gt;
_default_:30011 _default_:30012 _default_:30013 _default_:30014&lt;br /&gt;
_default_:30015 _default_:30016 _default_:30017 _default_:30018&lt;br /&gt;
_default_:30019 _default_:30020 _default_:30021 _default_:30022&lt;br /&gt;
_default_:30023 _default_:30024 _default_:30025 _default_:30026&lt;br /&gt;
_default_:30027 _default_:30028 _default_:30029 _default_:30030&lt;br /&gt;
_default_:30031 _default_:30032 _default_:30033 _default_:30034&lt;br /&gt;
_default_:30035 _default_:30036 _default_:30037 _default_:30038&lt;br /&gt;
_default_:30039 _default_:30040 _default_:30041 _default_:30042&lt;br /&gt;
_default_:30043 _default_:30044 _default_:30045 _default_:30046&lt;br /&gt;
_default_:30047 _default_:30048 _default_:30049 _default_:30050&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====/etc/apache2/ports.conf:====&lt;br /&gt;
Note:this doesn&amp;#039;t include everything in the file. Make sure to insert into the existing ports file where changes are appropriate and not simply clobber the original file with below.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        Listen 80&lt;br /&gt;
        Listen 30000&lt;br /&gt;
        Listen 30001&lt;br /&gt;
        Listen 30002&lt;br /&gt;
        Listen 30003&lt;br /&gt;
        Listen 30004&lt;br /&gt;
        Listen 30005&lt;br /&gt;
        Listen 30006&lt;br /&gt;
        Listen 30007&lt;br /&gt;
        Listen 30008&lt;br /&gt;
        Listen 30009&lt;br /&gt;
        Listen 30010&lt;br /&gt;
        Listen 30011&lt;br /&gt;
        Listen 30012&lt;br /&gt;
        Listen 30013&lt;br /&gt;
        Listen 30014&lt;br /&gt;
        Listen 30015&lt;br /&gt;
        Listen 30016&lt;br /&gt;
        Listen 30017&lt;br /&gt;
        Listen 30018&lt;br /&gt;
        Listen 30019&lt;br /&gt;
        Listen 30020&lt;br /&gt;
        Listen 30021&lt;br /&gt;
        Listen 30022&lt;br /&gt;
        Listen 30023&lt;br /&gt;
        Listen 30024&lt;br /&gt;
        Listen 30025&lt;br /&gt;
        Listen 30026&lt;br /&gt;
        Listen 30027&lt;br /&gt;
        Listen 30028&lt;br /&gt;
        Listen 30029&lt;br /&gt;
        Listen 30030&lt;br /&gt;
        Listen 30031&lt;br /&gt;
        Listen 30032&lt;br /&gt;
        Listen 30033&lt;br /&gt;
        Listen 30034&lt;br /&gt;
        Listen 30035&lt;br /&gt;
        Listen 30036&lt;br /&gt;
        Listen 30037&lt;br /&gt;
        Listen 30038&lt;br /&gt;
        Listen 30039&lt;br /&gt;
        Listen 30040&lt;br /&gt;
        Listen 30041&lt;br /&gt;
        Listen 30042&lt;br /&gt;
        Listen 30043&lt;br /&gt;
        Listen 30044&lt;br /&gt;
        Listen 30045&lt;br /&gt;
        Listen 30046&lt;br /&gt;
        Listen 30047&lt;br /&gt;
        Listen 30048&lt;br /&gt;
        Listen 30049&lt;br /&gt;
        Listen 30050&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You will also need to enable multi port in Zoneminder options. Please read the medium guide below for more information.&lt;br /&gt;
&lt;br /&gt;
===Resources===&lt;br /&gt;
* https://medium.com/zmninja/multi-port-storage-areas-and-more-d5836a336c93 - A developer of Zoneminder wrote up this helpful guide.&lt;br /&gt;
&lt;br /&gt;
==Firefox About:Config==&lt;br /&gt;
 &lt;br /&gt;
In Firefox you can make a change to the configuration to get cameras to display. First go to [https://support.mozilla.org/en-US/kb/about-config-editor-firefox about:config ].  And search for, then adjust [http://kb.mozillazine.org/Network.http.max-persistent-connections-per-server Network.http.max-persistent-connections-per-server]. The max persistent connections per server must be &amp;gt; 6 in order to see &amp;gt; 6 camera streams. It can be set to any number, so perhaps you might set it to 100 or 150.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Firefox About Config Edit.png|900px|||]]&lt;br /&gt;
&lt;br /&gt;
===Resources===&lt;br /&gt;
The forum will have a number of posts regarding this change. It has been discussed extensively. Such as:&lt;br /&gt;
* [https://forums.zoneminder.com/viewtopic.php?f=32&amp;amp;t=23417&amp;amp;p=89249  ZM Forums: Problems when two people are watching live]&lt;br /&gt;
&lt;br /&gt;
[[Category:Dummies_Guide]]&lt;br /&gt;
&lt;br /&gt;
==Alternative firewall based method==&lt;br /&gt;
&lt;br /&gt;
If you are unable or prefer not to modify your webserver configuration, you can also use Linux firewall functionality to redirect a range of incoming high ports to the configured webserver listening port.  This will work with both Apache and Nginx based Zoneminder installs.&lt;br /&gt;
&lt;br /&gt;
The following rules are are for iptables firewalls and are working on my RHEL based server.  If you use another firewall such as nftables, firewalld, ufw, etc., you will have to translate them accordingly.  AI is very helpful for tasks such as this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;iptables -t nat -A PREROUTING -p tcp -m multiport --dports 30000:30100 -j REDIRECT --to-ports 443&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are running a headless Zoneminder install this one rule is all you will need.  Replace 30000:30100 with the range of ports you want to redirect.  Of course this has to match the multiport starting port in Zoneminder&amp;#039;s network settings.  You can also use port 80 as destination if you don&amp;#039;t use https, or whatever other port your webserver listens on.&lt;br /&gt;
&lt;br /&gt;
If your ZM server has a graphical Linux install and a directly connected display, you will also need some additional configuration to make this work on the local desktop as internal traffic does not use the same prerouting rules.  First you need to enable routing on the localnet network.  Only do this if you trust the other devices on your local LAN, don&amp;#039;t do this if you connect directly to the internet.  Add the following line to /etc/sysctl.conf (or the appropriate location for your distribution). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;net.ipv4.conf.all.route_localnet = 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can apply it immediately with sysctl -p.  Then you need to add a second firewall rule to redirect the loopback address ports.  Use the same port range and destination port as the previous rule.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;iptables -t nat -I OUTPUT -p tcp -o lo --dport 30000:30100 -j REDIRECT --to-ports 443&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&amp;#039;s it!  You can quickly check it is working by browsing to http(s)://yourzomeminderserver:30099 (or some other redirected port).  Be sure to make any rules you add persistent across reboots with whatever firewall management method you are using.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Multi_Port&amp;diff=17865</id>
		<title>Multi Port</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Multi_Port&amp;diff=17865"/>
		<updated>2026-01-09T05:44:37Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;All browsers currently have a limitation where there can only be 6 connections to a server. This can be gotten around by using multi port, or by adjusting a Firefox about:config setting. &lt;br /&gt;
&lt;br /&gt;
==Apache Settings for Multi Port==&lt;br /&gt;
This is the optimal approach as it will work on any browser. If you are in a commercial setting, or have other users besides yourself, it is almost required to setup Multi Port through Apache. If on the other hand you are on your own, you may consider simply using Firefox. Multi port, does just what it says: It will redirect multiple server connections through different ports, which is a workaround for the browser security limitation. &lt;br /&gt;
&lt;br /&gt;
Here are some quick configs for 50 cameras. Note: The below config has been confirmed to work with only HTTP. HTTPS may need additional configuration. If you get an error about missing a &amp;gt; bracket on the 000-default.conf, make it so all these _default_:#### are on one line. Then it will work. It&amp;#039;s not easy to read it in that format, so it has been justified for viewing here.&lt;br /&gt;
&lt;br /&gt;
====/etc/apache2/sites-available/000-default.conf:====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost _default_:80 _default_:30000&lt;br /&gt;
        _default_:30001 _default_:30002 _default_:30003 _default_:30004&lt;br /&gt;
        _default_:30005 _default_:30006 _default_:30007 _default_:30008 _default_:30009 _default_:30010&lt;br /&gt;
_default_:30011 _default_:30012 _default_:30013 _default_:30014&lt;br /&gt;
_default_:30015 _default_:30016 _default_:30017 _default_:30018&lt;br /&gt;
_default_:30019 _default_:30020 _default_:30021 _default_:30022&lt;br /&gt;
_default_:30023 _default_:30024 _default_:30025 _default_:30026&lt;br /&gt;
_default_:30027 _default_:30028 _default_:30029 _default_:30030&lt;br /&gt;
_default_:30031 _default_:30032 _default_:30033 _default_:30034&lt;br /&gt;
_default_:30035 _default_:30036 _default_:30037 _default_:30038&lt;br /&gt;
_default_:30039 _default_:30040 _default_:30041 _default_:30042&lt;br /&gt;
_default_:30043 _default_:30044 _default_:30045 _default_:30046&lt;br /&gt;
_default_:30047 _default_:30048 _default_:30049 _default_:30050&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====/etc/apache2/ports.conf:====&lt;br /&gt;
Note:this doesn&amp;#039;t include everything in the file. Make sure to insert into the existing ports file where changes are appropriate and not simply clobber the original file with below.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        Listen 80&lt;br /&gt;
        Listen 30000&lt;br /&gt;
        Listen 30001&lt;br /&gt;
        Listen 30002&lt;br /&gt;
        Listen 30003&lt;br /&gt;
        Listen 30004&lt;br /&gt;
        Listen 30005&lt;br /&gt;
        Listen 30006&lt;br /&gt;
        Listen 30007&lt;br /&gt;
        Listen 30008&lt;br /&gt;
        Listen 30009&lt;br /&gt;
        Listen 30010&lt;br /&gt;
        Listen 30011&lt;br /&gt;
        Listen 30012&lt;br /&gt;
        Listen 30013&lt;br /&gt;
        Listen 30014&lt;br /&gt;
        Listen 30015&lt;br /&gt;
        Listen 30016&lt;br /&gt;
        Listen 30017&lt;br /&gt;
        Listen 30018&lt;br /&gt;
        Listen 30019&lt;br /&gt;
        Listen 30020&lt;br /&gt;
        Listen 30021&lt;br /&gt;
        Listen 30022&lt;br /&gt;
        Listen 30023&lt;br /&gt;
        Listen 30024&lt;br /&gt;
        Listen 30025&lt;br /&gt;
        Listen 30026&lt;br /&gt;
        Listen 30027&lt;br /&gt;
        Listen 30028&lt;br /&gt;
        Listen 30029&lt;br /&gt;
        Listen 30030&lt;br /&gt;
        Listen 30031&lt;br /&gt;
        Listen 30032&lt;br /&gt;
        Listen 30033&lt;br /&gt;
        Listen 30034&lt;br /&gt;
        Listen 30035&lt;br /&gt;
        Listen 30036&lt;br /&gt;
        Listen 30037&lt;br /&gt;
        Listen 30038&lt;br /&gt;
        Listen 30039&lt;br /&gt;
        Listen 30040&lt;br /&gt;
        Listen 30041&lt;br /&gt;
        Listen 30042&lt;br /&gt;
        Listen 30043&lt;br /&gt;
        Listen 30044&lt;br /&gt;
        Listen 30045&lt;br /&gt;
        Listen 30046&lt;br /&gt;
        Listen 30047&lt;br /&gt;
        Listen 30048&lt;br /&gt;
        Listen 30049&lt;br /&gt;
        Listen 30050&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You will also need to enable multi port in Zoneminder options. Please read the medium guide below for more information.&lt;br /&gt;
&lt;br /&gt;
===Resources===&lt;br /&gt;
* https://medium.com/zmninja/multi-port-storage-areas-and-more-d5836a336c93 - A developer of Zoneminder wrote up this helpful guide.&lt;br /&gt;
&lt;br /&gt;
==Firefox About:Config==&lt;br /&gt;
 &lt;br /&gt;
In Firefox you can make a change to the configuration to get cameras to display. First go to [https://support.mozilla.org/en-US/kb/about-config-editor-firefox about:config ].  And search for, then adjust [http://kb.mozillazine.org/Network.http.max-persistent-connections-per-server Network.http.max-persistent-connections-per-server]. The max persistent connections per server must be &amp;gt; 6 in order to see &amp;gt; 6 camera streams. It can be set to any number, so perhaps you might set it to 100 or 150.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Firefox About Config Edit.png|900px|||]]&lt;br /&gt;
&lt;br /&gt;
===Resources===&lt;br /&gt;
The forum will have a number of posts regarding this change. It has been discussed extensively. Such as:&lt;br /&gt;
* [https://forums.zoneminder.com/viewtopic.php?f=32&amp;amp;t=23417&amp;amp;p=89249  ZM Forums: Problems when two people are watching live]&lt;br /&gt;
&lt;br /&gt;
[[Category:Dummies_Guide]]&lt;br /&gt;
&lt;br /&gt;
==Alternative firewall based method==&lt;br /&gt;
&lt;br /&gt;
If you are unable or prefer not to modify your webserver configuration, you can also use Linux firewall functionality to redirect a range of incoming high ports to the configured webserver listening port.  This will work with both Apache and Nginx based Zoneminder installs.&lt;br /&gt;
&lt;br /&gt;
The following rules are are for iptables firewalls and are working on my RHEL based server.  If you use another firewall such as nftables, firewalld, ufw, etc., you will have to translate them accordingly.  AI is very helpful for tasks such as this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;iptables -t nat -A PREROUTING -p tcp -m multiport --dports 30000:30100 -j REDIRECT --to-ports 443&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are running a headless Zoneminder install this one rule is all you will need.  Replace 30000:30100 with the range of ports you want to redirect.  Of course this has to match the multiport starting port in Zoneminder&amp;#039;s network settings.  You can also use port 80 as destination if you don&amp;#039;t use https, or whatever other port your webserver listens on.&lt;br /&gt;
&lt;br /&gt;
If your ZM server has a graphical Linux install and a directly connected display, you will also need some additional configuration to make this work on the local desktop as internal traffic does not use the same prerouting rules.  First you need to enable routing on the localnet network.  Only do this if you trust the other devices on your local LAN, don&amp;#039;t do this if you connect directly to the internet.  Add the following line to /etc/sysctl.conf (or the appropriate location for your distribution). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;net.ipv4.conf.all.route_localnet = 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can apply it immediately with sysctl -p.  Then you need to add a second firewall rule to redirect the loopback address ports.  Use the same port range and destination port as the previous rule.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;iptables -t nat -I OUTPUT -p tcp -o lo --dport 30000:30100 -j REDIRECT --to-ports 443&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&amp;#039;s it!  Be sure to make any rules you add persistent across reboots with whatever firewall management method you are using.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Multi_Port&amp;diff=17864</id>
		<title>Multi Port</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Multi_Port&amp;diff=17864"/>
		<updated>2026-01-08T23:06:47Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Added firewall redirect method&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;All browsers currently have a limitation where there can only be 6 connections to a server. This can be gotten around by using multi port, or by adjusting a Firefox about:config setting. &lt;br /&gt;
&lt;br /&gt;
==Apache Settings for Multi Port==&lt;br /&gt;
This is the optimal approach as it will work on any browser. If you are in a commercial setting, or have other users besides yourself, it is almost required to setup Multi Port through Apache. If on the other hand you are on your own, you may consider simply using Firefox. Multi port, does just what it says: It will redirect multiple server connections through different ports, which is a workaround for the browser security limitation. &lt;br /&gt;
&lt;br /&gt;
Here are some quick configs for 50 cameras. Note: The below config has been confirmed to work with only HTTP. HTTPS may need additional configuration. If you get an error about missing a &amp;gt; bracket on the 000-default.conf, make it so all these _default_:#### are on one line. Then it will work. It&amp;#039;s not easy to read it in that format, so it has been justified for viewing here.&lt;br /&gt;
&lt;br /&gt;
====/etc/apache2/sites-available/000-default.conf:====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost _default_:80 _default_:30000&lt;br /&gt;
        _default_:30001 _default_:30002 _default_:30003 _default_:30004&lt;br /&gt;
        _default_:30005 _default_:30006 _default_:30007 _default_:30008 _default_:30009 _default_:30010&lt;br /&gt;
_default_:30011 _default_:30012 _default_:30013 _default_:30014&lt;br /&gt;
_default_:30015 _default_:30016 _default_:30017 _default_:30018&lt;br /&gt;
_default_:30019 _default_:30020 _default_:30021 _default_:30022&lt;br /&gt;
_default_:30023 _default_:30024 _default_:30025 _default_:30026&lt;br /&gt;
_default_:30027 _default_:30028 _default_:30029 _default_:30030&lt;br /&gt;
_default_:30031 _default_:30032 _default_:30033 _default_:30034&lt;br /&gt;
_default_:30035 _default_:30036 _default_:30037 _default_:30038&lt;br /&gt;
_default_:30039 _default_:30040 _default_:30041 _default_:30042&lt;br /&gt;
_default_:30043 _default_:30044 _default_:30045 _default_:30046&lt;br /&gt;
_default_:30047 _default_:30048 _default_:30049 _default_:30050&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====/etc/apache2/ports.conf:====&lt;br /&gt;
Note:this doesn&amp;#039;t include everything in the file. Make sure to insert into the existing ports file where changes are appropriate and not simply clobber the original file with below.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        Listen 80&lt;br /&gt;
        Listen 30000&lt;br /&gt;
        Listen 30001&lt;br /&gt;
        Listen 30002&lt;br /&gt;
        Listen 30003&lt;br /&gt;
        Listen 30004&lt;br /&gt;
        Listen 30005&lt;br /&gt;
        Listen 30006&lt;br /&gt;
        Listen 30007&lt;br /&gt;
        Listen 30008&lt;br /&gt;
        Listen 30009&lt;br /&gt;
        Listen 30010&lt;br /&gt;
        Listen 30011&lt;br /&gt;
        Listen 30012&lt;br /&gt;
        Listen 30013&lt;br /&gt;
        Listen 30014&lt;br /&gt;
        Listen 30015&lt;br /&gt;
        Listen 30016&lt;br /&gt;
        Listen 30017&lt;br /&gt;
        Listen 30018&lt;br /&gt;
        Listen 30019&lt;br /&gt;
        Listen 30020&lt;br /&gt;
        Listen 30021&lt;br /&gt;
        Listen 30022&lt;br /&gt;
        Listen 30023&lt;br /&gt;
        Listen 30024&lt;br /&gt;
        Listen 30025&lt;br /&gt;
        Listen 30026&lt;br /&gt;
        Listen 30027&lt;br /&gt;
        Listen 30028&lt;br /&gt;
        Listen 30029&lt;br /&gt;
        Listen 30030&lt;br /&gt;
        Listen 30031&lt;br /&gt;
        Listen 30032&lt;br /&gt;
        Listen 30033&lt;br /&gt;
        Listen 30034&lt;br /&gt;
        Listen 30035&lt;br /&gt;
        Listen 30036&lt;br /&gt;
        Listen 30037&lt;br /&gt;
        Listen 30038&lt;br /&gt;
        Listen 30039&lt;br /&gt;
        Listen 30040&lt;br /&gt;
        Listen 30041&lt;br /&gt;
        Listen 30042&lt;br /&gt;
        Listen 30043&lt;br /&gt;
        Listen 30044&lt;br /&gt;
        Listen 30045&lt;br /&gt;
        Listen 30046&lt;br /&gt;
        Listen 30047&lt;br /&gt;
        Listen 30048&lt;br /&gt;
        Listen 30049&lt;br /&gt;
        Listen 30050&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You will also need to enable multi port in Zoneminder options. Please read the medium guide below for more information.&lt;br /&gt;
&lt;br /&gt;
===Resources===&lt;br /&gt;
* https://medium.com/zmninja/multi-port-storage-areas-and-more-d5836a336c93 - A developer of Zoneminder wrote up this helpful guide.&lt;br /&gt;
&lt;br /&gt;
==Firefox About:Config==&lt;br /&gt;
 &lt;br /&gt;
In Firefox you can make a change to the configuration to get cameras to display. First go to [https://support.mozilla.org/en-US/kb/about-config-editor-firefox about:config ].  And search for, then adjust [http://kb.mozillazine.org/Network.http.max-persistent-connections-per-server Network.http.max-persistent-connections-per-server]. The max persistent connections per server must be &amp;gt; 6 in order to see &amp;gt; 6 camera streams. It can be set to any number, so perhaps you might set it to 100 or 150.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Firefox About Config Edit.png|900px|||]]&lt;br /&gt;
&lt;br /&gt;
===Resources===&lt;br /&gt;
The forum will have a number of posts regarding this change. It has been discussed extensively. Such as:&lt;br /&gt;
* [https://forums.zoneminder.com/viewtopic.php?f=32&amp;amp;t=23417&amp;amp;p=89249  ZM Forums: Problems when two people are watching live]&lt;br /&gt;
&lt;br /&gt;
[[Category:Dummies_Guide]]&lt;br /&gt;
&lt;br /&gt;
==Alternative firewall based method==&lt;br /&gt;
&lt;br /&gt;
If you are unable or prefer not to modify your webserver configuration, you can also use Linux firewall functionality to redirect a range of incoming high ports to the configured webserver listening port.  This will work with both Apache and Nginx based Zoneminder installs.&lt;br /&gt;
&lt;br /&gt;
The following rules are are for iptables firewalls and are working on my RHEL based server.  If you use another firewall such as nftables, firewalld, ufw, etc., you will have to translate them accordingly.  AI is very helpful for tasks such as this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;iptables -t nat -A PREROUTING -p tcp -m multiport --dports 30000:30100 -j REDIRECT --to-ports 443&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are running a headless Zoneminder install that is all you will need.  Replace 30000:30100 with the range of ports you want to redirect.  Of course this has to match the multiport starting port in Zoneminder&amp;#039;s network settings.  You can also use port 80 as destination if you don&amp;#039;t use https, or whatever other port your webserver listens on.&lt;br /&gt;
&lt;br /&gt;
If your ZM server has a graphical Linux install and a directly connected display, you will also need some additional configuration to make this work on the local desktop as internal traffic does not use the same prerouting rules.  First you need to enable routing on the localnet network.  Only do this if you trust the other devices on your local LAN.  Add the following line to /etc/sysctl.conf (or the appropriate location for your distribution). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;net.ipv4.conf.all.route_localnet = 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can apply it immediately with sysctl -p.  Then you need to add a second firewall rule to redirect the loopback address.  Use the same port range and destination port as the previous rule.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;iptables -t nat -I OUTPUT -p tcp -o lo --dport 30000:30100 -j REDIRECT --to-ports 443&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&amp;#039;s it!  Be sure to make any rules you add persistent across reboots with whatever firewall management method you are using.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17860</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17860"/>
		<updated>2025-12-29T17:39:21Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a destination.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17859</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17859"/>
		<updated>2025-12-29T17:36:30Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a target.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17858</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17858"/>
		<updated>2025-12-29T06:23:44Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Fixed default logfile paths, added link to Rclone mount page, cleanup&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
You can also use an Rclone mount point as a target.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/#providers&lt;br /&gt;
&lt;br /&gt;
Follow the instructions here to set up your cloud storage as a local mountpoint.&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/commands/rclone_mount/ &lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this file uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  This will be the mount point for the cloud storage configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17857</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17857"/>
		<updated>2025-12-27T06:45:50Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Added Rclone info and link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3. You can also use an Rclone mount point as a target.  I&amp;#039;ve tested this with Google Drive but it should work with any cloud provider supported by Rclone (also including S3).  See here for more info;&lt;br /&gt;
&lt;br /&gt;
https://rclone.org/&lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17846</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17846"/>
		<updated>2025-12-24T19:45:20Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Added more S3 optimizing.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Make sure your remote location is accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     whole_file = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode directive, though I have not tested this.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; and &amp;#039;&amp;#039;whole_file = true&amp;#039;&amp;#039; Sets these rsync options and are a further optimization for S3 API use efficiency.  This makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17845</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17845"/>
		<updated>2025-12-24T18:11:49Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
&lt;br /&gt;
Note the version of lsyncd included in EPEL for Rocky 8 (2.2.2-9) has a noticeable memory leak.  Building from source solved this issue.&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Make sure your remote location is accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses Lua syntax, not Bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     inplace = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.  It may be further possible to remove this limitation with the inotifyMode and whole_file directives, though I have not tested this.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;inplace = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --in-place directive, and is a further optimization for S3 API use efficiency.  Between ignore-times and in-place this makes rsync&amp;#039;s functionality less &amp;quot;sync&amp;quot; and more straight &amp;quot;copy&amp;quot;, again because we can safely assume only newly created files will ever be transferred.  These two options used together make this method of backing up events very close in terms of total traffic cost to natively writing to S3 straight from Zoneminder.  Once again if your provider does not charge for API calls this will not matter to you.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17843</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17843"/>
		<updated>2025-12-17T18:02:44Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Make sure your remote location is accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purpose it is being used here it is safe to assume you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17842</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17842"/>
		<updated>2025-12-17T16:35:53Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Ignore times S3 optimization&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It&amp;#039;s possible to use lsync daemon, which leverages the inotify https://en.wikipedia.org/wiki/Inotify kernel subsystem to run triggers based on events in a directory (create, modify, delete). Triggers can be varied (you can run any arbitrary command) but in the default use case, is as a filesystem backup / clone using the rsync command.&lt;br /&gt;
&lt;br /&gt;
You may also want to consider other options. Inotify has command line tools, and there is an incrond, which uses cron syntax to run commands based on inotify.&lt;br /&gt;
&lt;br /&gt;
==About==&lt;br /&gt;
After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Make sure your remote location is accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be &amp;#039;&amp;#039;&amp;#039;VERY CAREFUL&amp;#039;&amp;#039;&amp;#039; if you are testing remote write/sync/copy operations to a destination that already has important data on it (see links below).  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     ignore_times = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves ownership and permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;ignore_times = true&amp;#039;&amp;#039; This sets rsync&amp;#039;s --ignore-times directive.  For the purposes it is being used here it is assumed you will never have a newer copy of the files you are uploading already existing in your destination.  Removing this check reduces API calls and is a noticeable optimization for AWS.  If your cloud provider does not meter traffic you probably won&amp;#039;t notice any difference with this either way.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change or remove these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to again have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* https://github.com/lsyncd/lsyncd/issues/668 - Beware that lsync will do rsync with deletes on by default. With some casual testing, you might inadvertedly delete your home directory or root filesystem. From the github: &amp;quot;&amp;#039;&amp;#039;I was told many times delete by default was a bad idea and was a result of the usual use case in the sense of replicating the target exactly like the source. I suppose the people telling so have a point.&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
* https://docs.rockylinux.org/10/books/learning_rsync/06_rsync_inotify/&lt;br /&gt;
* https://linuxvox.com/blog/what-is-the-proper-way-to-use-inotify/ - Using inotify-tools command line interface&lt;br /&gt;
* https://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/ - Probably easier than lsyncd. (Was not available in default Debian Bullseye (have to use backports https://qa.debian.org/madison.php?package=incron&amp;amp;table=archived&amp;amp;a=&amp;amp;c=&amp;amp;s=#) but is in most other Debian releases.).&lt;br /&gt;
* https://wiki.alpinelinux.org/wiki/Inotifyd&lt;br /&gt;
* https://wiki.archlinux.org/title/Incron&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17825</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17825"/>
		<updated>2025-12-13T19:15:04Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Make sure your remote location is accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be VERY CAREFUL if you are testing remote write/sync/copy operations to a destination that already has important data on it.  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is also pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here so read the docs, but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17824</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17824"/>
		<updated>2025-12-13T19:13:33Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Added increase watch # info&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Make sure your remote location is accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be VERY CAREFUL if you are testing remote write/sync/copy operations to a destination that already has important data on it.  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change these unless you are absolutely sure you know what you are doing. There are also options besides true and false so read the docs for more info.  You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here so read the docs, but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Note:  If you receive errors similar to &amp;quot;maximum number of watches reached&amp;quot; in the logs you may need to increase the default limits on your system for /proc/sys/fs/inotify/max_user_instances and /proc/sys/fs/inotify/max_user_watches.  See here for more details;&lt;br /&gt;
&lt;br /&gt;
https://support.scc.suse.com/s/kb/360054835111?language=en_US&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17823</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17823"/>
		<updated>2025-12-13T02:40:41Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Better language&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Make sure your remote location is accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be VERY CAREFUL if you are testing remote write/sync/copy operations to a destination that already has important data on it.  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;This is how often rsync will run if there are any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change these unless you are absolutely sure you know what you are doing. You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here so read the docs, but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17822</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17822"/>
		<updated>2025-12-13T00:27:31Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Make sure your remote location is accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be VERY CAREFUL if you are testing remote write/sync/copy operations to a destination that already has important data on it.  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;quot;&amp;#039;&amp;#039; (not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;this is how often to run rsync if it is not triggered by any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change these unless you are absolutely sure you know what you are doing. You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here so read the docs, but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17821</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17821"/>
		<updated>2025-12-12T16:28:27Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Make sure your remote location is accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be VERY CAREFUL if you are testing remote write/sync/copy operations to a destination that already has important data on it.  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;logfile = &amp;#039;&amp;#039; is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;#039;&amp;#039;(not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;this is how often to run rsync if it is not triggered by any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change these unless you are absolutely sure you know what you are doing. You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here so read the docs, but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17820</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17820"/>
		<updated>2025-12-12T16:20:57Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Small corrections&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the installation instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch my Zoneminder storage location and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Make sure your remote location is accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be VERY CAREFUL if you are testing remote write/sync/copy operations to a destination that already has important data on it.  I highly recommend initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Logging is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;quot;/somedirectory&amp;#039;&amp;#039;(not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay = &amp;#039;&amp;#039;this is how often to run rsync if it is not triggered by any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay = # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;archive = true &amp;#039;&amp;#039;This preserves permissions on the copied files.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true. This also means if you accidentally delete events from your local storage they will be deleted from the cloud as well.  Use with caution.   &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to use against an existing target if you just run it on your machine without heeding my previous warning. Don&amp;#039;t change these unless you are absolutely sure you know what you are doing. You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here so read the docs, but the simple config above is all I need for my purposes.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor id and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17817</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17817"/>
		<updated>2025-12-12T03:07:38Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch a single event directory and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Make sure your remote location is accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be VERY CAREFUL if you are testing remote write/sync/copy operations to a destination that already has important data on it.  I suggest initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Logging is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;#039;&amp;#039;(not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay - &amp;#039;&amp;#039;this is how often to run rsync if it is not triggered by any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay - # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.  Be warned it will make your target an exact mirror of your source.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to run against an existing target if you just blindly copy it to your machine without heeding my previous warning. Don&amp;#039;t change these unless you are absolutely sure you know what you are doing. You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here so read the docs, but at this point you should get the idea.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor # and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17816</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17816"/>
		<updated>2025-12-12T02:58:26Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch a single event directory and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3FS-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Make sure your remote location is accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be VERY CAREFUL if you are testing remote write/sync/copy operations to a destination that already has important data on it.  I suggest initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Logging is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;#039;&amp;#039;(not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay - &amp;#039;&amp;#039;this is how often to run rsync if it is not triggered by any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay - # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to run against an existing target if you just blindly copy it to your machine without heeding my previous warning. Don&amp;#039;t change these unless you are absolutely sure you know what you are doing. You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here so read the docs, but at this point you should get the idea.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor # and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17815</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17815"/>
		<updated>2025-12-12T02:55:10Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch a single event directory and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3fs-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Be sure to have your remote location configured and accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be VERY CAREFUL if you are testing remote write/sync/copy operations to a destination that already has important data on it.  I suggest initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Logging is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.  In my case it is the mount point for the S3FS configured previously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;#039;&amp;#039;(not shown, part of sync block) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay - &amp;#039;&amp;#039;this is how often to run rsync if it is not triggered by any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay - # &amp;#039;&amp;#039; (not shown, part of settings block) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to run against an existing target if you just blindly copy it to your machine without heeding my previous warning. Don&amp;#039;t change these unless you are absolutely sure you know what you are doing. You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here so read the docs, but at this point you should get the idea.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  By default it will be disabled so you will need to enable it to run automatically after suitable testing.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor # and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17814</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17814"/>
		<updated>2025-12-12T00:30:28Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch a single event directory and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3fs-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Be sure to have your remote location configured and accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be VERY CAREFUL if you are testing remote write/sync/copy operations to a destination that already has important data on it.  I suggest initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Logging is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxProcesses = 1&amp;#039;&amp;#039; is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;insist = true&amp;#039;&amp;#039; allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;source = &amp;#039;&amp;#039;The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;target = &amp;#039;&amp;#039;The location to copy events to.  Must be accessible via rsync.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;targetdir = &amp;#039;&amp;#039;(not shown) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delay - &amp;#039;&amp;#039;this is how often to run rsync if it is not triggered by any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;maxDelay - # &amp;#039;&amp;#039; (not shown) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;delete = false&amp;#039;&amp;#039; this is pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true.  &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;init = false&amp;#039;&amp;#039; causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to run against an existing target if you just blindly copy it to your machine without heeding my previous warning. You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here so read the docs, but at this point you should get the idea.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor # and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17813</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17813"/>
		<updated>2025-12-12T00:20:49Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch a single event directory and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3fs-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Be sure to have your remote location configured and accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be VERY CAREFUL if you are testing remote write/sync/copy operations to a destination that already has important data on it.  I suggest initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Logging is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
maxProcesses = 1 is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
insist = true allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
source = The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
target = The location to copy events to.  Must be accessible via rsync.&lt;br /&gt;
&lt;br /&gt;
targetdir = (not shown) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
delay - this is how often to run rsync if it is not triggered by any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
maxDelay - #  (not shown) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
delete = false this is pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true.  &lt;br /&gt;
&lt;br /&gt;
init = false causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to run against an existing target if you just blindly copy it to your machine without heeding my previous warning. You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here so read the docs, but at this point you should get the idea.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]#&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor # and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17812</id>
		<title>Realtime event backup to cloud storage</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Realtime_event_backup_to_cloud_storage&amp;diff=17812"/>
		<updated>2025-12-12T00:12:33Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: Configuring lsyncd for realtime backups.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;After recently migrating from another popular NVR software on Windows to Zoneminder on Linux I started looking for a way to do near-real-time backups of my events to offsite storage, simply as a way to have a copy should the local ZM server be compromised by break-in, fire, etc.  Software such as Dropbox, Google Drive, etc. make it relatively easy to do this on Windows but the setup on Linux takes bit more effort.&lt;br /&gt;
&lt;br /&gt;
I wanted to keep it as simple as possible and don&amp;#039;t need my backup solution to interface with the ZM database at all.  After experimenting with several other options, I have settled on an application called lsyncd.  It runs as a service and leverages inotify and rsync to watch a directory or directories and mirror only new changes to another storage location.  Lsyncd should be available as a package for most Linux distributions.  I used the instructions here for my Rocky Linux system;&lt;br /&gt;
&lt;br /&gt;
	https://docs.rockylinux.org/10/guides/backup/mirroring_lsyncd/&lt;br /&gt;
	&lt;br /&gt;
At this point it is worthwhile to at least skim through the docs.&lt;br /&gt;
&lt;br /&gt;
	https://lsyncd.github.io/lsyncd/manual/config/file/&lt;br /&gt;
	&lt;br /&gt;
	https://linux.die.net/man/1/rsync&lt;br /&gt;
	&lt;br /&gt;
Both lsyncd and rsync are highly configurable and incredibly powerful.  For many people the use case will be very simple like my own which I will describe here - watch a single event directory and copy new files to a remote location, but much more complex configurations are certainly possible. In my case I&amp;#039;ll be writing to an Amazon S3 bucket which I have already setup using S3fs-FUSE according to the documentation here;  &lt;br /&gt;
&lt;br /&gt;
	https://zoneminder.readthedocs.io/en/latest/userguide/options/options_storage.html&lt;br /&gt;
	&lt;br /&gt;
Note you do not need to add the S3FS as a storage location in ZM to use lsyncd to copy files there, that is only required for ZM to write events directly to S3.  Configuring rsync is beyond the scope of this document but this solution will work with any target that rsync is capable of writing to.  Be sure to have your remote location configured and accessible via rsync before proceeding.&lt;br /&gt;
&lt;br /&gt;
Once lsyncd is installed you will need to create a configuration file in /etc/lsyncd.conf.  I&amp;#039;ve included mine as an example below.  Note this script uses LUA syntax, not bash. Be VERY CAREFUL if you are testing remote write/sync/copy operations to a destination that already has important data on it.  I suggest initial testing with an empty bucket or target filesystem.  You have been warned.&lt;br /&gt;
&lt;br /&gt;
settings {&lt;br /&gt;
&lt;br /&gt;
   logfile = &amp;quot;/var/log/lsyncd.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusFile = &amp;quot;/var/log/lsyncd-status.log&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   statusInterval = 10,&lt;br /&gt;
&lt;br /&gt;
   maxProcesses = 1,&lt;br /&gt;
&lt;br /&gt;
   insist = true,&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sync {&lt;br /&gt;
&lt;br /&gt;
   default.rsync,&lt;br /&gt;
&lt;br /&gt;
   source = &amp;quot;/home/Cameras&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   target = &amp;quot;/media/aws&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
   delete = false, &lt;br /&gt;
&lt;br /&gt;
   delay = 10,    &lt;br /&gt;
&lt;br /&gt;
   init = false,&lt;br /&gt;
&lt;br /&gt;
   rsync = {&lt;br /&gt;
&lt;br /&gt;
     archive = true,&lt;br /&gt;
&lt;br /&gt;
     compress = false&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Logging is pretty self-explanatory.  Be sure to set up log rotation after you are done setting lsyncd up, if required.&lt;br /&gt;
&lt;br /&gt;
maxProcesses = 1 is the default.  If you are syncing multiple sources or targets you may want to increase this.&lt;br /&gt;
&lt;br /&gt;
insist = true allows the service to start even if the target is not ready.  I got errors without this which went away when I added it.  YMMV.&lt;br /&gt;
&lt;br /&gt;
source = The directory where ZM events are stored.&lt;br /&gt;
&lt;br /&gt;
target = The location to copy events to.  Must be accessible via rsync.&lt;br /&gt;
&lt;br /&gt;
targetdir = (not shown) subdirectory to use on the target.  I&amp;#039;m just using the root directory in a dedicated bucket.  You may not want this and should specify something here.&lt;br /&gt;
&lt;br /&gt;
delay - this is how often to run rsync if it is not triggered by any filesystem changes.  Default is 15 seconds.  For many users this will be close enough to real-time for the purpose.  This can be combined with (or substituted) for another value in the settings field;&lt;br /&gt;
&lt;br /&gt;
maxDelay - #  (not shown) will queue this # of file changes before calling rsync.  Between delay and/or maxDelay it is possible to tune the timing of your file copies to your exact preference.  inotify waits for write_close  to add files to the transfer list so there are no issues with rsync trying to upload uncompleted files.  I typically only record short 10 second clips.  If you use your cameras to continuously record be aware that files in the process of being written will not be copied until they are closed.&lt;br /&gt;
&lt;br /&gt;
Two other very important variables in this file are;&lt;br /&gt;
&lt;br /&gt;
delete = false this is pretty self explanatory, file deletes will not be synced from source to target.  In the case of my S3 bucket I have lifecycle rules that handle this.  If you prefer for your remote storage location to synchronize the deletions made by Zoneminder you can set this to true.  &lt;br /&gt;
&lt;br /&gt;
init = false causes the lsyncd to not do a complete initial synchronization between source and target on service startup.  On S3 this is expensive in terms of API calls and may take a while.  I know I&amp;#039;m going to miss some events during system maintenance/OS updates etc anyway so I don&amp;#039;t consider this worth the cost, but you can set to true if you want it.&lt;br /&gt;
&lt;br /&gt;
Having delete and init set to false also should make this config fairly safe to run against an existing target if you just blindly copy it to your machine without heeding my previous warning. You can also add options to be added directly to the rsync calls. You can sync multiple directories to multiple targets with multiple options.  There are exclude options which you may find useful.  It is far too much to cover here so read the docs, but at this point you should get the idea.&lt;br /&gt;
&lt;br /&gt;
Once the service is configured you can control it with systemctl like any other systemd service.  You can check the logs for startup or running errors with journalctl.  If syncs are working correctly you can watch them as uploads are logged very shortly after the events are saved to local disk;&lt;br /&gt;
&lt;br /&gt;
[root@nvr system]# tail -50 /var/log/lsyncd.log&lt;br /&gt;
/1/2025-12-11/8707/8707-video.mp4&lt;br /&gt;
/1/2025-12-11/8707/snapshot.jpg&lt;br /&gt;
/1/2025-12-11/8707/alarm.jpg&lt;br /&gt;
/6/2025-12-11/8708/&lt;br /&gt;
/6/2025-12-11/&lt;br /&gt;
/6/&lt;br /&gt;
/6/2025-12-11/8708/8708-video.mp4&lt;br /&gt;
/6/2025-12-11/8708/snapshot.jpg&lt;br /&gt;
/6/2025-12-11/8708/alarm.jpg&lt;br /&gt;
/8/2025-12-11/8706/8706-video.mp4&lt;br /&gt;
/8/2025-12-11/8706/&lt;br /&gt;
/8/2025-12-11/&lt;br /&gt;
/8/&lt;br /&gt;
Thu Dec 11 14:30:25 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:31:50 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8709/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8709/8709-video.mp4&lt;br /&gt;
/2/2025-12-11/8709/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8709/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:00 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:04 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8710/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8710/8710-video.mp4&lt;br /&gt;
/2/2025-12-11/8710/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8710/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:11 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:32:45 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/2/2025-12-11/8711/&lt;br /&gt;
/2/2025-12-11/&lt;br /&gt;
/2/&lt;br /&gt;
/&lt;br /&gt;
/2/2025-12-11/8711/8711-video.mp4&lt;br /&gt;
/2/2025-12-11/8711/snapshot.jpg&lt;br /&gt;
/2/2025-12-11/8711/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:32:51 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
Thu Dec 11 14:37:36 2025 Normal: Calling rsync with filter-list of new/modified files/dirs&lt;br /&gt;
/3/2025-12-11/8712/&lt;br /&gt;
/3/2025-12-11/&lt;br /&gt;
/3/&lt;br /&gt;
/&lt;br /&gt;
/3/2025-12-11/8712/8712-video.mp4&lt;br /&gt;
/3/2025-12-11/8712/snapshot.jpg&lt;br /&gt;
/3/2025-12-11/8712/alarm.jpg&lt;br /&gt;
Thu Dec 11 14:37:41 2025 Normal: Finished a list after exitcode: 0&lt;br /&gt;
[root@nvr system]# &lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m happy to have offsite copies of all my events now.  While they are not tracked in the Zoneminder database, they are still saved in folders by monitor # and date for easy retrieval should the ZM database become unavailable.  Props to the ZM devs for their great software, and I hope this writeup proves useful to some other users in the future.&lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
	<entry>
		<id>http://wiki.staging.zoneminder.com/index.php?title=Helpful_user_contributed_resources&amp;diff=17811</id>
		<title>Helpful user contributed resources</title>
		<link rel="alternate" type="text/html" href="http://wiki.staging.zoneminder.com/index.php?title=Helpful_user_contributed_resources&amp;diff=17811"/>
		<updated>2025-12-12T00:04:56Z</updated>

		<summary type="html">&lt;p&gt;Kernelkurtz: /* Community docs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==FAQ==&lt;br /&gt;
&lt;br /&gt;
[[FAQ]]&lt;br /&gt;
&lt;br /&gt;
==Installation==&lt;br /&gt;
&lt;br /&gt;
[http://zoneminder.readthedocs.org/en/latest/installationguide/index.html Official install guides ]&lt;br /&gt;
&lt;br /&gt;
[https://wiki.zoneminder.com/Ubuntu  Ubuntu specific guides (currently more updated than official guides)]&lt;br /&gt;
&lt;br /&gt;
[https://wiki.zoneminder.com/Debian  Debian specific guides]&lt;br /&gt;
&lt;br /&gt;
[https://wiki.zoneminder.com/Ubuntu#Common_Issues_with_Zoneminder_Installation_on_Ubuntu._Includes_upgrade_instructions.21 Common problems migrating from old versions to new versions ]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/pliablepixels/zmNinja/wiki/Configuring-ZoneMinder-with-API API installation/validation guide]-  (use the link above first, if you face problems, refer to this link)&lt;br /&gt;
&lt;br /&gt;
[https://wiki.zoneminder.com/Using_a_dedicated_Hard_Drive Using a dedicated Hard Drive] - Adding HDDs.&lt;br /&gt;
&lt;br /&gt;
[[Single Board Computers]] - ARM devices.&lt;br /&gt;
&lt;br /&gt;
[[GPU passthrough in VMWare]] - Notes on using GPUs with Zoneminder running in a virtual machine&lt;br /&gt;
&lt;br /&gt;
[[Email]]&lt;br /&gt;
&lt;br /&gt;
==Supported== &lt;br /&gt;
&lt;br /&gt;
[[Beginner hardware]] - What cameras to start with.&lt;br /&gt;
&lt;br /&gt;
[[Supported distributions]]&lt;br /&gt;
&lt;br /&gt;
[[Hardware Compatibility List ]]&lt;br /&gt;
&lt;br /&gt;
[[MobileDevices]] - View ZM from your phone.&lt;br /&gt;
&lt;br /&gt;
==Third party==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Utilities]] - Software for ZM&lt;br /&gt;
&lt;br /&gt;
[[Infrared Leds, Cameras, DC-DC converters etc]] - Hardware&lt;br /&gt;
&lt;br /&gt;
[[External resources of information related to ZM]]&lt;br /&gt;
&lt;br /&gt;
[[Consultants]]&lt;br /&gt;
&lt;br /&gt;
==Community docs==&lt;br /&gt;
&lt;br /&gt;
[[How To]] &lt;br /&gt;
&lt;br /&gt;
[[Miscellaneous helpful hints]]&lt;br /&gt;
&lt;br /&gt;
[[Various Learnings from getting Zomeminder 1.28.1 working well on Ubuntu Server 14.04]]&lt;br /&gt;
&lt;br /&gt;
[[Dummies Guide]]&lt;br /&gt;
&lt;br /&gt;
[[MySQL]]&lt;br /&gt;
&lt;br /&gt;
[[Hardware recommendations]] - What computer hardware to use.&lt;br /&gt;
&lt;br /&gt;
[[Realtime event backup to cloud storage]]&lt;br /&gt;
&lt;br /&gt;
==CCTV Laws==&lt;br /&gt;
[[Area Specific Laws]]&lt;br /&gt;
&lt;br /&gt;
 &lt;/div&gt;</summary>
		<author><name>Kernelkurtz</name></author>
	</entry>
</feed>