Zoneminder Install
This role installs and configures ZoneMinder, an open-source video surveillance and NVR (Network Video Recorder) system, along with its dependencies including Apache web server and MariaDB database.
Zoneminder Install Role
Overview
This role installs and configures ZoneMinder, an open-source video surveillance and NVR (Network Video Recorder) system, along with its dependencies including Apache web server and MariaDB database. It handles complete setup including database creation and schema import, Apache virtual host configuration with SSL support, CGI and rewrite module enablement, filesystem permissions, and service management. The role provides a production-ready ZoneMinder installation accessible via HTTP and HTTPS.
Purpose
- NVR System: Deploy video surveillance and recording platform
- Complete Stack: Install and configure Apache, MariaDB, ZoneMinder
- Database Setup: Create database, user, and import schema
- Web Interface: Configure Apache with HTTP/HTTPS access
- API Support: Enable ZoneMinder API for automation
- Secure Configuration: SSL support and proper file permissions
- Service Management: Enable and start all required services
Requirements
- Ansible 2.9 or higher
- Target system: Debian/Ubuntu (tested on Ubuntu)
- Root or sudo privileges
- Sufficient disk space for video recordings (recommend 500GB+)
- SSL certificates (if using HTTPS)
- Database password stored in Ansible Vault
What is ZoneMinder?
ZoneMinder is an open-source video surveillance system:
Key features:
- Multi-camera support (IP cameras, USB cameras)
- Motion detection and alerting
- Continuous or triggered recording
- Web-based interface
- REST API for automation
- Event management and playback
- Live viewing and PTZ control
- Mobile app support
Use cases:
- Home security monitoring
- Business surveillance
- Wildlife monitoring
- Time-lapse recording
Compared to alternatives:
- vs Frigate: More mature, broader camera support
- vs Blue Iris: Open-source, Linux-based
- vs Shinobi: More established, better documentation
Role Variables
Optional Variables
| Variable | Default | Description |
|---|---|---|
zoneminder_install_db_name | zm | MariaDB database name |
zoneminder_install_db_user | zmuser | Database username |
zoneminder_install_db_password | {{ vault_zoneminder_db_password }} | Database password (vault) |
zoneminder_install_db_host | localhost | Database host |
zoneminder_install_ssl_cert_file | /etc/ssl/certs/zoneminder.pem | SSL certificate path |
zoneminder_install_ssl_key_file | /etc/ssl/private/zoneminder.key | SSL private key path |
zoneminder_install_config_file | /etc/zm/zm.conf | ZoneMinder config file |
Variable Details
zoneminder_install_db_password
MariaDB database password for ZoneMinder user.
IMPORTANT: Store in Ansible Vault
# In vault.yml
vault_zoneminder_db_password: "secure_random_password_here"
# In playbook
zoneminder_install_db_password: "{{ vault_zoneminder_db_password }}"
Generate secure password:
openssl rand -base64 32
zoneminder_install_ssl_cert_file / zoneminder_install_ssl_key_file
Paths to SSL certificate and private key for HTTPS.
Default paths:
- Certificate:
/etc/ssl/certs/zoneminder.pem - Key:
/etc/ssl/private/zoneminder.key
Prerequisites: SSL files must exist before running role
Generate self-signed certificate:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/private/zoneminder.key \
-out /etc/ssl/certs/zoneminder.pem \
-subj "/CN=zoneminder.local"
Production: Use Let’s Encrypt or organizational CA
Dependencies
No Ansible role dependencies, but requires:
- Sufficient disk space for recordings
- SSL certificates (for HTTPS)
- Database password in vault
Optionally used with:
- zoneminder_monitors: Add cameras after installation
- SSL certificate roles: Generate/deploy certificates
- backup roles: Backup ZoneMinder database and recordings
Example Playbook
Basic Installation
---
- name: Install ZoneMinder NVR
hosts: zoneminder
become: true
roles:
- zoneminder_install
With Custom Database
---
- name: Install ZoneMinder with Custom DB
hosts: zoneminder
become: true
vars:
zoneminder_install_db_name: zoneminder_prod
zoneminder_install_db_user: zm_admin
zoneminder_install_db_password: "{{ vault_zm_db_password }}"
roles:
- zoneminder_install
Complete Setup with Cameras
---
- name: Install and Configure ZoneMinder
hosts: zoneminder
become: true
roles:
- zoneminder_install
- zoneminder_monitors # Add cameras after install
What This Role Does
1. Install Python MySQL Packages
Installs Python packages required for Ansible MySQL modules:
python3-pymysql: Python MySQL client library
Why needed: Ansible community.mysql modules require Python MySQL client
2. Install ZoneMinder Packages
Installs required packages:
apache2: Web servermariadb-server: Database serverzoneminder: NVR application
Package manager: apt (Debian/Ubuntu)
3. Configure MariaDB
Start MariaDB:
- Enables MariaDB service
- Starts MariaDB daemon
Create database:
CREATE DATABASE zm;
Create user:
CREATE USER 'zmuser'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON zm.* TO 'zmuser'@'localhost';
FLUSH PRIVILEGES;
Import schema:
- Checks if schema already imported (looks for
Configtable) - Imports
/usr/share/zoneminder/db/zm_create.sqlif needed - Idempotent: Won’t re-import if already done
4. Configure ZoneMinder
Set file permissions:
- File:
/etc/zm/zm.conf - Owner:
root:www-data - Mode:
0640(read-only for www-data group)
Update database password:
- Sets
ZM_DB_PASS=passwordin/etc/zm/zm.conf - Uses lineinfile for idempotent updates
5. Configure Apache
Deploy ZoneMinder Apache config:
- Template:
zoneminder.conf.j2 - Location:
/etc/apache2/conf-available/zoneminder.conf - Enables ZoneMinder web application and API
Enable configuration:
a2enconf zoneminder
Enable Apache modules:
cgi: For ZoneMinder CGI scriptsrewrite: For URL rewritingssl: For HTTPS support
Deploy virtual hosts:
- HTTP:
000-default.conf(port 80) - HTTPS:
default-ssl.conf(port 443)
Enable sites:
a2ensite 000-default
a2ensite default-ssl
6. Start Services
Apache:
- Enables on boot
- Starts service
- Reloads on config changes
ZoneMinder:
- Enables on boot
- Starts service
- Restarts on config changes
Installed Components
Apache Web Server
Purpose: Serve ZoneMinder web interface
Configuration:
- HTTP on port 80
- HTTPS on port 443
- CGI enabled for ZoneMinder scripts
- Rewrite rules for clean URLs
Document root: /usr/share/zoneminder/www
MariaDB Database
Purpose: Store ZoneMinder configuration and events
Database structure:
- Monitors: Camera definitions
- Events: Recorded motion events
- Frames: Individual frame metadata
- Zones: Motion detection zones
- States: System states (recording modes)
Data location: /var/lib/mysql/zm/
ZoneMinder Application
Purpose: Video surveillance and recording
Components:
- Web interface: Camera viewing and configuration
- API: REST API for automation
- Daemons: Background processes for recording
- Storage: Event storage (
/var/cache/zoneminder/)
Default login:
- Username:
admin - Password:
admin(CHANGE IMMEDIATELY)
File Locations
| Component | Path | Purpose |
|---|---|---|
| Config file | /etc/zm/zm.conf | Main configuration |
| Database | /var/lib/mysql/zm/ | MariaDB database files |
| Events | /var/cache/zoneminder/events/ | Recorded events |
| Temp images | /var/cache/zoneminder/temp/ | Temporary frames |
| Apache config | /etc/apache2/conf-enabled/zoneminder.conf | Web server config |
| Virtual hosts | /etc/apache2/sites-enabled/ | HTTP/HTTPS sites |
| Logs | /var/log/zm/ | ZoneMinder logs |
Network Access
Web interface URLs:
- HTTP:
http://zoneminder-ip/zm - HTTPS:
https://zoneminder-ip/zm
API endpoint:
https://zoneminder-ip/zm/api/
Default ports:
- HTTP: 80
- HTTPS: 443
Firewall rules needed:
# Allow HTTP
- action: pass
destination_port: "80"
protocol: tcp
# Allow HTTPS
- action: pass
destination_port: "443"
protocol: tcp
First-Time Setup
1. Access Web Interface
https://zoneminder-ip/zm
2. Change Default Password
Critical security step:
- Log in:
admin/admin - Options → Users → admin
- Change password
- Save
3. Configure Storage
- Options → Storage
- Verify default storage location:
/var/cache/zoneminder/events - Add additional storage if needed
4. Configure Paths
- Options → Paths
- Verify FFmpeg path:
/usr/bin/ffmpeg - Save
5. Add Cameras
Use zoneminder_monitors role or add manually via UI:
- Add Monitor
- Enter camera details (name, RTSP URL, resolution)
- Save
Database Schema
Key tables:
| Table | Purpose |
|---|---|
Monitors | Camera definitions |
Events | Recorded events |
Frames | Frame metadata |
Zones | Motion detection zones |
States | System recording states |
Config | System configuration |
Users | User accounts |
Schema file: /usr/share/zoneminder/db/zm_create.sql
Storage Considerations
Disk space requirements:
Per camera estimation:
- Resolution: 1920x1080 (1080p)
- FPS: 5
- Recording: Motion only (50% of time)
- Retention: 30 days
Calculation:
Storage = Resolution × FPS × Recording% × Retention × Cameras
≈ 5GB/day × 30 days × 1 camera
≈ 150GB per camera
For 4 cameras: ~600GB minimum
Recommendations:
- 1TB disk for 4-5 cameras
- 2TB disk for 8-10 cameras
- Use dedicated disk for
/var/cache/zoneminder/
Security Considerations
- Change Default Password: IMMEDIATELY after install
- SSL Required: Use HTTPS in production
- Database Password: Store in Ansible Vault
- File Permissions: Config file is 0640 (not world-readable)
- Network Access: Restrict web interface to trusted networks
- Camera Credentials: Use strong passwords for camera RTSP
- Regular Updates: Keep ZoneMinder and dependencies updated
- Backup Database: Regular backups of MariaDB
Tags
This role does not define any tags. Use playbook-level tags if needed:
- hosts: zoneminder
roles:
- zoneminder_install
tags:
- zoneminder
- nvr
- surveillance
- install
Notes
- Role runs on target system (not localhost)
become: truerequired for package installation and config- SSL certificates must exist before running role
- Database schema imported automatically on first run
- Services enabled and started automatically
- Compatible with Debian/Ubuntu systems
- Idempotent: Safe to run multiple times
Troubleshooting
ZoneMinder web interface shows database error
Symptom: “Can’t connect to database” error
Check database:
# MariaDB running?
systemctl status mariadb
# Database exists?
mysql -u root -e "SHOW DATABASES LIKE 'zm';"
# User has permissions?
mysql -u root -e "SHOW GRANTS FOR 'zmuser'@'localhost';"
Test connection:
mysql -u zmuser -p zm
# Enter password from vault
# Should connect successfully
Fix:
# Restart MariaDB
systemctl restart mariadb
# Re-import schema if needed
mysql -u root zm < /usr/share/zoneminder/db/zm_create.sql
Apache shows 403 Forbidden
Symptom: Can’t access /zm URL
Check Apache config:
# ZoneMinder config enabled?
ls -la /etc/apache2/conf-enabled/ | grep zoneminder
# Apache config valid?
apachectl configtest
# Should show: "Syntax OK"
Check permissions:
# Web directory readable?
ls -la /usr/share/zoneminder/www/
# Should be owned by root:root, readable by all
Fix:
# Enable config
a2enconf zoneminder
# Restart Apache
systemctl restart apache2
ZoneMinder service won’t start
Check service status:
systemctl status zoneminder
journalctl -u zoneminder -n 50
Common issues:
- Database not accessible
- Config file syntax error
- Disk space full
Check disk space:
df -h /var/cache/zoneminder/
Check config syntax:
# Check zm.conf
cat /etc/zm/zm.conf | grep -v "^#" | grep -v "^$"
Fix:
# Restart service
systemctl restart zoneminder
# Check logs
tail -f /var/log/zm/zmpkg.log
SSL certificate errors
Symptom: HTTPS not working, certificate errors
Check certificates exist:
ls -la /etc/ssl/certs/zoneminder.pem
ls -la /etc/ssl/private/zoneminder.key
Generate self-signed:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/private/zoneminder.key \
-out /etc/ssl/certs/zoneminder.pem \
-subj "/CN=$(hostname -f)"
# Restart Apache
systemctl restart apache2
High CPU usage
Cause: Too many cameras or high FPS
Solutions:
- Reduce FPS: Lower capture rate (5 FPS sufficient for most)
- Use motion detection: Don’t record continuously
- Reduce resolution: Use 720p instead of 1080p
- Hardware acceleration: Enable GPU decoding if available
Testing the Role
Verify Installation
# Check packages installed
dpkg -l | grep -E 'apache2|mariadb-server|zoneminder'
# Check services running
systemctl status apache2
systemctl status mariadb
systemctl status zoneminder
Test Database
# Connect to database
mysql -u zmuser -p zm
# List tables
SHOW TABLES;
# Check Config table exists
SELECT COUNT(*) FROM Config;
Test Web Access
# HTTP
curl -I http://localhost/zm
# Should return 200 OK
# HTTPS
curl -Ik https://localhost/zm
# Should return 200 OK
Test API
# Get API version
curl -k https://localhost/zm/api/host/getVersion.json
# Should return JSON with version info
Post-Installation Steps
- Change admin password (critical)
- Configure storage locations
- Set up email notifications (optional)
- Add cameras (via
zoneminder_monitorsrole or UI) - Configure motion detection zones
- Set recording schedules
- Configure retention policies
- Set up backups
Performance Tuning
For Low-Power Systems
# In ZoneMinder web interface:
# Options → System
- MAX_FPS: 5
- ALARM_FRAME_COUNT: 3
# Per monitor:
- Reduce resolution (720p)
- Lower FPS (5)
- Use passthrough recording (no re-encoding)
For High-Performance Systems
# Enable more concurrent captures
# Options → System
- MAX_CAPTURE: 8
- MAX_ANALYSIS: 4
# Use GPU acceleration
# Install: ffmpeg with NVENC/VAAPI support
Best Practices
- Change default password immediately: Default is
admin/admin - Use SSL in production: HTTP transmits credentials in plain text
- Regular backups: Backup MariaDB database and event recordings
- Monitor disk space: Set up alerts for low space
- Limit retention: Don’t keep events forever
- Use motion detection: Continuous recording uses massive space
- Secure camera credentials: Use strong passwords for RTSP
- Network segmentation: Put cameras on isolated VLAN
- Regular updates: Keep ZoneMinder updated for security
- Document configuration: Keep inventory of cameras and settings
Related Roles
This role is often used with:
- zoneminder_monitors: Add cameras after installation
- SSL certificate roles: Deploy/manage SSL certificates
- backup_zoneminder: Backup database and recordings
- firewall roles: Configure access to ZoneMinder
Backup Strategy
What to backup:
- MariaDB database:
/var/lib/mysql/zm/ - Config file:
/etc/zm/zm.conf - Event recordings:
/var/cache/zoneminder/events/ - Apache config:
/etc/apache2/conf-available/zoneminder.conf
Backup script example:
#!/bin/bash
# Backup ZoneMinder database
mysqldump -u root zm > zm_backup_$(date +%Y%m%d).sql
# Backup config
cp /etc/zm/zm.conf /backup/zm.conf
# Backup recent events (last 7 days)
find /var/cache/zoneminder/events/ -mtime -7 -type f -exec cp --parents {} /backup/ \;
License
MIT
Author
Created for homelab infrastructure management.