Use /run/current-system/sw/bin/nixos-version instead of relying on PATH, since the systemd service may not have the system binaries in its PATH. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
nixos-exporter
A Prometheus exporter for NixOS-specific metrics. Exposes system state information that standard exporters don't cover: generation management, flake input freshness, and upgrade status.
Installation
As a flake
{
inputs.nixos-exporter.url = "git+https://git.t-juice.club/torjus/nixos-exporter";
outputs = { self, nixpkgs, nixos-exporter, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
nixos-exporter.nixosModules.default
{
services.prometheus.exporters.nixos = {
enable = true;
flake = {
enable = true;
url = "github:myuser/myconfig";
};
};
}
];
};
};
}
Manual
nix build
./result/bin/nixos-exporter --listen=:9971
CLI Flags
| Flag | Default | Description |
|---|---|---|
--listen |
:9971 |
Address to listen on |
--collector.flake |
false |
Enable flake collector |
--flake.url |
Flake URL for revision comparison (required if flake collector enabled) | |
--flake.check-interval |
1h |
Interval between remote flake checks |
NixOS Module Options
services.prometheus.exporters.nixos = {
enable = true;
port = 9971;
listenAddress = "0.0.0.0";
openFirewall = false;
flake = {
enable = false;
url = ""; # Required if flake.enable = true
checkInterval = "1h";
};
};
Metrics
Generation Metrics (always enabled)
| Metric | Type | Description |
|---|---|---|
nixos_generation_count |
Gauge | Total number of system generations |
nixos_current_generation |
Gauge | Currently active generation number |
nixos_booted_generation |
Gauge | Generation that was booted |
nixos_generation_age_seconds |
Gauge | Age of current generation in seconds |
nixos_config_mismatch |
Gauge | 1 if booted generation differs from current |
Flake Metrics (optional)
| Metric | Type | Labels | Description |
|---|---|---|---|
nixos_flake_input_age_seconds |
Gauge | input |
Age of flake input in seconds |
nixos_flake_input_info |
Gauge | input, rev, type |
Info gauge with revision and type labels |
nixos_flake_info |
Gauge | current_rev, remote_rev, nixpkgs_rev, nixos_version |
Info gauge with system version details |
nixos_flake_revision_behind |
Gauge | 1 if current system revision differs from remote latest |
Setting Configuration Revision
For current_rev to show the flake's git revision, you must set system.configurationRevision in your flake:
{
outputs = { self, nixpkgs, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
{
system.configurationRevision = self.rev or self.dirtyRev or "dirty";
}
./configuration.nix
];
};
};
}
This sets the revision to:
self.rev- Git commit hash (only available when tree is clean and committed)self.dirtyRev- Dirty revision (e.g.,abc1234-dirty) when uncommitted changes exist"dirty"- Fallback when neither is available
Without this setting, current_rev will be "unknown" and nixos_flake_revision_behind will always be 0.
Example Prometheus Alerts
groups:
- name: nixos
rules:
- alert: NixOSConfigStale
expr: nixos_generation_age_seconds > 7 * 24 * 3600
for: 1h
labels:
severity: warning
annotations:
summary: "NixOS config on {{ $labels.instance }} is over 7 days old"
- alert: NixOSRebootRequired
expr: nixos_config_mismatch == 1
for: 24h
labels:
severity: info
annotations:
summary: "{{ $labels.instance }} needs reboot to apply config"
- alert: NixpkgsInputStale
expr: nixos_flake_input_age_seconds{input="nixpkgs"} > 30 * 24 * 3600
for: 1d
labels:
severity: info
annotations:
summary: "nixpkgs input on {{ $labels.instance }} is over 30 days old"
- alert: NixOSRevisionBehind
expr: nixos_flake_revision_behind == 1
for: 1h
labels:
severity: info
annotations:
summary: "{{ $labels.instance }} is behind remote flake revision"
Security Considerations
- The
/metricsendpoint exposes system state and revision information. Only expose it on internal networks. - Runs as non-root user; only reads symlinks and files that are world-readable.
- When using the flake collector, the exporter executes
nix flake metadatato fetch remote data.
Known Limitations
-
Flake input metrics (
nixos_flake_input_age_seconds,nixos_flake_input_info) reflect the remote flake state, not the currently deployed system. If the deployed system is behind, these will show newer data than what's actually deployed. -
The
nixos_flake_revision_behindmetric requiressystem.configurationRevisionto be set. Without it, the metric will always be 0 since there's no local revision to compare against.
License
MIT