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}