1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.thanos;
7
8 nullOpt = type: description: mkOption {
9 type = types.nullOr type;
10 default = null;
11 description = lib.mdDoc description;
12 };
13
14 optionToArgs = opt: v : optional (v != null) ''--${opt}="${toString v}"'';
15 flagToArgs = opt: v : optional v "--${opt}";
16 listToArgs = opt: vs : map (v: ''--${opt}="${v}"'') vs;
17 attrsToArgs = opt: kvs: mapAttrsToList (k: v: ''--${opt}=${k}=\"${v}\"'') kvs;
18
19 mkParamDef = type: default: description: mkParam type (description + ''
20
21 Defaults to `${toString default}` in Thanos
22 when set to `null`.
23 '');
24
25 mkParam = type: description: {
26 toArgs = optionToArgs;
27 option = nullOpt type description;
28 };
29
30 mkFlagParam = description: {
31 toArgs = flagToArgs;
32 option = mkOption {
33 type = types.bool;
34 default = false;
35 description = lib.mdDoc description;
36 };
37 };
38
39 mkListParam = opt: description: {
40 toArgs = _opt: listToArgs opt;
41 option = mkOption {
42 type = types.listOf types.str;
43 default = [];
44 description = lib.mdDoc description;
45 };
46 };
47
48 mkAttrsParam = opt: description: {
49 toArgs = _opt: attrsToArgs opt;
50 option = mkOption {
51 type = types.attrsOf types.str;
52 default = {};
53 description = lib.mdDoc description;
54 };
55 };
56
57 mkStateDirParam = opt: default: description: {
58 toArgs = _opt: stateDir: optionToArgs opt "/var/lib/${stateDir}";
59 option = mkOption {
60 type = types.str;
61 inherit default;
62 description = lib.mdDoc description;
63 };
64 };
65
66 toYAML = name: attrs: pkgs.runCommand name {
67 preferLocalBuild = true;
68 json = builtins.toFile "${name}.json" (builtins.toJSON attrs);
69 nativeBuildInputs = [ pkgs.remarshal ];
70 } "json2yaml -i $json -o $out";
71
72 thanos = cmd: "${cfg.package}/bin/thanos ${cmd}" +
73 (let args = cfg.${cmd}.arguments;
74 in optionalString (length args != 0) (" \\\n " +
75 concatStringsSep " \\\n " args));
76
77 argumentsOf = cmd: concatLists (collect isList
78 (flip mapParamsRecursive params.${cmd} (path: param:
79 let opt = concatStringsSep "." path;
80 v = getAttrFromPath path cfg.${cmd};
81 in param.toArgs opt v)));
82
83 mkArgumentsOption = cmd: mkOption {
84 type = types.listOf types.str;
85 default = argumentsOf cmd;
86 defaultText = literalMD ''
87 calculated from `config.services.thanos.${cmd}`
88 '';
89 description = lib.mdDoc ''
90 Arguments to the `thanos ${cmd}` command.
91
92 Defaults to a list of arguments formed by converting the structured
93 options of {option}`services.thanos.${cmd}` to a list of arguments.
94
95 Overriding this option will cause none of the structured options to have
96 any effect. So only set this if you know what you're doing!
97 '';
98 };
99
100 mapParamsRecursive =
101 let noParam = attr: !(attr ? toArgs && attr ? option);
102 in mapAttrsRecursiveCond noParam;
103
104 paramsToOptions = mapParamsRecursive (_path: param: param.option);
105
106 params = {
107
108 log = {
109
110 log.level = mkParamDef (types.enum ["debug" "info" "warn" "error" "fatal"]) "info" ''
111 Log filtering level.
112 '';
113
114 log.format = mkParam types.str ''
115 Log format to use.
116 '';
117 };
118
119 tracing = cfg: {
120 tracing.config-file = {
121 toArgs = _opt: path: optionToArgs "tracing.config-file" path;
122 option = mkOption {
123 type = with types; nullOr str;
124 default = if cfg.tracing.config == null then null
125 else toString (toYAML "tracing.yaml" cfg.tracing.config);
126 defaultText = literalExpression ''
127 if config.services.thanos.<cmd>.tracing.config == null then null
128 else toString (toYAML "tracing.yaml" config.services.thanos.<cmd>.tracing.config);
129 '';
130 description = lib.mdDoc ''
131 Path to YAML file that contains tracing configuration.
132
133 See format details: <https://thanos.io/tracing.md/#configuration>
134 '';
135 };
136 };
137
138 tracing.config =
139 {
140 toArgs = _opt: _attrs: [];
141 option = nullOpt types.attrs ''
142 Tracing configuration.
143
144 When not `null` the attribute set gets converted to
145 a YAML file and stored in the Nix store. The option
146 {option}`tracing.config-file` will default to its path.
147
148 If {option}`tracing.config-file` is set this option has no effect.
149
150 See format details: <https://thanos.io/tracing.md/#configuration>
151 '';
152 };
153 };
154
155 common = cfg: params.log // params.tracing cfg // {
156
157 http-address = mkParamDef types.str "0.0.0.0:10902" ''
158 Listen `host:port` for HTTP endpoints.
159 '';
160
161 grpc-address = mkParamDef types.str "0.0.0.0:10901" ''
162 Listen `ip:port` address for gRPC endpoints (StoreAPI).
163
164 Make sure this address is routable from other components.
165 '';
166
167 grpc-server-tls-cert = mkParam types.str ''
168 TLS Certificate for gRPC server, leave blank to disable TLS
169 '';
170
171 grpc-server-tls-key = mkParam types.str ''
172 TLS Key for the gRPC server, leave blank to disable TLS
173 '';
174
175 grpc-server-tls-client-ca = mkParam types.str ''
176 TLS CA to verify clients against.
177
178 If no client CA is specified, there is no client verification on server side.
179 (tls.NoClientCert)
180 '';
181 };
182
183 objstore = cfg: {
184
185 objstore.config-file = {
186 toArgs = _opt: path: optionToArgs "objstore.config-file" path;
187 option = mkOption {
188 type = with types; nullOr str;
189 default = if cfg.objstore.config == null then null
190 else toString (toYAML "objstore.yaml" cfg.objstore.config);
191 defaultText = literalExpression ''
192 if config.services.thanos.<cmd>.objstore.config == null then null
193 else toString (toYAML "objstore.yaml" config.services.thanos.<cmd>.objstore.config);
194 '';
195 description = lib.mdDoc ''
196 Path to YAML file that contains object store configuration.
197
198 See format details: <https://thanos.io/storage.md/#configuration>
199 '';
200 };
201 };
202
203 objstore.config =
204 {
205 toArgs = _opt: _attrs: [];
206 option = nullOpt types.attrs ''
207 Object store configuration.
208
209 When not `null` the attribute set gets converted to
210 a YAML file and stored in the Nix store. The option
211 {option}`objstore.config-file` will default to its path.
212
213 If {option}`objstore.config-file` is set this option has no effect.
214
215 See format details: <https://thanos.io/storage.md/#configuration>
216 '';
217 };
218 };
219
220 sidecar = params.common cfg.sidecar // params.objstore cfg.sidecar // {
221
222 prometheus.url = mkParamDef types.str "http://localhost:9090" ''
223 URL at which to reach Prometheus's API.
224
225 For better performance use local network.
226 '';
227
228 tsdb.path = {
229 toArgs = optionToArgs;
230 option = mkOption {
231 type = types.str;
232 default = "/var/lib/${config.services.prometheus.stateDir}/data";
233 defaultText = literalExpression ''"/var/lib/''${config.services.prometheus.stateDir}/data"'';
234 description = lib.mdDoc ''
235 Data directory of TSDB.
236 '';
237 };
238 };
239
240 reloader.config-file = mkParam types.str ''
241 Config file watched by the reloader.
242 '';
243
244 reloader.config-envsubst-file = mkParam types.str ''
245 Output file for environment variable substituted config file.
246 '';
247
248 reloader.rule-dirs = mkListParam "reloader.rule-dir" ''
249 Rule directories for the reloader to refresh.
250 '';
251
252 };
253
254 store = params.common cfg.store // params.objstore cfg.store // {
255
256 stateDir = mkStateDirParam "data-dir" "thanos-store" ''
257 Data directory relative to `/var/lib`
258 in which to cache remote blocks.
259 '';
260
261 index-cache-size = mkParamDef types.str "250MB" ''
262 Maximum size of items held in the index cache.
263 '';
264
265 chunk-pool-size = mkParamDef types.str "2GB" ''
266 Maximum size of concurrently allocatable bytes for chunks.
267 '';
268
269 store.grpc.series-sample-limit = mkParamDef types.int 0 ''
270 Maximum amount of samples returned via a single Series call.
271
272 `0` means no limit.
273
274 NOTE: for efficiency we take 120 as the number of samples in chunk (it
275 cannot be bigger than that), so the actual number of samples might be
276 lower, even though the maximum could be hit.
277 '';
278
279 store.grpc.series-max-concurrency = mkParamDef types.int 20 ''
280 Maximum number of concurrent Series calls.
281 '';
282
283 sync-block-duration = mkParamDef types.str "3m" ''
284 Repeat interval for syncing the blocks between local and remote view.
285 '';
286
287 block-sync-concurrency = mkParamDef types.int 20 ''
288 Number of goroutines to use when syncing blocks from object storage.
289 '';
290
291 min-time = mkParamDef types.str "0000-01-01T00:00:00Z" ''
292 Start of time range limit to serve.
293
294 Thanos Store serves only metrics, which happened later than this
295 value. Option can be a constant time in RFC3339 format or time duration
296 relative to current time, such as -1d or 2h45m. Valid duration units are
297 ms, s, m, h, d, w, y.
298 '';
299
300 max-time = mkParamDef types.str "9999-12-31T23:59:59Z" ''
301 End of time range limit to serve.
302
303 Thanos Store serves only blocks, which happened earlier than this
304 value. Option can be a constant time in RFC3339 format or time duration
305 relative to current time, such as -1d or 2h45m. Valid duration units are
306 ms, s, m, h, d, w, y.
307 '';
308 };
309
310 query = params.common cfg.query // {
311
312 grpc-client-tls-secure = mkFlagParam ''
313 Use TLS when talking to the gRPC server
314 '';
315
316 grpc-client-tls-cert = mkParam types.str ''
317 TLS Certificates to use to identify this client to the server
318 '';
319
320 grpc-client-tls-key = mkParam types.str ''
321 TLS Key for the client's certificate
322 '';
323
324 grpc-client-tls-ca = mkParam types.str ''
325 TLS CA Certificates to use to verify gRPC servers
326 '';
327
328 grpc-client-server-name = mkParam types.str ''
329 Server name to verify the hostname on the returned gRPC certificates.
330 See <https://tools.ietf.org/html/rfc4366#section-3.1>
331 '';
332
333 web.route-prefix = mkParam types.str ''
334 Prefix for API and UI endpoints.
335
336 This allows thanos UI to be served on a sub-path. This option is
337 analogous to {option}`web.route-prefix` of Promethus.
338 '';
339
340 web.external-prefix = mkParam types.str ''
341 Static prefix for all HTML links and redirect URLs in the UI query web
342 interface.
343
344 Actual endpoints are still served on / or the
345 {option}`web.route-prefix`. This allows thanos UI to be served
346 behind a reverse proxy that strips a URL sub-path.
347 '';
348
349 web.prefix-header = mkParam types.str ''
350 Name of HTTP request header used for dynamic prefixing of UI links and
351 redirects.
352
353 This option is ignored if the option
354 `web.external-prefix` is set.
355
356 Security risk: enable this option only if a reverse proxy in front of
357 thanos is resetting the header.
358
359 The setting `web.prefix-header="X-Forwarded-Prefix"`
360 can be useful, for example, if Thanos UI is served via Traefik reverse
361 proxy with `PathPrefixStrip` option enabled, which
362 sends the stripped prefix value in `X-Forwarded-Prefix`
363 header. This allows thanos UI to be served on a sub-path.
364 '';
365
366 query.timeout = mkParamDef types.str "2m" ''
367 Maximum time to process query by query node.
368 '';
369
370 query.max-concurrent = mkParamDef types.int 20 ''
371 Maximum number of queries processed concurrently by query node.
372 '';
373
374 query.replica-label = mkParam types.str ''
375 Label to treat as a replica indicator along which data is
376 deduplicated.
377
378 Still you will be able to query without deduplication using
379 `dedup=false` parameter.
380 '';
381
382 selector-labels = mkAttrsParam "selector-label" ''
383 Query selector labels that will be exposed in info endpoint.
384 '';
385
386 store.addresses = mkListParam "store" ''
387 Addresses of statically configured store API servers.
388
389 The scheme may be prefixed with `dns+` or
390 `dnssrv+` to detect store API servers through
391 respective DNS lookups.
392 '';
393
394 store.sd-files = mkListParam "store.sd-files" ''
395 Path to files that contain addresses of store API servers. The path
396 can be a glob pattern.
397 '';
398
399 store.sd-interval = mkParamDef types.str "5m" ''
400 Refresh interval to re-read file SD files. It is used as a resync fallback.
401 '';
402
403 store.sd-dns-interval = mkParamDef types.str "30s" ''
404 Interval between DNS resolutions.
405 '';
406
407 store.unhealthy-timeout = mkParamDef types.str "5m" ''
408 Timeout before an unhealthy store is cleaned from the store UI page.
409 '';
410
411 query.auto-downsampling = mkFlagParam ''
412 Enable automatic adjustment (step / 5) to what source of data should
413 be used in store gateways if no
414 `max_source_resolution` param is specified.
415 '';
416
417 query.partial-response = mkFlagParam ''
418 Enable partial response for queries if no
419 `partial_response` param is specified.
420 '';
421
422 query.default-evaluation-interval = mkParamDef types.str "1m" ''
423 Set default evaluation interval for sub queries.
424 '';
425
426 store.response-timeout = mkParamDef types.str "0ms" ''
427 If a Store doesn't send any data in this specified duration then a
428 Store will be ignored and partial data will be returned if it's
429 enabled. `0` disables timeout.
430 '';
431 };
432
433 rule = params.common cfg.rule // params.objstore cfg.rule // {
434
435 labels = mkAttrsParam "label" ''
436 Labels to be applied to all generated metrics.
437
438 Similar to external labels for Prometheus,
439 used to identify ruler and its blocks as unique source.
440 '';
441
442 stateDir = mkStateDirParam "data-dir" "thanos-rule" ''
443 Data directory relative to `/var/lib`.
444 '';
445
446 rule-files = mkListParam "rule-file" ''
447 Rule files that should be used by rule manager. Can be in glob format.
448 '';
449
450 eval-interval = mkParamDef types.str "30s" ''
451 The default evaluation interval to use.
452 '';
453
454 tsdb.block-duration = mkParamDef types.str "2h" ''
455 Block duration for TSDB block.
456 '';
457
458 tsdb.retention = mkParamDef types.str "48h" ''
459 Block retention time on local disk.
460 '';
461
462 alertmanagers.urls = mkListParam "alertmanagers.url" ''
463 Alertmanager replica URLs to push firing alerts.
464
465 Ruler claims success if push to at least one alertmanager from
466 discovered succeeds. The scheme may be prefixed with
467 `dns+` or `dnssrv+` to detect
468 Alertmanager IPs through respective DNS lookups. The port defaults to
469 `9093` or the SRV record's value. The URL path is
470 used as a prefix for the regular Alertmanager API path.
471 '';
472
473 alertmanagers.send-timeout = mkParamDef types.str "10s" ''
474 Timeout for sending alerts to alertmanager.
475 '';
476
477 alert.query-url = mkParam types.str ''
478 The external Thanos Query URL that would be set in all alerts 'Source' field.
479 '';
480
481 alert.label-drop = mkListParam "alert.label-drop" ''
482 Labels by name to drop before sending to alertmanager.
483
484 This allows alert to be deduplicated on replica label.
485
486 Similar Prometheus alert relabelling
487 '';
488
489 web.route-prefix = mkParam types.str ''
490 Prefix for API and UI endpoints.
491
492 This allows thanos UI to be served on a sub-path.
493
494 This option is analogous to `--web.route-prefix` of Promethus.
495 '';
496
497 web.external-prefix = mkParam types.str ''
498 Static prefix for all HTML links and redirect URLs in the UI query web
499 interface.
500
501 Actual endpoints are still served on / or the
502 {option}`web.route-prefix`. This allows thanos UI to be served
503 behind a reverse proxy that strips a URL sub-path.
504 '';
505
506 web.prefix-header = mkParam types.str ''
507 Name of HTTP request header used for dynamic prefixing of UI links and
508 redirects.
509
510 This option is ignored if the option
511 {option}`web.external-prefix` is set.
512
513 Security risk: enable this option only if a reverse proxy in front of
514 thanos is resetting the header.
515
516 The header `X-Forwarded-Prefix` can be useful, for
517 example, if Thanos UI is served via Traefik reverse proxy with
518 `PathPrefixStrip` option enabled, which sends the
519 stripped prefix value in `X-Forwarded-Prefix`
520 header. This allows thanos UI to be served on a sub-path.
521 '';
522
523 query.addresses = mkListParam "query" ''
524 Addresses of statically configured query API servers.
525
526 The scheme may be prefixed with `dns+` or
527 `dnssrv+` to detect query API servers through
528 respective DNS lookups.
529 '';
530
531 query.sd-files = mkListParam "query.sd-files" ''
532 Path to file that contain addresses of query peers.
533 The path can be a glob pattern.
534 '';
535
536 query.sd-interval = mkParamDef types.str "5m" ''
537 Refresh interval to re-read file SD files. (used as a fallback)
538 '';
539
540 query.sd-dns-interval = mkParamDef types.str "30s" ''
541 Interval between DNS resolutions.
542 '';
543 };
544
545 compact = params.log // params.tracing cfg.compact // params.objstore cfg.compact // {
546
547 http-address = mkParamDef types.str "0.0.0.0:10902" ''
548 Listen `host:port` for HTTP endpoints.
549 '';
550
551 stateDir = mkStateDirParam "data-dir" "thanos-compact" ''
552 Data directory relative to `/var/lib`
553 in which to cache blocks and process compactions.
554 '';
555
556 consistency-delay = mkParamDef types.str "30m" ''
557 Minimum age of fresh (non-compacted) blocks before they are being
558 processed. Malformed blocks older than the maximum of consistency-delay
559 and 30m0s will be removed.
560 '';
561
562 retention.resolution-raw = mkParamDef types.str "0d" ''
563 How long to retain raw samples in bucket.
564
565 `0d` - disables this retention
566 '';
567
568 retention.resolution-5m = mkParamDef types.str "0d" ''
569 How long to retain samples of resolution 1 (5 minutes) in bucket.
570
571 `0d` - disables this retention
572 '';
573
574 retention.resolution-1h = mkParamDef types.str "0d" ''
575 How long to retain samples of resolution 2 (1 hour) in bucket.
576
577 `0d` - disables this retention
578 '';
579
580 startAt = {
581 toArgs = _opt: startAt: flagToArgs "wait" (startAt == null);
582 option = nullOpt types.str ''
583 When this option is set to a `systemd.time`
584 specification the Thanos compactor will run at the specified period.
585
586 When this option is `null` the Thanos compactor service
587 will run continuously. So it will not exit after all compactions have
588 been processed but wait for new work.
589 '';
590 };
591
592 downsampling.disable = mkFlagParam ''
593 Disables downsampling.
594
595 This is not recommended as querying long time ranges without
596 non-downsampled data is not efficient and useful e.g it is not possible
597 to render all samples for a human eye anyway
598 '';
599
600 block-sync-concurrency = mkParamDef types.int 20 ''
601 Number of goroutines to use when syncing block metadata from object storage.
602 '';
603
604 compact.concurrency = mkParamDef types.int 1 ''
605 Number of goroutines to use when compacting groups.
606 '';
607 };
608
609 downsample = params.log // params.tracing cfg.downsample // params.objstore cfg.downsample // {
610
611 stateDir = mkStateDirParam "data-dir" "thanos-downsample" ''
612 Data directory relative to `/var/lib`
613 in which to cache blocks and process downsamplings.
614 '';
615
616 };
617
618 receive = params.common cfg.receive // params.objstore cfg.receive // {
619
620 remote-write.address = mkParamDef types.str "0.0.0.0:19291" ''
621 Address to listen on for remote write requests.
622 '';
623
624 stateDir = mkStateDirParam "tsdb.path" "thanos-receive" ''
625 Data directory relative to `/var/lib` of TSDB.
626 '';
627
628 labels = mkAttrsParam "labels" ''
629 External labels to announce.
630
631 This flag will be removed in the future when handling multiple tsdb
632 instances is added.
633 '';
634
635 tsdb.retention = mkParamDef types.str "15d" ''
636 How long to retain raw samples on local storage.
637
638 `0d` - disables this retention
639 '';
640 };
641
642 };
643
644 assertRelativeStateDir = cmd: {
645 assertions = [
646 {
647 assertion = !hasPrefix "/" cfg.${cmd}.stateDir;
648 message =
649 "The option services.thanos.${cmd}.stateDir should not be an absolute directory." +
650 " It should be a directory relative to /var/lib.";
651 }
652 ];
653 };
654
655in {
656
657 options.services.thanos = {
658
659 package = mkOption {
660 type = types.package;
661 default = pkgs.thanos;
662 defaultText = literalExpression "pkgs.thanos";
663 description = lib.mdDoc ''
664 The thanos package that should be used.
665 '';
666 };
667
668 sidecar = paramsToOptions params.sidecar // {
669 enable = mkEnableOption
670 (lib.mdDoc "the Thanos sidecar for Prometheus server");
671 arguments = mkArgumentsOption "sidecar";
672 };
673
674 store = paramsToOptions params.store // {
675 enable = mkEnableOption
676 (lib.mdDoc "the Thanos store node giving access to blocks in a bucket provider.");
677 arguments = mkArgumentsOption "store";
678 };
679
680 query = paramsToOptions params.query // {
681 enable = mkEnableOption
682 (lib.mdDoc ("the Thanos query node exposing PromQL enabled Query API " +
683 "with data retrieved from multiple store nodes"));
684 arguments = mkArgumentsOption "query";
685 };
686
687 rule = paramsToOptions params.rule // {
688 enable = mkEnableOption
689 (lib.mdDoc ("the Thanos ruler service which evaluates Prometheus rules against" +
690 " given Query nodes, exposing Store API and storing old blocks in bucket"));
691 arguments = mkArgumentsOption "rule";
692 };
693
694 compact = paramsToOptions params.compact // {
695 enable = mkEnableOption
696 (lib.mdDoc "the Thanos compactor which continuously compacts blocks in an object store bucket");
697 arguments = mkArgumentsOption "compact";
698 };
699
700 downsample = paramsToOptions params.downsample // {
701 enable = mkEnableOption
702 (lib.mdDoc "the Thanos downsampler which continuously downsamples blocks in an object store bucket");
703 arguments = mkArgumentsOption "downsample";
704 };
705
706 receive = paramsToOptions params.receive // {
707 enable = mkEnableOption
708 (lib.mdDoc ("the Thanos receiver which accept Prometheus remote write API requests " +
709 "and write to local tsdb (EXPERIMENTAL, this may change drastically without notice)"));
710 arguments = mkArgumentsOption "receive";
711 };
712 };
713
714 config = mkMerge [
715
716 (mkIf cfg.sidecar.enable {
717 assertions = [
718 {
719 assertion = config.services.prometheus.enable;
720 message =
721 "Please enable services.prometheus when enabling services.thanos.sidecar.";
722 }
723 {
724 assertion = !(config.services.prometheus.globalConfig.external_labels == null ||
725 config.services.prometheus.globalConfig.external_labels == {});
726 message =
727 "services.thanos.sidecar requires uniquely identifying external labels " +
728 "to be configured in the Prometheus server. " +
729 "Please set services.prometheus.globalConfig.external_labels.";
730 }
731 ];
732 systemd.services.thanos-sidecar = {
733 wantedBy = [ "multi-user.target" ];
734 after = [ "network.target" "prometheus.service" ];
735 serviceConfig = {
736 User = "prometheus";
737 Restart = "always";
738 ExecStart = thanos "sidecar";
739 };
740 };
741 })
742
743 (mkIf cfg.store.enable (mkMerge [
744 (assertRelativeStateDir "store")
745 {
746 systemd.services.thanos-store = {
747 wantedBy = [ "multi-user.target" ];
748 after = [ "network.target" ];
749 serviceConfig = {
750 DynamicUser = true;
751 StateDirectory = cfg.store.stateDir;
752 Restart = "always";
753 ExecStart = thanos "store";
754 };
755 };
756 }
757 ]))
758
759 (mkIf cfg.query.enable {
760 systemd.services.thanos-query = {
761 wantedBy = [ "multi-user.target" ];
762 after = [ "network.target" ];
763 serviceConfig = {
764 DynamicUser = true;
765 Restart = "always";
766 ExecStart = thanos "query";
767 };
768 };
769 })
770
771 (mkIf cfg.rule.enable (mkMerge [
772 (assertRelativeStateDir "rule")
773 {
774 systemd.services.thanos-rule = {
775 wantedBy = [ "multi-user.target" ];
776 after = [ "network.target" ];
777 serviceConfig = {
778 DynamicUser = true;
779 StateDirectory = cfg.rule.stateDir;
780 Restart = "always";
781 ExecStart = thanos "rule";
782 };
783 };
784 }
785 ]))
786
787 (mkIf cfg.compact.enable (mkMerge [
788 (assertRelativeStateDir "compact")
789 {
790 systemd.services.thanos-compact =
791 let wait = cfg.compact.startAt == null; in {
792 wantedBy = [ "multi-user.target" ];
793 after = [ "network.target" ];
794 serviceConfig = {
795 Type = if wait then "simple" else "oneshot";
796 Restart = if wait then "always" else "no";
797 DynamicUser = true;
798 StateDirectory = cfg.compact.stateDir;
799 ExecStart = thanos "compact";
800 };
801 } // optionalAttrs (!wait) { inherit (cfg.compact) startAt; };
802 }
803 ]))
804
805 (mkIf cfg.downsample.enable (mkMerge [
806 (assertRelativeStateDir "downsample")
807 {
808 systemd.services.thanos-downsample = {
809 wantedBy = [ "multi-user.target" ];
810 after = [ "network.target" ];
811 serviceConfig = {
812 DynamicUser = true;
813 StateDirectory = cfg.downsample.stateDir;
814 Restart = "always";
815 ExecStart = thanos "downsample";
816 };
817 };
818 }
819 ]))
820
821 (mkIf cfg.receive.enable (mkMerge [
822 (assertRelativeStateDir "receive")
823 {
824 systemd.services.thanos-receive = {
825 wantedBy = [ "multi-user.target" ];
826 after = [ "network.target" ];
827 serviceConfig = {
828 DynamicUser = true;
829 StateDirectory = cfg.receive.stateDir;
830 Restart = "always";
831 ExecStart = thanos "receive";
832 };
833 };
834 }
835 ]))
836
837 ];
838}