Skip to content

Commit ebb2ef6

Browse files
authored
feat: add support for phpfpm (#529)
I wanted to run WordPress via process compose and had to add phpfpm to achieve this. It is mainly based on the [NixOS module](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-servers/phpfpm/default.nix), but simplified a bit.
1 parent 8bc6dff commit ebb2ef6

File tree

6 files changed

+239
-0
lines changed

6 files changed

+239
-0
lines changed

doc/phpfpm.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# PHP FastCGI Process Manager
2+
3+
[PHP FPM](https://www.php.net/manual/en/book.fpm.php) (FastCGI Process Manager) is a primary PHP FastCGI implementation containing some features (mostly) useful for heavy-loaded sites.
4+
5+
## Unix socket
6+
7+
PHP FPM supports the usage of [Unix socket](https://man7.org/linux/man-pages/man2/socket.2.html) to listen to connections. By default, the socket `phpfpm.sock` will be used.
8+
9+
```nix
10+
# Inside `process-compose.<name>`
11+
{
12+
services.phpfpm."php1" = {
13+
enable = true;
14+
extraConfig = {
15+
"pm" = "ondemand";
16+
"pm.max_children" = 1;
17+
};
18+
};
19+
}
20+
```
21+
22+
## TCP port
23+
24+
```nix
25+
# Inside `process-compose.<name>`
26+
{
27+
services.phpfpm."php1" = {
28+
enable = true;
29+
listen = 9000;
30+
extraConfig = {
31+
"pm" = "ondemand";
32+
"pm.max_children" = 1;
33+
};
34+
};
35+
}
36+
```
37+
38+
## Usage example
39+
40+
<https://github.com/juspay/services-flake/blob/main/nix/services/phpfpm_test.nix>

doc/services.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ short-title: Services
1818
- [[nginx]]#
1919
- [[ollama]]#
2020
- [[open-webui]]#
21+
- [[phpfpm]]#
2122
- [[postgresql]]#
2223
- [[pgadmin]]
2324
- [[prometheus]]#

nix/services/default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ in
2626
./weaviate.nix
2727
./searxng.nix
2828
./tika.nix
29+
./phpfpm.nix
2930
]) ++ [
3031
./devshell.nix
3132
];

nix/services/phpfpm.nix

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
{ config
2+
, lib
3+
, name
4+
, pkgs
5+
, ...
6+
}:
7+
let
8+
inherit (lib) types;
9+
10+
toStr =
11+
value:
12+
if true == value then
13+
"yes"
14+
else if false == value then
15+
"no"
16+
else
17+
toString value;
18+
19+
configType =
20+
with types;
21+
attrsOf (oneOf [
22+
str
23+
int
24+
bool
25+
]);
26+
in
27+
{
28+
options = {
29+
package = lib.mkPackageOption pkgs "php" { };
30+
31+
listen = lib.mkOption {
32+
type = types.either types.port types.str;
33+
default = "phpfpm.sock";
34+
description = ''
35+
The address on which to accept FastCGI requests.
36+
'';
37+
};
38+
39+
phpOptions = lib.mkOption {
40+
type = types.lines;
41+
default = "";
42+
example = ''
43+
date.timezone = "CET"
44+
'';
45+
description = ''
46+
Options appended to the PHP configuration file {file}`php.ini` used for this PHP-FPM pool.
47+
'';
48+
};
49+
50+
phpEnv = lib.mkOption {
51+
type = types.attrsOf types.str;
52+
default = { };
53+
description = ''
54+
Environment variables used for this PHP-FPM pool.
55+
'';
56+
example = lib.literalExpression ''
57+
{
58+
HOSTNAME = "$HOSTNAME";
59+
TMP = "/tmp";
60+
TMPDIR = "/tmp";
61+
TEMP = "/tmp";
62+
}
63+
'';
64+
};
65+
66+
extraConfig = lib.mkOption {
67+
type = configType;
68+
default = { };
69+
description = ''
70+
PHP-FPM pool directives. Refer to the "List of pool directives" section of
71+
<https://www.php.net/manual/en/install.fpm.configuration.php>
72+
for details. Note that settings names must be enclosed in quotes (e.g.
73+
`"pm.max_children"` instead of `pm.max_children`).
74+
'';
75+
example = lib.literalExpression ''
76+
{
77+
"pm" = "dynamic";
78+
"pm.max_children" = 75;
79+
"pm.start_servers" = 10;
80+
"pm.min_spare_servers" = 5;
81+
"pm.max_spare_servers" = 20;
82+
"pm.max_requests" = 500;
83+
}
84+
'';
85+
};
86+
};
87+
config = {
88+
extraConfig.listen = lib.mkDefault config.listen;
89+
90+
outputs.settings = {
91+
processes.${name} =
92+
let
93+
cfgFile = pkgs.writeText "phpfpm-${name}.conf" ''
94+
[global]
95+
daemonize = no
96+
error_log = /proc/self/fd/2
97+
98+
[${name}]
99+
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "${n} = ${toStr v}") config.extraConfig)}
100+
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "env[${n}] = ${toStr v}") config.phpEnv)}
101+
'';
102+
iniFile =
103+
pkgs.runCommand "php.ini"
104+
{
105+
inherit (config) phpOptions;
106+
preferLocalBuild = true;
107+
passAsFile = [ "phpOptions" ];
108+
}
109+
''
110+
cat ${config.package}/etc/php.ini $phpOptionsPath > $out
111+
'';
112+
in
113+
{
114+
command = pkgs.writeShellApplication {
115+
name = "start-phpfpm";
116+
runtimeInputs = [ config.package ];
117+
text = ''
118+
DATA_DIR="$(readlink -m ${config.dataDir})"
119+
if [[ ! -d "$DATA_DIR" ]]; then
120+
mkdir -p "$DATA_DIR"
121+
fi
122+
exec php-fpm -p "$DATA_DIR" -y ${cfgFile} -c ${iniFile}
123+
'';
124+
};
125+
126+
readiness_probe =
127+
let
128+
# Transform `listen` by prefixing `config.dataDir` if a relative path is used
129+
transformedListen =
130+
if (builtins.isString config.listen && (! lib.hasPrefix "/" config.listen)) then
131+
"${config.dataDir}/${config.listen}"
132+
else
133+
config.listen;
134+
in
135+
{
136+
exec.command =
137+
if (builtins.isInt config.listen) then
138+
"${pkgs.fcgi}/bin/cgi-fcgi -bind -connect 127.0.0.1:${toString config.listen}"
139+
else
140+
"${pkgs.fcgi}/bin/cgi-fcgi -bind -connect ${transformedListen}";
141+
142+
initial_delay_seconds = 2;
143+
period_seconds = 10;
144+
timeout_seconds = 4;
145+
success_threshold = 1;
146+
failure_threshold = 5;
147+
};
148+
};
149+
};
150+
};
151+
}

