1
{
2
pkg,
3
...
4
}:
5
{
6
config,
7
lib,
8
pkgs,
9
...
10
}:
11
let
12
cfg = config.services.kv;
13
inherit (lib)
14
mkIf
15
mkEnableOption
16
mkOption
17
types
18
;
19
in
20
{
21
options.services.kv = {
22
enable = mkEnableOption "Enable the kv service";
23
package = mkOption {
24
type = types.package;
25
default = pkg;
26
description = "Package to use for kv.";
27
};
28
user = mkOption {
29
type = types.str;
30
description = "User account to run kv as (defaults to 'kv', which I'll create).";
31
default = "kv";
32
};
33
group = mkOption {
34
type = types.str;
35
description = "Group user to run kv as (defaults to 'kv', which I'll create).";
36
default = "kv";
37
};
38
port = mkOption {
39
type = types.int;
40
description = "Port to listen on.";
41
example = 3000;
42
};
43
metricsPort = mkOption {
44
type = types.int;
45
description = "Port to expose Prometheus metrics on.";
46
example = 3001;
47
};
48
host = mkOption {
49
type = types.str;
50
description = "Value for PHX_HOST.";
51
example = "example.com";
52
};
53
databaseUrl = mkOption {
54
type = types.str;
55
description = ''
56
Full Ecto-compatible database URL. Pass `local` to setup a database on
57
the local server automatically.
58
'';
59
example = "postgres://localhost/kv_prod";
60
default = "local";
61
};
62
erlangCookieFile = mkOption {
63
type = types.path;
64
description = "Path to the file containing the Erlang node cookie.";
65
};
66
secretKeyBaseFile = mkOption {
67
type = types.path;
68
description = "Path to the file containing the secret key base.";
69
};
70
};
71
72
config = mkIf (cfg.enable) (
73
let
74
environment = {
75
MIX_ENV = "prod";
76
PHX_HOST = cfg.host;
77
PORT = builtins.toString cfg.port;
78
TELEMETRY_PORT = builtins.toString cfg.metricsPort;
79
};
80
81
localPostgresqlUrl = "ecto://localhost/kv?socket_dir=/run/postgresql";
82
envVarScript = ''
83
export KV_STORAGE_ROOT="$STATE_DIRECTORY"
84
export RELEASE_COOKIE="$(head -n1 ${lib.escapeShellArg cfg.erlangCookieFile})"
85
export SECRET_KEY_BASE="$(head -n1 ${lib.escapeShellArg cfg.secretKeyBaseFile})"
86
${
87
if (cfg.databaseUrl == "local") then
88
''
89
export DATABASE_URL=${lib.escapeShellArg localPostgresqlUrl}
90
''
91
else
92
''
93
export DATABASE_URL=${lib.escapeShellArg cfg.databaseUrl}
94
''
95
}
96
'';
97
in
98
{
99
users.groups.kv = mkIf (cfg.group == "kv") { };
100
users.users.kv = mkIf (cfg.user == "kv") {
101
description = "kv user";
102
group = cfg.group;
103
isSystemUser = true;
104
};
105
106
services.postgresql = mkIf (cfg.databaseUrl == "local") {
107
enable = true;
108
109
ensureUsers = [
110
{
111
name = "kv";
112
ensureDBOwnership = true;
113
}
114
];
115
ensureDatabases = [ "kv" ];
116
};
117
118
systemd.services."kv-migrations" = {
119
description = "kv-migrations";
120
after = [ "networking.target" ] ++ lib.optional (cfg.databaseUrl == "local") "postgresql.service";
121
requires = lib.optional (cfg.databaseUrl == "local") "postgresql.service";
122
123
script = ''
124
${envVarScript}
125
${cfg.package}/bin/kv-migrate
126
'';
127
128
serviceConfig = {
129
Type = "oneshot";
130
User = cfg.user;
131
ProtectSystem = "strict";
132
PrivateTmp = true;
133
UMask = "0007";
134
RemainAfterExit = "true";
135
};
136
137
inherit environment;
138
};
139
140
systemd.services.kv = {
141
description = "kv";
142
wantedBy = [ "multi-user.target" ];
143
after = [ "kv-migrations.service" ];
144
requires = [
145
"postgresql.service"
146
"kv-migrations.service"
147
];
148
149
script = ''
150
${envVarScript}
151
${cfg.package}/bin/kv-server
152
'';
153
154
serviceConfig = {
155
User = cfg.user;
156
ProtectSystem = "strict";
157
PrivateTmp = true;
158
UMask = "0007";
159
Restart = "on-failure";
160
RestartSec = "10s";
161
StateDirectory = "kv";
162
StateDirectoryMode = "0750";
163
};
164
165
inherit environment;
166
};
167
}
168
);
169
}
170