nixos/prometheus-elasticsearch-exporter: init

Add a `services.prometheus.exporters.elasticsearch` module wrapping
`pkgs.prometheus-elasticsearch-exporter`, which also supports OpenSearch.

Credentials are kept out of the process arguments and the store via an
`environmentFile` carrying `ES_USERNAME`/`ES_PASSWORD` or `ES_API_KEY`,
which override any auth embedded in `--es.uri`. Collector toggles go
through the framework's `extraFlags`.

A NixOS test exercises the exporter against a single-node OpenSearch
instance.

Assisted-by: claude-code: claude-opus-4-8
(cherry picked from commit 7195e583ce)
This commit is contained in:
cinereal 2026-05-29 11:04:55 +02:00
commit b5ec7fd0fa
No known key found for this signature in database
4 changed files with 90 additions and 0 deletions

View file

@ -62,6 +62,7 @@ let
"domain"
"dovecot"
"ebpf"
"elasticsearch"
"fail2ban"
"fastly"
"flow"

View file

@ -0,0 +1,62 @@
{
config,
lib,
pkgs,
utils,
...
}:
let
inherit (lib)
mkIf
mkOption
types
;
inherit (utils) escapeSystemdExecArgs;
cfg = config.services.prometheus.exporters.elasticsearch;
in
{
port = 9114;
extraOpts = {
package = lib.mkPackageOption pkgs "prometheus-elasticsearch-exporter" { };
url = mkOption {
type = types.str;
default = "http://localhost:9200";
example = "https://localhost:9200";
description = ''
URI of the Elasticsearch (or OpenSearch) node to scrape, passed as
`--es.uri`. Any credentials embedded here are overridden by the
`ES_USERNAME`/`ES_PASSWORD` or `ES_API_KEY` environment variables when
{option}`environmentFile` is set.
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/secrets/elasticsearch-exporter.env";
description = ''
Path to an environment file, as defined in {manpage}`systemd.exec(5)`,
used to pass credentials to the exporter without exposing them in the
process arguments. It should contain either `ES_USERNAME` and
`ES_PASSWORD`, or `ES_API_KEY`.
'';
};
};
serviceOpts = {
serviceConfig = {
EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
ExecStart = escapeSystemdExecArgs (
[
(lib.getExe cfg.package)
"--web.listen-address=${cfg.listenAddress}:${toString cfg.port}"
"--es.uri=${cfg.url}"
]
++ cfg.extraFlags
);
};
};
}

View file

@ -436,6 +436,30 @@ let
'';
};
elasticsearch =
{ ... }:
{
exporterConfig = {
enable = true;
url = "http://localhost:9200";
};
metricProvider = {
# `services.elasticsearch` is unmaintained; OpenSearch is the same
# engine class and is explicitly supported by the exporter.
services.opensearch.enable = true;
virtualisation.memorySize = 2048;
};
exporterTest = ''
wait_for_unit("opensearch.service")
wait_for_open_port(9200)
wait_for_unit("prometheus-elasticsearch-exporter.service")
wait_for_open_port(9114)
succeed(
"curl -sSf localhost:9114/metrics | grep 'elasticsearch_cluster_health_status'"
)
'';
};
fail2ban =
{ ... }:
{

View file

@ -2,6 +2,7 @@
lib,
buildGoModule,
fetchFromGitHub,
nixosTests,
}:
buildGoModule (finalAttrs: {
pname = "elasticsearch_exporter";
@ -16,6 +17,8 @@ buildGoModule (finalAttrs: {
vendorHash = "sha256-8y0M1b34eJpuHOuXPemhB5kKwBSgU7cMFxOaIZFS/bo=";
passthru.tests = { inherit (nixosTests.prometheus-exporters) elasticsearch; };
meta = {
description = "Elasticsearch stats exporter for Prometheus";
mainProgram = "elasticsearch_exporter";