nix/services/phpfpm_test.nix

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{ pkgs, config, ... }: {
2+
services.phpfpm."phpfpm1" = {
3+
enable = true;
4+
listen = "phpfpm.sock";
5+
extraConfig = {
6+
"pm" = "ondemand";
7+
"pm.max_children" = 1;
8+
};
9+
phpOptions = ''
10+
date.timezone = "CET"
11+
'';
12+
phpEnv = {
13+
TMPDIR = "/tmp";
14+
};
15+
};
16+
17+
services.phpfpm."phpfpm2" = {
18+
enable = true;
19+
listen = 9000;
20+
extraConfig = {
21+
"pm" = "ondemand";
22+
"pm.max_children" = 1;
23+
};
24+
};
25+
26+
settings.processes.test =
27+
let
28+
cfg = config.services.phpfpm."phpfpm1";
29+
in
30+
{
31+
command = pkgs.writeShellApplication {
32+
runtimeInputs = [ cfg.package pkgs.fcgi ];
33+
text = ''
34+
echo "Test connection to phpfpm1 listening on Unix socket"
35+
cgi-fcgi -bind -connect ./data/phpfpm1/phpfpm.sock
36+
37+
echo "Test connection to phpfpm2 listening on port 9000"
38+
cgi-fcgi -bind -connect 127.0.0.1:9000
39+
'';
40+
name = "phpfpm-test";
41+
};
42+
depends_on."phpfpm1".condition = "process_healthy";
43+
depends_on."phpfpm2".condition = "process_healthy";
44+
};
45+
}

test/flake.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"${inputs.services-flake}/nix/services/ollama_test.nix"
5555
"${inputs.services-flake}/nix/services/open-webui_test.nix"
5656
"${inputs.services-flake}/nix/services/pgadmin_test.nix"
57+
"${inputs.services-flake}/nix/services/phpfpm_test.nix"
5758
"${inputs.services-flake}/nix/services/postgres/postgres_test.nix"
5859
"${inputs.services-flake}/nix/services/prometheus_test.nix"
5960
"${inputs.services-flake}/nix/services/redis_test.nix"

0 commit comments

Comments
 (0)