Monitoring
BoronDNS exposes health probes and a Prometheus-compatible /metrics endpoint on its management HTTP listener. All endpoints are plain HTTP/1.1, unauthenticated by default, and intended for private management networks or an orchestrator-side proxy.
Management HTTP listener
Configure the listener in your config:
[interfaces.mgmt]
bind_address = "127.0.0.1"
port = 8080
[health]
metrics_enabled = trueHealth probes
| Path | Success | Failure | Notes |
|---|---|---|---|
/livez | 200 | (no response or 5xx) | Alive while zones are loading and during graceful shutdown |
/readyz | 200 | 503 | Ready when ≥ 1 zone is ACTIVE and the process is not draining |
/healthz | 200 | 503 | Alias for /readyz |
These endpoints are never rate-limited.
/livez response
{"status":"alive","version":"0.2.0","uptime_seconds":12345}/readyz responses
Ready:
{"status":"ready","version":"0.2.0","zones_active":5,"zones_loading":0,"zones_expired":0}
Not ready (zones still loading):
{"status":"not-ready","reason":"loading","version":"0.2.0","zones_active":0,"zones_loading":3,"zones_expired":0}
Draining (graceful shutdown in progress):
{"status":"draining","version":"0.2.0","grace_period_remaining_seconds":15}Prometheus metrics
/metrics emits standard Prometheus text exposition (text/plain; version=0.0.4). The endpoint supports gzip compression when requested.
Scrape configuration:
scrape_configs:
- job_name: borondns
static_configs:
- targets: ['localhost:8080']
scrape_interval: 15s
metrics_path: /metricsAvailable metric families
Zone state (per-zone gauges):
| Metric | Type | Description |
|---|---|---|
borondns_secondary_zone_state{zone="...",state="active|loading|expired"} | gauge | 1 if zone is in this state |
borondns_secondary_zone_soa_serial{zone="..."} | gauge | Current served SOA serial |
borondns_secondary_zone_last_refresh_seconds{zone="..."} | gauge | Seconds since last successful transfer |
borondns_secondary_zone_next_refresh_seconds{zone="..."} | gauge | Seconds until next scheduled refresh |
borondns_secondary_zone_refresh_failures{zone="..."} | counter | Transfer failure count since startup |
borondns_secondary_zone_loading_seconds{zone="..."} | gauge | Seconds the zone has been in LOADING state |
Query counters:
| Metric | Type | Description |
|---|---|---|
borondns_secondary_queries_total{zone="..."} | counter | Authoritative queries answered per zone |
borondns_transfer_sessions_started_total | counter | Total AXFR/IXFR sessions started |
borondns_transfer_sessions_completed_total | counter | Total sessions completed successfully |
Catalog zones (when RFC 9432 catalog zones are configured):
| Metric | Type | Description |
|---|---|---|
borondns_catalog_member_info{catalog_zone="...",zone="...",managed="..."} | gauge | Catalog membership indicator |
Recommended alert rules
groups:
- name: borondns
rules:
- alert: BoronDNSZoneExpired
expr: borondns_secondary_zone_state{state="expired"} == 1
for: 0m
labels:
severity: critical
annotations:
summary: "Zone {{ $labels.zone }} has expired"
description: "BoronDNS cannot reach the primary and SOA expire time has passed."
- alert: BoronDNSZoneStale
expr: borondns_secondary_zone_last_refresh_seconds > 3600
for: 5m
labels:
severity: warning
annotations:
summary: "Zone {{ $labels.zone }} has not refreshed in over 1 hour"
- alert: BoronDNSDown
expr: up{job="borondns"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "BoronDNS instance {{ $labels.instance }} is unreachable"Structured logs
Set format = "json" in [log] to emit structured log lines:
{"timestamp":"2026-06-15T10:23:01.452Z","level":"info","message":"zone transfer complete","zone":"example.com.","type":"IXFR","records_changed":3,"duration_ms":42}
{"timestamp":"2026-06-15T10:24:00.001Z","level":"warn","message":"zone refresh failed","zone":"old.example.org.","error":"connection refused","retry_in":"10s"}Observability API
BoronDNS optionally exposes a richer JSON observability API for management-network consumers. Enable it with:
[observability]
enabled = true
path_prefix = "/observability/v1"
Key endpoints include:
| Path | Description |
|---|---|
/observability/v1/summary | One-page runtime, zone, transfer, and security summary |
/observability/v1/zones | Per-zone state, serial, refresh, and transfer details |
/observability/v1/transfers | Current and recent transfer session state |
/observability/v1/security | TSIG, NOTIFY, RRL, and DNS Cookie counters |
/observability/v1/runtime | Process uptime, version, worker mode, and drain state |
/observability/v1/resources | Coarse memory, CPU, file-descriptor, and filesystem view |
All observability endpoints are GET-only, rate-limited (default: 60 req/min per source), and must not be exposed on the public DNS listener address. For deployments where the management listener is reachable beyond localhost, set a bearer token:
[observability]
enabled = true
bearer_token_file = "/etc/borondns-secondary/observability.token"
Secrets (TSIG values, private keys, tokens) are never returned by any observability endpoint.