How to manually clean up Zoneminder events

Written by - 2 comments

Published on December 14th 2018 - last updated on September 4th 2019 - Listed in Zoneminder Surveillance Linux

Zoneminder is a great tool to build a surveillance system, combining all kinds of ip cameras in one dashboard and use it to manage recordings. 

But sometimes Zoneminder can be a bit of a pain, especially when the disk is getting filled. With the high resolutions of todays IP cameras this can happen pretty quickly. Although Zoneminder has an internal "filter" to automatically purge old events when the disk threshold hits a certain limit.

Zoneminder Purge Filter

However this filter only works if there's actually still some space left available. The filter searches the oldest N events in the database, deleting them from the database and also on the filesystem. But when there's no disk space available at all, the database is likely to be frozen/unavailable. Ergo no clean up anymore. And in this situation you're stuck with a non-working Zoneminder.

This happened to me twice already on my Zoneminder installation (side note: I have to admit my current disk size of 120GB dedicated for Zoneminder is rather small) so I built a clean up script and this is what this post is about.

We don't want to delete the archived events!

When you archive a footage, this usually means you want to keep it. The cleanup script needs to respect that. But it needs to know about the archived events first. This can be done by getting the relevant information from the database:

root@zoneminder:~# mysql -N -b -r -e "select Id from zm.Events where Archived = 1;"
| 135933 |
| 136590 |
| 154831 |
| 160832 |
| 162647 |
| 162649 |
| 167562 |

Find all events except the archived ones

We can use the find command and the ID's from step one to find all events except the archived ones:

root@zoneminder:~# find /var/cache/zoneminder/events/ -mindepth 2 -type l ! -name ".135933" ! -name ".136590" ! -name ".154831" ! -name ".160832" ! -name ".162647" ! -name ".162649" ! -name ".167562" -exec ls -la {} + > /tmp/xxx

find will now look in the path /var/cache/zoneminder/events/ for symlinks (type l), except for the given names (excluded with exclamation mark). The output of the full path and other information will be saved in /tmp/xxx.

The output file /tmp/xxx will now look something like that:

root@zoneminder:~# tail /tmp/xxx
lrwxrwxrwx 1 www-data www-data 8 Dec 14 06:45 /var/cache/zoneminder/events/5/18/12/14/.448512 -> 06/45/12
lrwxrwxrwx 1 www-data www-data 8 Dec 14 06:51 /var/cache/zoneminder/events/5/18/12/14/.448517 -> 06/51/29
lrwxrwxrwx 1 www-data www-data 8 Dec 14 06:51 /var/cache/zoneminder/events/5/18/12/14/.448518 -> 06/51/34
lrwxrwxrwx 1 www-data www-data 8 Dec 14 07:02 /var/cache/zoneminder/events/5/18/12/14/.448533 -> 07/02/28
lrwxrwxrwx 1 www-data www-data 8 Dec 14 07:44 /var/cache/zoneminder/events/5/18/12/14/.448546 -> 07/44/56
lrwxrwxrwx 1 www-data www-data 8 Dec 14 07:47 /var/cache/zoneminder/events/5/18/12/14/.448548 -> 07/47/09
lrwxrwxrwx 1 www-data www-data 8 Dec 14 08:22 /var/cache/zoneminder/events/5/18/12/14/.448551 -> 08/22/17
lrwxrwxrwx 1 www-data www-data 8 Dec 14 08:26 /var/cache/zoneminder/events/5/18/12/14/.448552 -> 08/26/13
lrwxrwxrwx 1 www-data www-data 8 Dec 14 08:27 /var/cache/zoneminder/events/5/18/12/14/.448555 -> 08/27/30
lrwxrwxrwx 1 www-data www-data 8 Dec 14 08:28 /var/cache/zoneminder/events/5/18/12/14/.448557 -> 08/28/19

Get the event id and real path

Each path in /tmp/xxx contains two important information: The event id and the real path.

/var/cache/zoneminder/events/5/18/12/14/.448512 -> 06/45/12

In this case .448512 is the symlink of the event pointing to the subfolders 06/45/12.
The name of the symlink also contains the event id (448512).
By removing the symlink and adding the subfolders into the path, we get the real path where the footage is stored:


Delete the footage and the info in the database

Now that the real path is known, it can be deleted:

root@zoneminder:~# rm -rf /var/cache/zoneminder/events/5/18/12/14/06/45/12

We should also delete the symlink:

root@zoneminder:~# rm -f /var/cache/zoneminder/events/5/18/12/14/.448512

And now that there's some disk space available again, the MySQL database should accept writes again. Therefore we can delete this event (448512) from the relevant tables:

mysql> DELETE FROM zm.Events where Id = 448512;
mysql> DELETE FROM zm.Frames where EventId = 448512;
mysql> DELETE FROM zm.Stats where EventId = 448512;

Automate it with a script

As I mentioned at the beginning, I wrote a script to automate these tasks. It's called and you can download it.

Using the script is very simple.

1) Download it

# wget

2) Give execute permissions:

# chmod 755

3) Open the script with an editor and adjust the user variables:

# User variables
olderthan=2 # Defines the minimum age in days of the events to be deleted
zmcache=/var/cache/zoneminder/events # Defines the path where zm stores events
mysqlhost=localhost # Defines the MySQL host for the zm database
mysqldb=zm # Defines the MySQL database name used by zm
mysqluser=zmuser # Defines a MySQL user to connect to the database
mysqlpass=secret # Defines the password for the MySQL user

Run the final script

root@zoneminder:~# ./
Deleting 447900
Deleting 447901
Deleting 447902
Deleting 447903
Deleting 447904
Deleting 447905
Deleting 447906
Deleting 447907
Deleting 447908
Deleting 447909
Deleting 447911
Deleting 447912
Deleting 447913
Deleting 447914
Deleting 447915

The script is also available on Github.

Add a comment

Show form to leave a comment

Comments (newest first)

ck from Switzerland wrote on Sep 4th, 2019:

Thanks for that hint, Elliot! As my Zoneminder is currently still running 1.29, I cannot very this. Could you however open an "issue" in the public GitHub repo: When I have time, I will look at it.

Elliot from wrote on Sep 4th, 2019:

I took a look at results of just using the commands, and it appears that the version of zoneminder I'm using (v1.32.3) doesn't use symbolic links, instead the events point directly at the associated directory (e.g. event 3270 = /var/cache/zoneminder/events/1/2019-08-28/3270/
and so the "find" line doesn't work as intended.