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