1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.phpfpm;
7 enabled = cfg.poolConfigs != {} || cfg.pools != {};
8
9 stateDir = "/run/phpfpm";
10
11 poolConfigs = cfg.poolConfigs // mapAttrs mkPool cfg.pools;
12
13 mkPool = n: p: ''
14 listen = ${p.listen}
15 ${p.extraConfig}
16 '';
17
18 fpmCfgFile = pool: poolConfig: pkgs.writeText "phpfpm-${pool}.conf" ''
19 [global]
20 error_log = syslog
21 daemonize = no
22 ${cfg.extraConfig}
23
24 [${pool}]
25 ${poolConfig}
26 '';
27
28 phpIni = pkgs.runCommand "php.ini" {
29 inherit (cfg) phpPackage phpOptions;
30 nixDefaults = ''
31 sendmail_path = "/run/wrappers/bin/sendmail -t -i"
32 '';
33 passAsFile = [ "nixDefaults" "phpOptions" ];
34 } ''
35 cat $phpPackage/etc/php.ini $nixDefaultsPath $phpOptionsPath > $out
36 '';
37
38in {
39
40 options = {
41 services.phpfpm = {
42 extraConfig = mkOption {
43 type = types.lines;
44 default = "";
45 description = ''
46 Extra configuration that should be put in the global section of
47 the PHP-FPM configuration file. Do not specify the options
48 <literal>error_log</literal> or
49 <literal>daemonize</literal> here, since they are generated by
50 NixOS.
51 '';
52 };
53
54 phpPackage = mkOption {
55 type = types.package;
56 default = pkgs.php;
57 defaultText = "pkgs.php";
58 description = ''
59 The PHP package to use for running the PHP-FPM service.
60 '';
61 };
62
63 phpOptions = mkOption {
64 type = types.lines;
65 default = "";
66 example =
67 ''
68 date.timezone = "CET"
69 '';
70 description =
71 "Options appended to the PHP configuration file <filename>php.ini</filename>.";
72 };
73
74 poolConfigs = mkOption {
75 default = {};
76 type = types.attrsOf types.lines;
77 example = literalExample ''
78 { mypool = '''
79 listen = /run/phpfpm/mypool
80 user = nobody
81 pm = dynamic
82 pm.max_children = 75
83 pm.start_servers = 10
84 pm.min_spare_servers = 5
85 pm.max_spare_servers = 20
86 pm.max_requests = 500
87 ''';
88 }
89 '';
90 description = ''
91 A mapping between PHP-FPM pool names and their configurations.
92 See the documentation on <literal>php-fpm.conf</literal> for
93 details on configuration directives. If no pools are defined,
94 the phpfpm service is disabled.
95 '';
96 };
97
98 pools = mkOption {
99 type = types.attrsOf (types.submodule (import ./pool-options.nix {
100 inherit lib;
101 }));
102 default = {};
103 example = literalExample ''
104 {
105 mypool = {
106 listen = "/path/to/unix/socket";
107 extraConfig = '''
108 user = nobody
109 pm = dynamic
110 pm.max_children = 75
111 pm.start_servers = 10
112 pm.min_spare_servers = 5
113 pm.max_spare_servers = 20
114 pm.max_requests = 500
115 ''';
116 }
117 }'';
118 description = ''
119 PHP-FPM pools. If no pools or poolConfigs are defined, the PHP-FPM
120 service is disabled.
121 '';
122 };
123 };
124 };
125
126 config = mkIf enabled {
127
128 systemd.slices.phpfpm = {
129 description = "PHP FastCGI Process manager pools slice";
130 };
131
132 systemd.targets.phpfpm = {
133 description = "PHP FastCGI Process manager pools target";
134 wantedBy = [ "multi-user.target" ];
135 };
136
137 systemd.services = flip mapAttrs' poolConfigs (pool: poolConfig:
138 nameValuePair "phpfpm-${pool}" {
139 description = "PHP FastCGI Process Manager service for pool ${pool}";
140 after = [ "network.target" ];
141 wantedBy = [ "phpfpm.target" ];
142 partOf = [ "phpfpm.target" ];
143 preStart = ''
144 mkdir -p ${stateDir}
145 '';
146 serviceConfig = let
147 cfgFile = fpmCfgFile pool poolConfig;
148 in {
149 Slice = "phpfpm.slice";
150 PrivateDevices = true;
151 ProtectSystem = "full";
152 ProtectHome = true;
153 # XXX: We need AF_NETLINK to make the sendmail SUID binary from postfix work
154 RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
155 Type = "notify";
156 ExecStart = "${cfg.phpPackage}/bin/php-fpm -y ${cfgFile} -c ${phpIni}";
157 ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID";
158 };
159 }
160 );
161 };
162}