Zoneminder as an NVR

I posted about ShinobiCCTV awhile back and I still think that it is still a great FOSS NVR. However, ShinobiCCTV not for me. I have been testing Shinobi instance both on a VM and Docker container, and I could not get it to work properly. There were several issues that I was having with Shinobi and these issues were constant whether I use a VM or Docker container.

These were the issues that I was having during when I was piloting Shinobi. I simply don’t have enough time to sit down and keep tweaking or troubleshooting it. The list below are the issues that I encountered on both VM and Docker container instances.

  • The primary issue that I was having was my cameras randomly disconnect from Shinobi and would not reconnect
  • The dashboard 70% of the time the cameras show a blank screen
  • There was 60% – 70% chance that the cameras were connected but not recording despite the active motion
  • Searching for specific motion in timeline is very slow
  • Viewing the list of recorded video, the load time was very slow
  • A lot of smearing even after following the recommended settings by the dev and community

I tested Zoneminder in the past, but quickly abandoned it due to high CPU usage. I am not sure when the H.264 passthrough has been added to ZM, but with this feature enabled, it reduces the CPU utilization by ~80%. I am not an expert or even a power user in regards to NVR system, I just want to replace my Reolink desktop app that I am currently using. To get the H.264 pass-through working, the camera must support it to begin with.

For the Timezone selection, pick one from here To locate the php.ini file, enter this command:

php -i | grep "Loaded Configuration File"

CentOS 8

To install Zoneminder on CentOS 8 (yeah I know – hopefully there is an easy migration to Rocky)

dnf install -y mariadb mariadb-server mod_ssl php-mbstring php php-mysql httpd wget
dnf install -y epel-release
dnf install --nogpgcheck
dnf install --nogpgcheck
dnf config-manager --enable PowerTools
dnf update -y
dnf search zoneminder

# Disable SELinux
setenforce 0
sed -i --follow-symlinks 's/^SELINUX=.*/SELINUX=disabled/g' /etc/sysconfig/selinux

# Configure mariaDB
systemctl enable --now mariadb
mysql < /usr/share/zoneminder/db/zm_create.sql
mysql -e "GRANT ALL ON zm.* to 'zmuser'@localhost identified by 'zmpass';"

# Configure Apache
cd /etc/httpd/conf.d
ln -s /etc/zm/www/zoneminder.httpd.conf
sed -i --follow-symlinks 's/;date.timezone =/date.timezone = America\/Los_Angeles/g' /etc/php.ini

# Enable httpd and zoneminder
systemctl enable --now httpd
systemctl enable --now zoneminder

# Allow port 80/tcp and 443/tcp inbound
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --zone=public --add-port=443/tcp --permanent
firewall-cmd --reload

# Restart the host machine

OpenSUSE 15.2

There is an issue with openSUSE that I could not figure out how to fix at this point. I can get ZoneMinder installed, but it keeps barking that the timezone does not match between the ZoneMinder and the system. I have updated the php.ini, ZoneMinder and mariadb, but no luck. Anyways, I am leaving it here just in case that in the newer version of ZoneMinder for openSUSE gets updated.

# Need to add the repository to install the
zypper addrepo

# Add the repository to download ZoneMinder
zypper addrepo

# Refresh zypper
zypper refresh

# Install ZoneMinder
zypper install ZoneMinder

# Configure mariaDB 
systemctl enable --now mariadb
mysql < /usr/share/zoneminder/db/zm_create.sql
mysql -e "GRANT ALL ON zm.* to 'zm_admin'@localhost identified by 'zmpass';"

# Fix the apache timezone from UTC to desire timezone
sed -i --follow-symlinks 's/UTC/America\/New_York/g' /etc/php7/apache2/php.ini

# Enable and start the apache and zoneminder
systemctl enable --now apache2
systemctl enable --now zm


Probably the easiest and somewhat universal is the Docker installation. Make sure to install Docker and Docker Compose. Below is the docker-compose file from ZoneMinder. I changed the networking part since my network is segmented by zone/vlan. Here is the original docker-compose file.

version: '3.1'
    container_name: zoneminder
    image: zoneminderhq/zoneminder:latest-ubuntu18.04
    restart: unless-stopped
      - 8080:80
    privileged: true
    shm_size: 512M
      - TZ=America/New_York
      - /mnt/pool_surv/zoneminder/events:/var/cache/zoneminder/events
      - /mnt/pool_surv/zoneminder/iamges:/var/cache/zoneminder/images
      - /mnt/pool_surv/zoneminder/logs:/var/log/zoneminder
      - /mnt/disk13/appdata/zoneminder/mysql:/var/lib/mysql

    driver: macvlan
      parent: eth0.5
        - subnet:

Before running the docker-compose up -d, make sure that you have the permission and owner fixed for the volumes. In my case, the volume specified in docker-compose file above. Otherwise, none of the volumes will get created.

chown -R :users /mnt/pool_surv/zoneminder
chmod -R 774 /mnt/pool_surv/zoneminder
chown -R :users /mnt/disk13/appdata/zoneminder
chmod -R 774 /mnt/disk13/appdata/zoneminder

Currently the www-data does not have access to the volumes. To fix this, we need to bash-into zoneminder container and chown the volumes.

docker-compose exec zoneminder chown -R www-data:www-data /var/cache/zoneminder/events
docker-compose exec zoneminder chown -R www-data:www-data /var/cache/zoneminder/images
docker-compose exec zoneminder chown -R www-data:www-data /var/log/zonemider

Make sure that firewalld is allowing inbound for port 8080/tcp if you are using “Bridge”. I am using Debian with this Docker installation and macvlan, so I don’t really need to open a port

firewall-cmd --zone=public --add-port=8080/tcp --permanent
firewall-cmd --reload

To fine tune the monitors, I found this wiki from ZoneMinder. Here is a great instruction how to fine tune each monitor’s zone.

I think this is it for now. My follow-up is how I added the cameras/monitors. Cheers!

Notify of

Inline Feedbacks
View all comments
Would love your thoughts, please comment.x