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