Docker
The ZoneMinder project publishes Docker images built from the same zmrepo packages as a normal install. They run on amd64 and arm64. The compose setup, the full list of settings and the issue tracker are in the zmdockerfiles repo. This page covers what you need to get running.
Quick start
You need Docker with the compose plugin. The compose file runs ZoneMinder and MariaDB in separate containers.
git clone https://github.com/ZoneMinder/zmdockerfiles.git
cd zmdockerfiles
cp .env.example .env # set ZM_DB_PASS, MARIADB_ROOT_PASSWORD and TZ
docker compose up -d
Open http://localhost:8080/zm. On first start the container creates the database schema. On
later starts it runs zmupdate.pl to upgrade the schema when the image has a newer ZoneMinder.
Then finish ZoneMinder’s own first-run setup, as on any other install:
1.38 starts with authentication off. Turn on
OPT_USE_AUTHunder Options > Authentication, log in asadmin/admin, and change that password under Options > Users.1.39 and later (the
devtags) start with authentication on. The first page asks you to set theadminpassword or turn authentication off. See First Run.Answer the usage statistics prompt. Until you do, the web UI and image URLs redirect to it.
The container writes only the database settings to /etc/zm/conf.d/03-docker.conf. Everything
else is set in the web UI.
docker compose ps shows whether the container is healthy. It reports unhealthy when the web
server stops answering, the zmdc.pl daemon is gone, or the database doesn’t answer.
Storage and ports
/var/cache/zoneminderEvents, images and the cache. This is the volume that grows, so put it on a disk with room.
/configgo2rtc.yaml, the event server config and its certificates./var/log/zmZoneMinder’s logs. The main ones also go to
docker compose logs./dev/shmA tmpfs for each monitor’s frame buffer, 1 GiB by default. Raise
ZM_SHM_SIZEin.envfor many or high resolution monitors.
The web UI is on port 80 in the container, published as 8080. WebRTC uses 8555 (TCP and UDP), and the event server uses 9000.
If your volumes are bind mounts owned by a user on the host, set PUID and PGID to that
user’s IDs. The container runs ZoneMinder, Apache and go2rtc as that user.
Live view with go2rtc
The image includes go2rtc for WebRTC and MSE live view, served under ZoneMinder’s own address at
/zm/go2rtc/. To use it:
Set
ZM_PUBLIC_URLin.envto the address you open ZoneMinder at, using a hostname, for examplehttp://zm.lan:8080.Set Options > System >
GO2RTC_PATHto that address plus/zm/go2rtc, sohttp://zm.lan:8080/zm/go2rtc.Turn on go2rtc for each monitor.
Use a hostname rather than an IP address. ZoneMinder registers streams with go2rtc from inside the
container, and with an IP address those calls arrive through the host and are refused. Behind an
https reverse proxy, also set GO2RTC_API_ALLOW to the proxy’s address.
Warning
go2rtc live streams are not protected by ZoneMinder’s login. Anyone who can reach the web port can watch any monitor with go2rtc turned on. Keep the port on a network you trust, or put an authenticating reverse proxy or a VPN in front of it. Monitors without go2rtc use ZoneMinder’s own streaming, which does check the login.
The container refuses the rest of go2rtc’s API from outside, including its config and its web page, so the exposure is limited to watching streams.
Hardware decoding
Uncomment the devices: block in docker-compose.yaml to pass /dev/dri into the
container, then set DecoderHWAccelName and DecoderHWAccelDevice on the monitor. The image ships
the Mesa VA-API drivers, plus Intel’s media driver on amd64. To see what the container can use:
docker compose exec zoneminder vainfo
The container adds its user to the groups that own any devices you pass through, so no
privileged mode is needed.
Event server and object detection
The event server reads ZoneMinder’s shared memory, so it runs inside the ZoneMinder container
(the -es images). Object detection runs in a separate pyzm-serve container, which the
event server’s hook sends frames to over HTTP.
# push notifications, WebSocket (port 9000) and MQTT, no detection
ZM_TAG=latest-es docker compose up -d
# plus object detection
ZM_TAG=latest-es ZMES_USE_HOOKS=yes docker compose --profile ml up -d
On first start the container copies the event server config to /config/es/. Edit the files
there and restart. Once ZoneMinder’s authentication is on, put your login in
/config/es/secrets.yml as ZM_USER and ZM_PASSWORD.
pyzm-serve also has a cuda tag that runs detection on an NVIDIA GPU (amd64 only). It
needs the NVIDIA Container Toolkit on the host. See the zmdockerfiles README for the GPU setup,
and the event server documentation for what the hook can do.
Upgrading from the old images
The old zoneminderhq/zoneminder images, last published in 2021, ran MySQL inside the
ZoneMinder container. To move to the new setup, dump the database from the old container and
load it into the new one:
docker exec old-zoneminder mysqldump --single-transaction --routines --triggers zm > zm.sql
docker compose up -d --wait db
docker compose exec -T db sh -c 'exec mariadb -uroot -p"$MARIADB_ROOT_PASSWORD" zm' < zm.sql
docker compose up -d
old-zoneminder is the old container’s name. zmupdate.pl upgrades the schema on first
start. Events are in the same place (/var/cache/zoneminder/events), so point the events
volume at the old directory or bind mount it.
Verifying images
CI signs every image with cosign and attaches an SBOM listing every package inside it. To check that an image came from the zmdockerfiles master branch:
cosign verify zoneminder/zoneminder:latest \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github\.com/ZoneMinder/zmdockerfiles/\.github/workflows/(images|pyzm-cuda)\.yml@refs/heads/master$'
Getting help
Ask questions on the forums. Report bugs in the images at
zmdockerfiles issues, with your compose
file (passwords removed) and the output of docker compose logs zoneminder.