Upgraded. pkg upgrade zoneminder made for a long night but in the end, I got it fixed.

If you've read the last post about ZoneMinder on FreeBSD, you've seen all the details on how to set up the entire suite. Here I'll go over what needs to be updated to support the newly polished ZoneMinder 1.32.3 on FreeBSD 11.

Start by kicking off an upgrade of the ZM package with pkg upgrade zoneminder. This will upgrade your PHP from 5.6 to 7.2. If you have any # in your php.ini file, php will fail to start. Make sure you only comment out lines using ;.

Next, stop the zoneminder serivce with service zoneminder stop. Once it's stopped you'll need to run the zmupdate.pl command to upgrade the database. This was the first snag. The upgrade was successful all the way up to the end where it errored out.

  • First, I had to modify the database user's permissions:
    GRANT SUPER ON *.* TO <zmuser>@"<zmIPaddress>";
  • Second, I reran the zmupdate.pl command, this time successfully completing the upgrade.
  • Third, I removed the permissions:
    REVOKE SUPER ON *.* FROM <zmuser>@"<zmIPaddress>";
  • Finally, I restarted the zm service:
    service zoneminder restart

ZoneMinder's code

If you have a reverse proxy in front of yours like I do, you'll have to make some changes to the way ZM identifies the proxy protocol and port. If you don't have a reverse proxy, you can skip this part as the ZoneMinder code will correctly identify your port and protocol.

Note: These changes were found in the ZoneMinder forums and on their GitHub page. This is a resurfaced bug from a few years ago.

All modifications are made in the includes/Server.php file:

  • About 65 lines down, replace this
    return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? 'https' : 'http';
    with this
    return  (
              ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' )
              ( isset($_SERVER['HTTP_X_FORWARDED_PROTO']) and ( $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) )
            ) ? 'https' : 'http';
  • At the ~81 line mark of the same includes/Settings.php file, add the following above return $_SERVER['SERVER_PORT'];:
    if ( isset($_SERVER['HTTP_X_FORWARDED_PORT']) ) {

I noticed errors in the ZM logs complaining that it could not find /usr/local/bin/pear. That was resolved with a simple pkg install php72-pear.

Nginx updates

Above, you may have noticed we introduced two new headers into ZM's code:


Here's my new proxy config defining these headers:

server {
    server_name zm.example.net;
    include enforce_https;          # Redirects to HTTPS

server {
    include      ssl;               # External SSL config
    server_name  zm.example.net;

    access_log   /var/log/nginx/access.zm.log main
    error_log    /var/log/nginx/error.http.zm.log;

    location / {
        proxy_pass          http://<zoneminder_backend_ip>;
        proxy_set_header    Host $host;
        proxy_set_header    X-Forwarded-Host $host;
        proxy_set_header    X-Forwarded-Proto $scheme;
        proxy_set_header    X-Forwarded-Port 443;

For the downstream Nginx configuration (the one in the ZM jail), I had to add two distinct rewrites. One for the cache and one for the api.

After some searching and reading of the ZoneMinder forums, a few posters were gracious enough to suggest using nginx's rewrites to mimic Apache's config. I wanted to link to the comments directly and give credit where it's due, but I honestly can't find that post again. I'll dig through my history later, and if I come across it, I'll update this post.

Below is my new config with these rewrite statements:

user www;
worker_processes  1;

error_log  /var/log/nginx/error.log;

events {
    worker_connections  1024;
http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;
    gzip  on;
    server_tokens off;

    server {
        listen       80;
        server_name  __;

	root /usr/local/www/zoneminder;
	try_files $uri $uri/ /index.php$is_args$args;
	index index.php;
	location /cache {
		alias "/var/cache/zoneminder";
	location /zm {
		rewrite ^/zm/(.+)$ /$1 last;

	location / {
		location = /cgi-bin/nph-zms {
			gzip off;
			include fastcgi_params;
			fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
			fastcgi_pass    unix:/var/run/fcgiwrap/fcgiwrap.sock;
		location ~ \.php$ {
				include fastcgi_params;
				fastcgi_param   SCRIPT_FILENAME  $document_root$fastcgi_script_name;
				fastcgi_pass    unix:/var/run/php-fpm.socket;
		location /api {
					rewrite ^/api/(.+)$ /api/app/webroot/index.php?p=$1 last;

And that's it! a quick service restart _____ for each service and I was back up and running!

Other changes

I made a few other changes that might have added to the performance of ZoneMinder, but are not crucial to its operation:

  • Updated /etc/rc.conf.local
    Change fcgiwrap_flags="-c 4" to fcgiwrap_flags="-c 10"
  • Updated /usr/local/etc/php.ini
    Change memory_limit = 128M to memory_limit = 512M

Oh, yeah, SET YOUR TIMEZONE! I'm embarrassed to say how long I spent working on an issue where the images wouldn't load in the browser. All it took was a simple tzsetup and images loaded!