diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 2b7fcb31404..c1a51fbf8b4 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -162,6 +162,10 @@ let ${cfg.appendConfig} ''; + configPath = if cfg.enableReload + then "/etc/nginx/nginx.conf" + else configFile; + vhosts = concatStringsSep "\n" (mapAttrsToList (vhostName: vhost: let onlySSL = vhost.onlySSL || vhost.enableSSL; @@ -431,6 +435,16 @@ in "; }; + enableReload = mkOption { + default = false; + type = types.bool; + description = '' + Reload nginx when configuration file changes (instead of restart). + The configuration file is exposed at /etc/nginx/nginx.conf. + See also systemd.services.*.restartIfChanged. + ''; + }; + stateDir = mkOption { default = "/var/spool/nginx"; description = " @@ -638,10 +652,10 @@ in preStart = '' ${cfg.preStart} - ${cfg.package}/bin/nginx -c ${configFile} -p ${cfg.stateDir} -t + ${cfg.package}/bin/nginx -c ${configPath} -p ${cfg.stateDir} -t ''; serviceConfig = { - ExecStart = "${cfg.package}/bin/nginx -c ${configFile} -p ${cfg.stateDir}"; + ExecStart = "${cfg.package}/bin/nginx -c ${configPath} -p ${cfg.stateDir}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; Restart = "always"; RestartSec = "10s"; @@ -649,6 +663,21 @@ in }; }; + environment.etc."nginx/nginx.conf" = mkIf cfg.enableReload { + source = configFile; + }; + + systemd.services.nginx-config-reload = mkIf cfg.enableReload { + wantedBy = [ "nginx.service" ]; + restartTriggers = [ configFile ]; + script = '' + if ${pkgs.systemd}/bin/systemctl -q is-active nginx.service ; then + ${pkgs.systemd}/bin/systemctl reload nginx.service + fi + ''; + serviceConfig.RemainAfterExit = true; + }; + security.acme.certs = filterAttrs (n: v: v != {}) ( let vhostsConfigs = mapAttrsToList (vhostName: vhostConfig: vhostConfig) virtualHosts; diff --git a/nixos/tests/nginx.nix b/nixos/tests/nginx.nix index d66d99821c1..d0b7306ae83 100644 --- a/nixos/tests/nginx.nix +++ b/nixos/tests/nginx.nix @@ -3,15 +3,15 @@ # generated virtual hosts config. # 2. whether the ETag header is properly generated whenever we're serving # files in Nix store paths - +# 3. nginx doesn't restart on configuration changes (only reloads) import ./make-test.nix ({ pkgs, ... }: { name = "nginx"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ mbbx6spp ]; }; - nodes = let - commonConfig = { pkgs, ... }: { + nodes = { + webserver = { pkgs, lib, ... }: { services.nginx.enable = true; services.nginx.commonHttpConfig = '' log_format ceeformat '@cee: {"status":"$status",' @@ -32,30 +32,42 @@ import ./make-test.nix ({ pkgs, ... }: { location /favicon.ico { allow all; access_log off; log_not_found off; } ''; }; + services.nginx.virtualHosts.localhost = { root = pkgs.runCommand "testdir" {} '' mkdir "$out" echo hello world > "$out/index.html" ''; }; - }; - in { - webserver = commonConfig; - newwebserver = { pkgs, lib, ... }: { - imports = [ commonConfig ]; - services.nginx.virtualHosts.localhost = { - root = lib.mkForce (pkgs.runCommand "testdir2" {} '' - mkdir "$out" - echo hello world > "$out/index.html" - ''); - }; + services.nginx.enableReload = true; + + nesting.clone = [ + { + services.nginx.virtualHosts.localhost = { + root = lib.mkForce (pkgs.runCommand "testdir2" {} '' + mkdir "$out" + echo content changed > "$out/index.html" + ''); + }; + } + + { + services.nginx.virtualHosts."1.my.test".listen = [ { addr = "127.0.0.1"; port = 8080; }]; + } + + { + services.nginx.package = pkgs.nginxUnstable; + } + ]; }; + }; testScript = { nodes, ... }: let - newServerSystem = nodes.newwebserver.config.system.build.toplevel; - switch = "${newServerSystem}/bin/switch-to-configuration test"; + etagSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-1"; + justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-2"; + reloadRestartSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-3"; in '' my $url = 'http://localhost/index.html'; @@ -77,9 +89,23 @@ import ./make-test.nix ({ pkgs, ... }: { subtest "check ETag if serving Nix store paths", sub { my $oldEtag = checkEtag; - $webserver->succeed('${switch}'); + $webserver->succeed("${etagSystem}/bin/switch-to-configuration test >&2"); + $webserver->sleep(1); # race condition my $newEtag = checkEtag; die "Old ETag $oldEtag is the same as $newEtag" if $oldEtag eq $newEtag; }; + + subtest "config is reloaded on nixos-rebuild switch", sub { + $webserver->succeed("${justReloadSystem}/bin/switch-to-configuration test >&2"); + $webserver->waitForOpenPort("8080"); + $webserver->fail("journalctl -u nginx | grep -q -i stopped"); + $webserver->succeed("journalctl -u nginx | grep -q -i reloaded"); + }; + + subtest "restart when nginx package changes", sub { + $webserver->succeed("${reloadRestartSystem}/bin/switch-to-configuration test >&2"); + $webserver->waitForUnit("nginx"); + $webserver->succeed("journalctl -u nginx | grep -q -i stopped"); + }; ''; })