{ config, lib, pkgs, ... }: with lib; let cfg = config.services.myPhpfpm; enabled = cfg.poolConfigs != {} || cfg.pools != {}; stateDir = "/run/phpfpm"; poolConfigs = cfg.poolConfigs // mapAttrs mkPool cfg.pools; mkPool = n: p: '' listen = ${p.listen} ${p.extraConfig} ''; fpmCfgFile = pool: poolConfig: pkgs.writeText "phpfpm-${pool}.conf" '' [global] error_log = syslog daemonize = no ${cfg.extraConfig} [${pool}] ${poolConfig} ''; phpIni = poolPhpOptions: (pkgs.runCommand "php.ini" { inherit (cfg) phpPackage phpOptions; inherit poolPhpOptions; nixDefaults = '' sendmail_path = "/run/wrappers/bin/sendmail -t -i" ''; passAsFile = [ "nixDefaults" "phpOptions" "poolPhpOptions" ]; } '' cat $phpPackage/etc/php.ini $nixDefaultsPath $phpOptionsPath $poolPhpOptionsPath > $out ''); in { options = { services.myPhpfpm = { extraConfig = mkOption { type = types.lines; default = ""; description = '' Extra configuration that should be put in the global section of the PHP-FPM configuration file. Do not specify the options error_log or daemonize here, since they are generated by NixOS. ''; }; phpPackage = mkOption { type = types.package; default = pkgs.php; defaultText = "pkgs.php"; description = '' The PHP package to use for running the PHP-FPM service. ''; }; phpOptions = mkOption { type = types.lines; default = ""; example = '' date.timezone = "CET" ''; description = "Options appended to the PHP configuration file php.ini."; }; serviceDependencies = mkOption { default = {}; type = types.attrsOf (types.listOf types.string); example = literalExample '' { mypool = ["postgresql.service"]; } ''; description = '' Extra service dependencies specific to pool. ''; }; envFile = mkOption { default = {}; type = types.attrsOf types.string; example = literalExample '' { mypool = "path/to/file"; } ''; description = '' Extra environment file go into the service script. ''; }; preStart = mkOption { default = {}; type = types.attrsOf types.lines; example = literalExample '' { mypool = ''' touch foo '''; } ''; description = '' Extra lines that will go into the preStart systemd service ''; }; poolPhpConfigs = mkOption { default = {}; type = types.attrsOf types.lines; example = literalExample '' { mypool = ''' extension = some_extension.so '''; } ''; description = '' Extra lines that go into the php configuration specific to pool. ''; }; poolConfigs = mkOption { default = {}; type = types.attrsOf types.lines; example = literalExample '' { mypool = ''' listen = /run/phpfpm/mypool user = nobody pm = dynamic pm.max_children = 75 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.max_requests = 500 '''; } ''; description = '' A mapping between PHP-FPM pool names and their configurations. See the documentation on php-fpm.conf for details on configuration directives. If no pools are defined, the phpfpm service is disabled. ''; }; pools = mkOption { type = types.attrsOf (types.submodule (import ./pool-options.nix { inherit lib; })); default = {}; example = literalExample '' { mypool = { listen = "/path/to/unix/socket"; extraConfig = ''' user = nobody pm = dynamic pm.max_children = 75 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.max_requests = 500 '''; } }''; description = '' PHP-FPM pools. If no pools or poolConfigs are defined, the PHP-FPM service is disabled. ''; }; }; }; config = mkIf enabled { systemd.slices.phpfpm = { description = "PHP FastCGI Process manager pools slice"; }; systemd.targets.phpfpm = { description = "PHP FastCGI Process manager pools target"; wantedBy = [ "multi-user.target" ]; }; systemd.services = flip mapAttrs' poolConfigs (pool: poolConfig: nameValuePair "phpfpm-${pool}" { description = "PHP FastCGI Process Manager service for pool ${pool}"; after = [ "network.target" ] ++ (cfg.serviceDependencies.${pool} or []); wants = cfg.serviceDependencies.${pool} or []; wantedBy = [ "phpfpm.target" ]; partOf = [ "phpfpm.target" ]; preStart = '' mkdir -p ${stateDir} '' + (cfg.preStart.${pool} or ""); serviceConfig = let cfgFile = fpmCfgFile pool poolConfig; poolPhpIni = cfg.poolPhpConfigs.${pool} or ""; in { EnvironmentFile = if builtins.hasAttr pool cfg.envFile then [cfg.envFile.${pool}] else []; Slice = "phpfpm.slice"; PrivateDevices = true; ProtectSystem = "full"; ProtectHome = true; # XXX: We need AF_NETLINK to make the sendmail SUID binary from postfix work RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK"; Type = "notify"; ExecStart = "${cfg.phpPackage}/bin/php-fpm -y ${cfgFile} -c ${phpIni poolPhpIni}"; ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID"; }; } ); }; }