1 { config, lib, pkgs, ... }:
4 cfg = config.services.zoneminder;
5 fpm = config.services.phpfpm.pools.zoneminder;
12 nginx = config.services.nginx.group;
16 useNginx = cfg.webserver == "nginx";
18 defaultDir = "/var/lib/${user}";
19 home = if useCustomDir then cfg.storageDir else defaultDir;
21 useCustomDir = cfg.storageDir != null;
25 dirs = dirList: [ dirName ] ++ map (e: "${dirName}/${e}") dirList;
27 cacheDirs = [ "swap" ];
28 libDirs = [ "events" "exports" "images" "sounds" ];
31 lib.concatStringsSep "\n" (map (e:
32 "ZM_DIR_${lib.toUpper e}=${baseDir}/${e}"
35 defaultsFile = pkgs.writeText "60-defaults.conf" ''
36 # 01-system-paths.conf
38 ZM_PATH_ARP=${lib.getBin pkgs.nettools}/bin/arp
39 ZM_PATH_LOGS=/var/log/${dirName}
41 ZM_PATH_SOCKS=/run/${dirName}
42 ZM_PATH_SWAP=/var/cache/${dirName}/swap
50 ZM_DB_HOST=${cfg.database.host}
51 ZM_DB_NAME=${cfg.database.name}
52 ZM_DB_USER=${cfg.database.username}
53 ZM_DB_PASS=${cfg.database.password}
60 configFile = pkgs.writeText "80-nixos.conf" ''
61 # You can override defaults here
68 services.zoneminder = with lib; {
69 enable = lib.mkEnableOption ''
72 If you intend to run the database locally, you should set
73 `config.services.zoneminder.database.createLocally` to true. Otherwise,
74 when set to `false` (the default), you will have to create the database
75 and database user as well as populate the database yourself.
76 Additionally, you will need to run `zmupdate.pl` yourself when
77 upgrading to a newer version
80 webserver = mkOption {
81 type = types.enum [ "nginx" "none" ];
84 The webserver to configure for the PHP frontend.
86 Set it to `none` if you want to configure it yourself. PRs are welcome
87 for support for other web servers.
93 default = "localhost";
95 The hostname on which to listen.
103 The port on which to listen.
107 openFirewall = mkOption {
111 Open the firewall port(s).
116 createLocally = mkOption {
120 Create the database and database user locally.
126 default = "localhost";
128 Hostname hosting the database.
140 username = mkOption {
144 Username for accessing the database.
148 password = mkOption {
152 Username for accessing the database.
153 Not used if `createLocally` is set.
162 Set this to the number of cameras you expect to support.
166 storageDir = mkOption {
167 type = types.nullOr types.str;
169 example = "/storage/tank";
171 ZoneMinder can generate quite a lot of data, so in case you don't want
172 to use the default ${defaultDir}, you can override the path here.
176 extraConfig = mkOption {
180 Additional configuration added verbatim to the configuration file.
186 config = lib.mkIf cfg.enable {
189 { assertion = cfg.database.createLocally -> cfg.database.username == user;
190 message = "services.zoneminder.database.username must be set to ${user} if services.zoneminder.database.createLocally is set true";
195 "zoneminder/60-defaults.conf".source = defaultsFile;
196 "zoneminder/80-nixos.conf".source = configFile;
199 networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
205 fcgiwrap.instances.zoneminder = lib.mkIf useNginx {
206 process.prefork = cfg.cameras;
208 process.group = group;
209 socket = { inherit (config.services.nginx) user group; };
212 mysql = lib.mkIf cfg.database.createLocally {
214 package = lib.mkDefault pkgs.mariadb;
215 ensureDatabases = [ cfg.database.name ];
217 name = cfg.database.username;
218 ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
222 nginx = lib.mkIf useNginx {
227 root = "${pkg}/share/zoneminder/www";
228 listen = [ { addr = "0.0.0.0"; inherit (cfg) port; } ];
233 try_files $uri $uri/ /index.php?$args =404;
235 rewrite ^/skins/.*/css/fonts/(.*)$ /fonts/$1 permanent;
237 location ~ /api/(css|img|ico) {
238 rewrite ^/api(.+)$ /api/app/webroot/$1 break;
239 try_files $uri $uri/ =404;
242 location ~ \.(gif|ico|jpg|jpeg|png)$ {
248 rewrite ^/api(.+)$ /api/app/webroot/index.php?p=$1 last;
254 include ${config.services.nginx.package}/conf/fastcgi_params;
255 fastcgi_param SCRIPT_FILENAME ${pkg}/libexec/zoneminder/${zms};
256 fastcgi_param HTTP_PROXY "";
257 fastcgi_intercept_errors on;
259 fastcgi_pass unix:${config.services.fcgiwrap.instances.zoneminder.socket.address};
263 alias /var/cache/${dirName}/;
268 fastcgi_index index.php;
270 include ${config.services.nginx.package}/conf/fastcgi_params;
271 fastcgi_param SCRIPT_FILENAME $request_filename;
272 fastcgi_param HTTP_PROXY "";
274 fastcgi_pass unix:${fpm.socket};
282 phpfpm = lib.mkIf useNginx {
285 phpPackage = pkgs.php.withExtensions (
286 { enabled, all }: enabled ++ [ all.apcu all.sysvsem ]);
288 date.timezone = "${config.time.timeZone}"
290 settings = lib.mapAttrs (name: lib.mkDefault) {
291 "listen.owner" = user;
292 "listen.group" = group;
293 "listen.mode" = "0660";
296 "pm.start_servers" = 1;
297 "pm.min_spare_servers" = 1;
298 "pm.max_spare_servers" = 2;
299 "pm.max_requests" = 500;
300 "pm.max_children" = 5;
301 "pm.status_path" = "/$pool-status";
302 "ping.path" = "/$pool-ping";
309 zoneminder = with pkgs; {
310 inherit (zoneminder.meta) description;
311 documentation = [ "https://zoneminder.readthedocs.org/en/latest/" ];
317 after = [ "nginx.service" ] ++ lib.optional cfg.database.createLocally "mysql.service";
318 wantedBy = [ "multi-user.target" ];
319 restartTriggers = [ defaultsFile configFile ];
320 preStart = lib.optionalString useCustomDir ''
321 install -dm775 -o ${user} -g ${group} ${cfg.storageDir}/{${lib.concatStringsSep "," libDirs}}
322 '' + lib.optionalString cfg.database.createLocally ''
323 if ! test -e "/var/lib/${dirName}/db-created"; then
324 ${config.services.mysql.package}/bin/mysql < ${pkg}/share/zoneminder/db/zm_create.sql
325 touch "/var/lib/${dirName}/db-created"
328 ${zoneminder}/bin/zmupdate.pl -nointeractive
329 ${zoneminder}/bin/zmupdate.pl --nointeractive -f
331 # Update ZM's Nix store path in the configuration table. Do nothing if the config doesn't
332 # contain ZM's Nix store path.
333 ${config.services.mysql.package}/bin/mysql -u zoneminder zm << EOF
335 SET Value = REGEXP_REPLACE(Value, "^/nix/store/[^-/]+-zoneminder-[^/]+", "${pkgs.zoneminder}")
336 WHERE Name = "ZM_FONT_FILE_LOCATION";
342 SupplementaryGroups = [ "video" ];
343 ExecStart = "${zoneminder}/bin/zmpkg.pl start";
344 ExecStop = "${zoneminder}/bin/zmpkg.pl stop";
345 ExecReload = "${zoneminder}/bin/zmpkg.pl restart";
346 PIDFile = "/run/${dirName}/zm.pid";
348 Restart = "on-failure";
350 CacheDirectory = dirs cacheDirs;
351 RuntimeDirectory = dirName;
352 ReadWritePaths = lib.mkIf useCustomDir [ cfg.storageDir ];
353 StateDirectory = dirs (lib.optionals (!useCustomDir) libDirs);
354 LogsDirectory = dirName;
356 ProtectSystem = "strict";
357 ProtectKernelTunables = true;
358 SystemCallArchitectures = "native";
359 NoNewPrivileges = true;
364 users.groups.${user} = {
365 gid = config.ids.gids.zoneminder;
368 users.users.${user} = {
369 uid = config.ids.uids.zoneminder;
372 inherit (pkgs.zoneminder.meta) description;
376 meta.maintainers = [ ];