1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6 cfg = config.services.prometheus;
7 promUser = "prometheus";
8 promGroup = "prometheus";
9
10 # Get a submodule without any embedded metadata:
11 _filter = x: filterAttrs (k: v: k != "_module") x;
12
13 # Pretty-print JSON to a file
14 writePrettyJSON = name: x:
15 pkgs.runCommand name { } ''
16 echo '${builtins.toJSON x}' | ${pkgs.jq}/bin/jq . > $out
17 '';
18
19 # This becomes the main config file
20 promConfig = {
21 global = cfg.globalConfig;
22 rule_files = cfg.ruleFiles ++ [
23 (pkgs.writeText "prometheus.rules" (concatStringsSep "\n" cfg.rules))
24 ];
25 scrape_configs = cfg.scrapeConfigs;
26 };
27
28 generatedPrometheusYml = writePrettyJSON "prometheus.yml" promConfig;
29
30 prometheusYml =
31 if cfg.configText != null then
32 pkgs.writeText "prometheus.yml" cfg.configText
33 else generatedPrometheusYml;
34
35 cmdlineArgs = cfg.extraFlags ++ [
36 "-storage.local.path=${cfg.dataDir}/metrics"
37 "-config.file=${prometheusYml}"
38 "-web.listen-address=${cfg.listenAddress}"
39 "-alertmanager.notification-queue-capacity=${toString cfg.alertmanagerNotificationQueueCapacity}"
40 "-alertmanager.timeout=${toString cfg.alertmanagerTimeout}s"
41 (optionalString (cfg.alertmanagerURL != []) "-alertmanager.url=${concatStringsSep "," cfg.alertmanagerURL}")
42 ];
43
44 promTypes.globalConfig = types.submodule {
45 options = {
46 scrape_interval = mkOption {
47 type = types.str;
48 default = "1m";
49 description = ''
50 How frequently to scrape targets by default.
51 '';
52 };
53
54 scrape_timeout = mkOption {
55 type = types.str;
56 default = "10s";
57 description = ''
58 How long until a scrape request times out.
59 '';
60 };
61
62 evaluation_interval = mkOption {
63 type = types.str;
64 default = "1m";
65 description = ''
66 How frequently to evaluate rules by default.
67 '';
68 };
69 };
70 };
71
72 promTypes.scrape_config = types.submodule {
73 options = {
74 job_name = mkOption {
75 type = types.str;
76 description = ''
77 The job name assigned to scraped metrics by default.
78 '';
79 };
80 scrape_interval = mkOption {
81 type = types.nullOr types.str;
82 default = null;
83 description = ''
84 How frequently to scrape targets from this job. Defaults to the
85 globally configured default.
86 '';
87 };
88 scrape_timeout = mkOption {
89 type = types.nullOr types.str;
90 default = null;
91 description = ''
92 Per-target timeout when scraping this job. Defaults to the
93 globally configured default.
94 '';
95 };
96 metrics_path = mkOption {
97 type = types.str;
98 default = "/metrics";
99 description = ''
100 The HTTP resource path on which to fetch metrics from targets.
101 '';
102 };
103 scheme = mkOption {
104 type = types.enum ["http" "https"];
105 default = "http";
106 description = ''
107 The URL scheme with which to fetch metrics from targets.
108 '';
109 };
110 params = mkOption {
111 type = types.attrsOf (types.listOf types.str);
112 default = {};
113 description = ''
114 Optional HTTP URL parameters.
115 '';
116 };
117 basic_auth = mkOption {
118 type = types.nullOr (types.submodule {
119 options = {
120 username = mkOption {
121 type = types.str;
122 description = ''
123 HTTP username
124 '';
125 };
126 password = mkOption {
127 type = types.str;
128 description = ''
129 HTTP password
130 '';
131 };
132 };
133 });
134 default = null;
135 apply = x: mapNullable _filter x;
136 description = ''
137 Optional http login credentials for metrics scraping.
138 '';
139 };
140 dns_sd_configs = mkOption {
141 type = types.listOf promTypes.dns_sd_config;
142 default = [];
143 apply = x: map _filter x;
144 description = ''
145 List of DNS service discovery configurations.
146 '';
147 };
148 consul_sd_configs = mkOption {
149 type = types.listOf promTypes.consul_sd_config;
150 default = [];
151 apply = x: map _filter x;
152 description = ''
153 List of Consul service discovery configurations.
154 '';
155 };
156 file_sd_configs = mkOption {
157 type = types.listOf promTypes.file_sd_config;
158 default = [];
159 apply = x: map _filter x;
160 description = ''
161 List of file service discovery configurations.
162 '';
163 };
164 static_configs = mkOption {
165 type = types.listOf promTypes.static_config;
166 default = [];
167 apply = x: map _filter x;
168 description = ''
169 List of labeled target groups for this job.
170 '';
171 };
172 relabel_configs = mkOption {
173 type = types.listOf promTypes.relabel_config;
174 default = [];
175 apply = x: map _filter x;
176 description = ''
177 List of relabel configurations.
178 '';
179 };
180 };
181 };
182
183 promTypes.static_config = types.submodule {
184 options = {
185 targets = mkOption {
186 type = types.listOf types.str;
187 description = ''
188 The targets specified by the target group.
189 '';
190 };
191 labels = mkOption {
192 type = types.attrsOf types.str;
193 default = {};
194 description = ''
195 Labels assigned to all metrics scraped from the targets.
196 '';
197 };
198 };
199 };
200
201 promTypes.dns_sd_config = types.submodule {
202 options = {
203 names = mkOption {
204 type = types.listOf types.str;
205 description = ''
206 A list of DNS SRV record names to be queried.
207 '';
208 };
209 refresh_interval = mkOption {
210 type = types.str;
211 default = "30s";
212 description = ''
213 The time after which the provided names are refreshed.
214 '';
215 };
216 };
217 };
218
219 promTypes.consul_sd_config = types.submodule {
220 options = {
221 server = mkOption {
222 type = types.str;
223 description = "Consul server to query.";
224 };
225 token = mkOption {
226 type = types.nullOr types.str;
227 description = "Consul token";
228 };
229 datacenter = mkOption {
230 type = types.nullOr types.str;
231 description = "Consul datacenter";
232 };
233 scheme = mkOption {
234 type = types.nullOr types.str;
235 description = "Consul scheme";
236 };
237 username = mkOption {
238 type = types.nullOr types.str;
239 description = "Consul username";
240 };
241 password = mkOption {
242 type = types.nullOr types.str;
243 description = "Consul password";
244 };
245
246 services = mkOption {
247 type = types.listOf types.str;
248 description = ''
249 A list of services for which targets are retrieved.
250 '';
251 };
252 tag_separator = mkOption {
253 type = types.str;
254 default = ",";
255 description = ''
256 The string by which Consul tags are joined into the tag label.
257 '';
258 };
259 };
260 };
261
262 promTypes.file_sd_config = types.submodule {
263 options = {
264 files = mkOption {
265 type = types.listOf types.str;
266 description = ''
267 Patterns for files from which target groups are extracted. Refer
268 to the Prometheus documentation for permitted filename patterns
269 and formats.
270
271 '';
272 };
273 refresh_interval = mkOption {
274 type = types.str;
275 default = "30s";
276 description = ''
277 Refresh interval to re-read the files.
278 '';
279 };
280 };
281 };
282
283 promTypes.relabel_config = types.submodule {
284 options = {
285 source_labels = mkOption {
286 type = types.listOf types.str;
287 description = ''
288 The source labels select values from existing labels. Their content
289 is concatenated using the configured separator and matched against
290 the configured regular expression.
291 '';
292 };
293 separator = mkOption {
294 type = types.str;
295 default = ";";
296 description = ''
297 Separator placed between concatenated source label values.
298 '';
299 };
300 target_label = mkOption {
301 type = types.nullOr types.str;
302 default = null;
303 description = ''
304 Label to which the resulting value is written in a replace action.
305 It is mandatory for replace actions.
306 '';
307 };
308 regex = mkOption {
309 type = types.str;
310 default = "(.*)";
311 description = ''
312 Regular expression against which the extracted value is matched.
313 '';
314 };
315 replacement = mkOption {
316 type = types.str;
317 default = "$1";
318 description = ''
319 Replacement value against which a regex replace is performed if the
320 regular expression matches.
321 '';
322 };
323 action = mkOption {
324 type = types.enum ["replace" "keep" "drop"];
325 default = "replace";
326 description = ''
327 Action to perform based on regex matching.
328 '';
329 };
330 };
331 };
332
333in {
334 options = {
335 services.prometheus = {
336
337 enable = mkOption {
338 type = types.bool;
339 default = false;
340 description = ''
341 Enable the Prometheus monitoring daemon.
342 '';
343 };
344
345 listenAddress = mkOption {
346 type = types.str;
347 default = "0.0.0.0:9090";
348 description = ''
349 Address to listen on for the web interface, API, and telemetry.
350 '';
351 };
352
353 dataDir = mkOption {
354 type = types.path;
355 default = "/var/lib/prometheus";
356 description = ''
357 Directory to store Prometheus metrics data.
358 '';
359 };
360
361 extraFlags = mkOption {
362 type = types.listOf types.str;
363 default = [];
364 description = ''
365 Extra commandline options when launching Prometheus.
366 '';
367 };
368
369 configText = mkOption {
370 type = types.nullOr types.lines;
371 default = null;
372 description = ''
373 If non-null, this option defines the text that is written to
374 prometheus.yml. If null, the contents of prometheus.yml is generated
375 from the structured config options.
376 '';
377 };
378
379 globalConfig = mkOption {
380 type = promTypes.globalConfig;
381 default = {};
382 apply = _filter;
383 description = ''
384 Parameters that are valid in all configuration contexts. They
385 also serve as defaults for other configuration sections
386 '';
387 };
388
389 rules = mkOption {
390 type = types.listOf types.str;
391 default = [];
392 description = ''
393 Alerting and/or Recording rules to evaluate at runtime.
394 '';
395 };
396
397 ruleFiles = mkOption {
398 type = types.listOf types.path;
399 default = [];
400 description = ''
401 Any additional rules files to include in this configuration.
402 '';
403 };
404
405 scrapeConfigs = mkOption {
406 type = types.listOf promTypes.scrape_config;
407 default = [];
408 apply = x: map _filter x;
409 description = ''
410 A list of scrape configurations.
411 '';
412 };
413
414 alertmanagerURL = mkOption {
415 type = types.listOf types.str;
416 default = [];
417 description = ''
418 List of Alertmanager URLs to send notifications to.
419 '';
420 };
421
422 alertmanagerNotificationQueueCapacity = mkOption {
423 type = types.int;
424 default = 10000;
425 description = ''
426 The capacity of the queue for pending alert manager notifications.
427 '';
428 };
429
430 alertmanagerTimeout = mkOption {
431 type = types.int;
432 default = 10;
433 description = ''
434 Alert manager HTTP API timeout (in seconds).
435 '';
436 };
437 };
438 };
439
440 config = mkIf cfg.enable {
441 users.extraGroups.${promGroup}.gid = config.ids.gids.prometheus;
442 users.extraUsers.${promUser} = {
443 description = "Prometheus daemon user";
444 uid = config.ids.uids.prometheus;
445 group = promGroup;
446 home = cfg.dataDir;
447 createHome = true;
448 };
449 systemd.services.prometheus = {
450 wantedBy = [ "multi-user.target" ];
451 after = [ "network.target" ];
452 script = ''
453 #!/bin/sh
454 exec ${pkgs.prometheus}/bin/prometheus \
455 ${concatStringsSep " \\\n " cmdlineArgs}
456 '';
457 serviceConfig = {
458 User = promUser;
459 Restart = "always";
460 WorkingDirectory = cfg.dataDir;
461 };
462 };
463 };
464}