Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Troubleshooting

The dashboard loads but every request fails

CORS. The browser reports it in its own console and the API logs nothing unusual, which is what makes it hard to spot.

Set the dashboard’s origin:

APP_SERVER__ALLOWED_ORIGINS=https://zm.example.com

zm-api logs its effective origin list at startup and warns when it fell back to the localhost-only default — check there first:

journalctl -u zm-api | grep -i cors

A bare * is not accepted. The API sends credentials and the CORS spec forbids that pairing. Not needed at all when the dashboard and API share a hostname — see Serving a dashboard.

permission denied opening stream socket

The service user is not in ZoneMinder’s ZM_STREAM_SOCKET_GROUP. The per-monitor sockets are mode 0660.

sudo systemctl edit zm-api
[Service]
SupplementaryGroups=<value of ZM_STREAM_SOCKET_GROUP>

Do not add it to the shipped unit file. A group that does not exist makes systemd refuse to start the service entirely (status=216/GROUP).

The service starts but features are missing

Migrations probably failed. zm-api only warns on migration failure at startup, so the service looks healthy.

journalctl -u zm-api | grep -i migrat
zm-api-db status -u mysql://zmuser:zmpass@localhost/zm

See Upgrading.

The unit won’t start at all

systemctl status zm-api
journalctl -u zm-api -n 100 --no-pager

Common causes:

SymptomCause
status=216/GROUPA SupplementaryGroups= entry names a group that doesn’t exist
Cannot connect to databaseAPP_DB__* set to something wrong, overriding the working zm.conf fallback
Missing JWT keys/var/lib/zm-api/keys not provisioned — run /usr/share/zm-api/setup-instance.sh
Port in useSomething else on 8080; set APP_SERVER__PORT

Streams stall behind a reverse proxy

proxy_buffering off; on the live and playback routes, and the Upgrade/Connection headers with proxy_http_version 1.1 for the WebRTC signalling WebSocket. Full config in Serving a dashboard.

WebRTC connects then shows nothing

Usually NAT. STUN cannot traverse symmetric NAT; configure a TURN server. Note the media ports are OS-assigned ephemeral UDP with no configurable range. See Live streaming.

Getting 429s

Rate limiting. The auth endpoints allow roughly one request per two seconds with a burst of 10; prod also enables a global per-IP limiter.

Behind a proxy, set APP_SERVER__MIDDLEWARE__TRUST_PROXY_HEADERS=true — without it every client shares the proxy’s single bucket.

Turning up the logs

sudo systemctl edit zm-api      # [Service] Environment=RUST_LOG=debug
# or per-module:
#   RUST_LOG=info,zm-api::streaming=debug
#   RUST_LOG=info,zm-api::daemon=debug
sudo systemctl restart zm-api
journalctl -u zm-api -f