1 { config, lib, pkgs, ... }:
7 cfg = config.services.httpd;
9 certs = config.security.acme.certs;
11 runtimeDir = "/run/httpd";
13 pkg = cfg.package.out;
15 apachectl = pkgs.runCommand "apachectl" { meta.priority = -1; } ''
17 cp ${pkg}/bin/apachectl $out/bin/apachectl
18 sed -i $out/bin/apachectl -e 's|$HTTPD -t|$HTTPD -t -f /etc/httpd/httpd.conf|'
21 php = cfg.phpPackage.override { apxs2Support = true; apacheHttpd = pkg; };
24 majorVersion = lib.versions.major (lib.getVersion php);
25 in (if majorVersion == "8" then "php" else "php${majorVersion}");
27 mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = pkg; };
29 vhosts = attrValues cfg.virtualHosts;
31 # certName is used later on to determine systemd service names.
32 acmeEnabledVhosts = map (hostOpts: hostOpts // {
33 certName = if hostOpts.useACMEHost != null then hostOpts.useACMEHost else hostOpts.hostName;
34 }) (filter (hostOpts: hostOpts.enableACME || hostOpts.useACMEHost != null) vhosts);
36 vhostCertNames = unique (map (hostOpts: hostOpts.certName) acmeEnabledVhosts);
37 dependentCertNames = filter (cert: certs.${cert}.dnsProvider == null) vhostCertNames; # those that might depend on the HTTP server
38 independentCertNames = filter (cert: certs.${cert}.dnsProvider != null) vhostCertNames; # those that don't depend on the HTTP server
40 mkListenInfo = hostOpts:
41 if hostOpts.listen != [] then
44 optionals (hostOpts.onlySSL || hostOpts.addSSL || hostOpts.forceSSL) (map (addr: { ip = addr; port = 443; ssl = true; }) hostOpts.listenAddresses) ++
45 optionals (!hostOpts.onlySSL) (map (addr: { ip = addr; port = 80; ssl = false; }) hostOpts.listenAddresses)
48 listenInfo = unique (concatMap mkListenInfo vhosts);
50 enableHttp2 = any (vhost: vhost.http2) vhosts;
51 enableSSL = any (listen: listen.ssl) listenInfo;
52 enableUserDir = any (vhost: vhost.enableUserDir) vhosts;
54 # NOTE: generally speaking order of modules is very important
56 [ # required apache modules our httpd service cannot run without
57 "authn_core" "authz_core"
59 "mime" "autoindex" "negotiation" "dir"
61 "unixd" "slotmem_shm" "socache_shmcb"
64 ++ (if cfg.mpm == "prefork" then [ "cgi" ] else [ "cgid" ])
65 ++ optional enableHttp2 "http2"
66 ++ optional enableSSL "ssl"
67 ++ optional enableUserDir "userdir"
68 ++ optional cfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; }
69 ++ optional cfg.enablePHP { name = phpModuleName; path = "${php}/modules/lib${phpModuleName}.so"; }
70 ++ optional cfg.enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; }
73 loggingConf = (if cfg.logFormat != "none" then ''
74 ErrorLog ${cfg.logDir}/error.log
78 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
79 LogFormat "%h %l %u %t \"%r\" %>s %b" common
80 LogFormat "%{Referer}i -> %U" referer
81 LogFormat "%{User-agent}i" agent
83 CustomLog ${cfg.logDir}/access.log ${cfg.logFormat}
90 <IfModule mod_setenvif.c>
91 BrowserMatch "Mozilla/2" nokeepalive
92 BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
93 BrowserMatch "RealPlayer 4\.0" force-response-1.0
94 BrowserMatch "Java/1\.0" force-response-1.0
95 BrowserMatch "JDK/1\.0" force-response-1.0
96 BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully
97 BrowserMatch "^WebDrive" redirect-carefully
98 BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully
99 BrowserMatch "^gnome-vfs" redirect-carefully
106 SSLSessionCache shmcb:${runtimeDir}/ssl_scache(512000)
110 SSLRandomSeed startup builtin
111 SSLRandomSeed connect builtin
113 SSLProtocol ${cfg.sslProtocols}
114 SSLCipherSuite ${cfg.sslCiphers}
115 SSLHonorCipherOrder on
121 TypesConfig ${pkg}/conf/mime.types
123 AddType application/x-x509-ca-cert .crt
124 AddType application/x-pkcs7-crl .crl
125 AddType application/x-httpd-php .php .phtml
127 <IfModule mod_mime_magic.c>
128 MIMEMagicFile ${pkg}/conf/magic
133 # support both lua and lua.withPackages derivations
134 luaversion = cfg.package.lua5.lua.luaversion or cfg.package.lua5.luaversion;
138 LuaPackageCPath ${cfg.package.lua5}/lib/lua/${luaversion}/?.so
139 LuaPackagePath ${cfg.package.lua5}/share/lua/${luaversion}/?.lua
143 mkVHostConf = hostOpts:
145 adminAddr = if hostOpts.adminAddr != null then hostOpts.adminAddr else cfg.adminAddr;
146 listen = filter (listen: !listen.ssl) (mkListenInfo hostOpts);
147 listenSSL = filter (listen: listen.ssl) (mkListenInfo hostOpts);
149 useACME = hostOpts.enableACME || hostOpts.useACMEHost != null;
151 if hostOpts.enableACME then certs.${hostOpts.hostName}.directory
152 else if hostOpts.useACMEHost != null then certs.${hostOpts.useACMEHost}.directory
153 else abort "This case should never happen.";
155 sslServerCert = if useACME then "${sslCertDir}/fullchain.pem" else hostOpts.sslServerCert;
156 sslServerKey = if useACME then "${sslCertDir}/key.pem" else hostOpts.sslServerKey;
157 sslServerChain = if useACME then "${sslCertDir}/chain.pem" else hostOpts.sslServerChain;
159 acmeChallenge = optionalString (useACME && hostOpts.acmeRoot != null) ''
160 Alias /.well-known/acme-challenge/ "${hostOpts.acmeRoot}/.well-known/acme-challenge/"
161 <Directory "${hostOpts.acmeRoot}">
163 Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
164 Require method GET POST OPTIONS
169 optionalString (listen != []) ''
170 <VirtualHost ${concatMapStringsSep " " (listen: "${listen.ip}:${toString listen.port}") listen}>
171 ServerName ${hostOpts.hostName}
172 ${concatMapStrings (alias: "ServerAlias ${alias}\n") hostOpts.serverAliases}
173 ${optionalString (adminAddr != null) "ServerAdmin ${adminAddr}"}
178 ${if hostOpts.forceSSL then ''
179 <IfModule mod_rewrite.c>
181 RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge [NC]
182 RewriteCond %{HTTPS} off
183 RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
185 '' else mkVHostCommonConf hostOpts}
188 optionalString (listenSSL != []) ''
189 <VirtualHost ${concatMapStringsSep " " (listen: "${listen.ip}:${toString listen.port}") listenSSL}>
190 ServerName ${hostOpts.hostName}
191 ${concatMapStrings (alias: "ServerAlias ${alias}\n") hostOpts.serverAliases}
192 ${optionalString (adminAddr != null) "ServerAdmin ${adminAddr}"}
194 SSLCertificateFile ${sslServerCert}
195 SSLCertificateKeyFile ${sslServerKey}
196 ${optionalString (sslServerChain != null) "SSLCertificateChainFile ${sslServerChain}"}
197 ${optionalString hostOpts.http2 "Protocols h2 h2c http/1.1"}
199 ${mkVHostCommonConf hostOpts}
204 mkVHostCommonConf = hostOpts:
206 documentRoot = if hostOpts.documentRoot != null
207 then hostOpts.documentRoot
208 else pkgs.emptyDirectory
211 mkLocations = locations: concatStringsSep "\n" (map (config: ''
212 <Location ${config.location}>
213 ${optionalString (config.proxyPass != null) ''
214 <IfModule mod_proxy.c>
215 ProxyPass ${config.proxyPass}
216 ProxyPassReverse ${config.proxyPass}
219 ${optionalString (config.index != null) ''
221 DirectoryIndex ${config.index}
224 ${optionalString (config.alias != null) ''
225 <IfModule mod_alias.c>
226 Alias "${config.alias}"
229 ${config.extraConfig}
231 '') (sortProperties (mapAttrsToList (k: v: v // { location = k; }) locations)));
234 ${optionalString cfg.logPerVirtualHost ''
235 ErrorLog ${cfg.logDir}/error-${hostOpts.hostName}.log
236 CustomLog ${cfg.logDir}/access-${hostOpts.hostName}.log ${hostOpts.logFormat}
239 ${optionalString (hostOpts.robotsEntries != "") ''
240 Alias /robots.txt ${pkgs.writeText "robots.txt" hostOpts.robotsEntries}
243 DocumentRoot "${documentRoot}"
245 <Directory "${documentRoot}">
246 Options Indexes FollowSymLinks
251 ${optionalString hostOpts.enableUserDir ''
253 UserDir disabled root
254 <Directory "/home/*/public_html">
255 AllowOverride FileInfo AuthConfig Limit Indexes
256 Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
257 <Limit GET POST OPTIONS>
260 <LimitExcept GET POST OPTIONS>
266 ${optionalString (hostOpts.globalRedirect != null && hostOpts.globalRedirect != "") ''
267 RedirectPermanent / ${hostOpts.globalRedirect}
271 let makeDirConf = elem: ''
272 Alias ${elem.urlPath} ${elem.dir}/
273 <Directory ${elem.dir}>
279 in concatMapStrings makeDirConf hostOpts.servedDirs
282 ${mkLocations hostOpts.locations}
283 ${hostOpts.extraConfig}
288 confFile = pkgs.writeText "httpd.conf" ''
291 ServerName ${config.networking.hostName}
292 DefaultRuntimeDir ${runtimeDir}/runtime
294 PidFile ${runtimeDir}/httpd.pid
296 ${optionalString (cfg.mpm != "prefork") ''
297 # mod_cgid requires this.
298 ScriptSock ${runtimeDir}/cgisock
302 MaxClients ${toString cfg.maxClients}
303 MaxRequestsPerChild ${toString cfg.maxRequestsPerChild}
307 toStr = listen: "Listen ${listen.ip}:${toString listen.port} ${if listen.ssl then "https" else "http"}";
308 uniqueListen = uniqList {inputList = map toStr listenInfo;};
309 in concatStringsSep "\n" uniqueListen
317 if isString module then { name = module; path = "${pkg}/modules/mod_${module}.so"; }
318 else if isAttrs module then { inherit (module) name path; }
319 else throw "Expecting either a string or attribute set including a name and path.";
321 concatMapStringsSep "\n" (module: "LoadModule ${module.name}_module ${module.path}") (unique (map mkModule modules))
324 AddHandler type-map var
334 Include ${pkg}/conf/extra/httpd-default.conf
335 Include ${pkg}/conf/extra/httpd-autoindex.conf
336 Include ${pkg}/conf/extra/httpd-multilang-errordoc.conf
337 Include ${pkg}/conf/extra/httpd-languages.conf
343 ${optionalString cfg.package.luaSupport luaSetPaths}
345 # Fascist default - deny access to everything.
347 Options FollowSymLinks
352 # But do allow access to files in the store so that we don't have
353 # to generate <Directory> clauses for every generated file that we
355 <Directory /nix/store>
361 ${concatMapStringsSep "\n" mkVHostConf vhosts}
364 # Generate the PHP configuration file. Should probably be factored
365 # out into a separate module.
366 phpIni = pkgs.runCommand "php.ini"
367 { options = cfg.phpOptions;
368 preferLocalBuild = true;
371 cat ${php}/etc/php.ini > $out
372 cat ${php.phpIni} > $out
373 echo "$options" >> $out
376 mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix lib;
383 (mkRemovedOptionModule [ "services" "httpd" "extraSubservices" ] "Most existing subservices have been ported to the NixOS module system. Please update your configuration accordingly.")
384 (mkRemovedOptionModule [ "services" "httpd" "stateDir" ] "The httpd module now uses /run/httpd as a runtime directory.")
385 (mkRenamedOptionModule [ "services" "httpd" "multiProcessingModule" ] [ "services" "httpd" "mpm" ])
387 # virtualHosts options
388 (mkRemovedOptionModule [ "services" "httpd" "documentRoot" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
389 (mkRemovedOptionModule [ "services" "httpd" "enableSSL" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
390 (mkRemovedOptionModule [ "services" "httpd" "enableUserDir" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
391 (mkRemovedOptionModule [ "services" "httpd" "globalRedirect" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
392 (mkRemovedOptionModule [ "services" "httpd" "hostName" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
393 (mkRemovedOptionModule [ "services" "httpd" "listen" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
394 (mkRemovedOptionModule [ "services" "httpd" "robotsEntries" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
395 (mkRemovedOptionModule [ "services" "httpd" "servedDirs" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
396 (mkRemovedOptionModule [ "services" "httpd" "servedFiles" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
397 (mkRemovedOptionModule [ "services" "httpd" "serverAliases" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
398 (mkRemovedOptionModule [ "services" "httpd" "sslServerCert" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
399 (mkRemovedOptionModule [ "services" "httpd" "sslServerChain" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
400 (mkRemovedOptionModule [ "services" "httpd" "sslServerKey" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
409 enable = mkEnableOption "the Apache HTTP Server";
411 package = mkPackageOption pkgs "apacheHttpd" { };
413 configFile = mkOption {
416 defaultText = literalExpression "confFile";
417 example = literalExpression ''pkgs.writeText "httpd.conf" "# my custom config file ..."'';
419 Override the configuration file used by Apache. By default,
420 NixOS generates one automatically.
424 extraConfig = mkOption {
428 Configuration lines appended to the generated Apache
429 configuration file. Note that this mechanism will not work
430 when {option}`configFile` is overridden.
434 extraModules = mkOption {
435 type = types.listOf types.unspecified;
437 example = literalExpression ''
440 { name = "jk"; path = "''${pkgs.apacheHttpdPackages.mod_jk}/modules/mod_jk.so"; }
444 Additional Apache modules to be used. These can be
445 specified as a string in the case of modules distributed
446 with Apache, or as an attribute set specifying the
447 {var}`name` and {var}`path` of the
452 adminAddr = mkOption {
453 type = types.nullOr types.str;
454 example = "admin@example.org";
456 description = "E-mail address of the server administrator.";
459 logFormat = mkOption {
462 example = "combined";
464 Log format for log files. Possible values are: combined, common, referer, agent, none.
465 See <https://httpd.apache.org/docs/2.4/logs.html> for more details.
469 logPerVirtualHost = mkOption {
473 If enabled, each virtual host gets its own
474 {file}`access.log` and
475 {file}`error.log`, namely suffixed by the
476 {option}`hostName` of the virtual host.
484 User account under which httpd children processes run.
486 If you require the main httpd process to run as
487 `root` add the following configuration:
489 systemd.services.httpd.serviceConfig.User = lib.mkForce "root";
498 Group under which httpd children processes run.
504 default = "/var/log/httpd";
506 Directory for Apache's log files. It is created automatically.
510 virtualHosts = mkOption {
511 type = with types; attrsOf (submodule (import ./vhost-options.nix));
514 documentRoot = "${pkg}/htdocs";
517 defaultText = literalExpression ''
520 documentRoot = "''${package.out}/htdocs";
524 example = literalExpression ''
526 "foo.example.com" = {
528 documentRoot = "/var/www/foo.example.com"
530 "bar.example.com" = {
532 documentRoot = "/var/www/bar.example.com";
537 Specification of the virtual hosts served by Apache. Each
538 element should be an attribute set specifying the
539 configuration of the virtual host.
543 enableMellon = mkEnableOption "the mod_auth_mellon module";
545 enablePHP = mkEnableOption "the PHP module";
547 phpPackage = mkPackageOption pkgs "php" { };
549 enablePerl = mkEnableOption "the Perl module (mod_perl)";
551 phpOptions = mkOption {
556 date.timezone = "CET"
559 Options appended to the PHP configuration file {file}`php.ini`.
564 type = types.enum [ "event" "prefork" "worker" ];
568 Multi-processing module to be used by Apache. Available
569 modules are `prefork` (handles each
570 request in a separate child process), `worker`
571 (hybrid approach that starts a number of child processes
572 each running a number of threads) and `event`
573 (the default; a recent variant of `worker`
574 that handles persistent connections more efficiently).
578 maxClients = mkOption {
582 description = "Maximum number of httpd processes (prefork)";
585 maxRequestsPerChild = mkOption {
590 Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited.
594 sslCiphers = mkOption {
596 default = "HIGH:!aNULL:!MD5:!EXP";
597 description = "Cipher Suite available for negotiation in SSL proxy handshake.";
600 sslProtocols = mkOption {
602 default = "All -SSLv2 -SSLv3 -TLSv1 -TLSv1.1";
603 example = "All -SSLv2 -SSLv3";
604 description = "Allowed SSL/TLS protocol versions.";
612 config = mkIf cfg.enable {
616 assertion = all (hostOpts: !hostOpts.enableSSL) vhosts;
618 The option `services.httpd.virtualHosts.<name>.enableSSL` no longer has any effect; please remove it.
619 Select one of `services.httpd.virtualHosts.<name>.addSSL`, `services.httpd.virtualHosts.<name>.forceSSL`,
620 or `services.httpd.virtualHosts.<name>.onlySSL`.
624 assertion = all (hostOpts: with hostOpts; !(addSSL && onlySSL) && !(forceSSL && onlySSL) && !(addSSL && forceSSL)) vhosts;
626 Options `services.httpd.virtualHosts.<name>.addSSL`,
627 `services.httpd.virtualHosts.<name>.onlySSL` and `services.httpd.virtualHosts.<name>.forceSSL`
628 are mutually exclusive.
632 assertion = all (hostOpts: !(hostOpts.enableACME && hostOpts.useACMEHost != null)) vhosts;
634 Options `services.httpd.virtualHosts.<name>.enableACME` and
635 `services.httpd.virtualHosts.<name>.useACMEHost` are mutually exclusive.
639 assertion = cfg.enablePHP -> php.ztsSupport;
641 The php package provided by `services.httpd.phpPackage` is not built with zts support. Please
642 ensure the php has zts support by settings `services.httpd.phpPackage = php.override { ztsSupport = true; }`
645 ] ++ map (name: mkCertOwnershipAssertion {
646 cert = config.security.acme.certs.${name};
647 groups = config.users.groups;
648 services = [ config.systemd.services.httpd ] ++ lib.optional (vhostCertNames != []) config.systemd.services.httpd-config-reload;
652 mapAttrsToList (name: hostOpts: ''
653 Using config.services.httpd.virtualHosts."${name}".servedFiles is deprecated and will become unsupported in a future release. Your configuration will continue to work as is but please migrate your configuration to config.services.httpd.virtualHosts."${name}".locations before the 20.09 release of NixOS.
654 '') (filterAttrs (name: hostOpts: hostOpts.servedFiles != []) cfg.virtualHosts);
656 users.users = optionalAttrs (cfg.user == "wwwrun") {
659 description = "Apache httpd user";
660 uid = config.ids.uids.wwwrun;
664 users.groups = optionalAttrs (cfg.group == "wwwrun") {
665 wwwrun.gid = config.ids.gids.wwwrun;
668 security.acme.certs = let
669 acmePairs = map (hostOpts: let
670 hasRoot = hostOpts.acmeRoot != null;
671 in nameValuePair hostOpts.hostName {
672 group = mkDefault cfg.group;
673 # if acmeRoot is null inherit config.security.acme
674 # Since config.security.acme.certs.<cert>.webroot's own default value
675 # should take precedence set priority higher than mkOptionDefault
676 webroot = mkOverride (if hasRoot then 1000 else 2000) hostOpts.acmeRoot;
677 # Also nudge dnsProvider to null in case it is inherited
678 dnsProvider = mkOverride (if hasRoot then 1000 else 2000) null;
679 extraDomainNames = hostOpts.serverAliases;
680 # Use the vhost-specific email address if provided, otherwise let
681 # security.acme.email or security.acme.certs.<cert>.email be used.
682 email = mkOverride 2000 (if hostOpts.adminAddr != null then hostOpts.adminAddr else cfg.adminAddr);
683 # Filter for enableACME-only vhosts. Don't want to create dud certs
684 }) (filter (hostOpts: hostOpts.useACMEHost == null) acmeEnabledVhosts);
685 in listToAttrs acmePairs;
687 # httpd requires a stable path to the configuration file for reloads
688 environment.etc."httpd/httpd.conf".source = cfg.configFile;
689 environment.systemPackages = [
694 services.logrotate = optionalAttrs (cfg.logFormat != "none") {
695 enable = mkDefault true;
697 files = "${cfg.logDir}/*.log";
698 su = "${cfg.user} ${cfg.group}";
701 sharedscripts = true;
703 delaycompress = true;
704 postrotate = "systemctl reload httpd.service > /dev/null 2>/dev/null || true";
708 services.httpd.phpOptions =
710 ; Don't advertise PHP
712 '' + optionalString (config.time.timeZone != null) ''
714 ; Apparently PHP doesn't use $TZ.
715 date.timezone = "${config.time.timeZone}"
718 services.httpd.extraModules = mkBefore [
719 # HTTP authentication mechanisms: basic and digest.
720 "auth_basic" "auth_digest"
722 # Authentication: is the user who he claims to be?
723 "authn_file" "authn_dbm" "authn_anon"
725 # Authorization: is the user allowed access?
726 "authz_user" "authz_groupfile" "authz_host"
729 "ext_filter" "include" "env" "mime_magic"
730 "cern_meta" "expires" "headers" "usertrack" "setenvif"
731 "dav" "status" "asis" "info" "dav_fs"
732 "vhost_alias" "imagemap" "actions" "speling"
736 # For compatibility with old configurations, the new module mod_access_compat is provided.
740 systemd.tmpfiles.rules =
742 svc = config.systemd.services.httpd.serviceConfig;
745 "d '${cfg.logDir}' 0700 ${svc.User} ${svc.Group}"
746 "Z '${cfg.logDir}' - ${svc.User} ${svc.Group}"
749 systemd.services.httpd = {
750 description = "Apache HTTPD";
751 wantedBy = [ "multi-user.target" ];
752 wants = concatLists (map (certName: [ "acme-finished-${certName}.target" ]) vhostCertNames);
753 after = [ "network.target" ]
754 ++ map (certName: "acme-selfsigned-${certName}.service") vhostCertNames
755 ++ map (certName: "acme-${certName}.service") independentCertNames; # avoid loading self-signed key w/ real cert, or vice-versa
756 before = map (certName: "acme-${certName}.service") dependentCertNames;
757 restartTriggers = [ cfg.configFile ];
759 path = [ pkg pkgs.coreutils pkgs.gnugrep ];
762 optionalAttrs cfg.enablePHP { PHPRC = phpIni; }
763 // optionalAttrs cfg.enableMellon { LD_LIBRARY_PATH = "${pkgs.xmlsec}/lib"; };
767 # Get rid of old semaphores. These tend to accumulate across
768 # server restarts, eventually preventing it from restarting
770 for i in $(${pkgs.util-linux}/bin/ipcs -s | grep ' ${cfg.user} ' | cut -f2 -d ' '); do
771 ${pkgs.util-linux}/bin/ipcrm -s $i
776 ExecStart = "@${pkg}/bin/httpd httpd -f /etc/httpd/httpd.conf";
777 ExecStop = "${pkg}/bin/httpd -f /etc/httpd/httpd.conf -k graceful-stop";
778 ExecReload = "${pkg}/bin/httpd -f /etc/httpd/httpd.conf -k graceful";
782 PIDFile = "${runtimeDir}/httpd.pid";
785 RuntimeDirectory = "httpd httpd/runtime";
786 RuntimeDirectoryMode = "0750";
787 AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
791 # postRun hooks on cert renew can't be used to restart Apache since renewal
792 # runs as the unprivileged acme user. sslTargets are added to wantedBy + before
793 # which allows the acme-finished-$cert.target to signify the successful updating
794 # of certs end-to-end.
795 systemd.services.httpd-config-reload = let
796 sslServices = map (certName: "acme-${certName}.service") vhostCertNames;
797 sslTargets = map (certName: "acme-finished-${certName}.target") vhostCertNames;
798 in mkIf (vhostCertNames != []) {
799 wantedBy = sslServices ++ [ "multi-user.target" ];
800 # Before the finished targets, after the renew services.
801 # This service might be needed for HTTP-01 challenges, but we only want to confirm
802 # certs are updated _after_ config has been reloaded.
805 restartTriggers = [ cfg.configFile ];
806 # Block reloading if not all certs exist yet.
807 # Happens when config changes add new vhosts/certs.
808 unitConfig.ConditionPathExists = map (certName: certs.${certName}.directory + "/fullchain.pem") vhostCertNames;
812 ExecCondition = "/run/current-system/systemd/bin/systemctl -q is-active httpd.service";
813 ExecStartPre = "${pkg}/bin/httpd -f /etc/httpd/httpd.conf -t";
814 ExecStart = "/run/current-system/systemd/bin/systemctl reload httpd.service";