checkfleet

Configuration

checkfleet reads a single YAML file (default checkfleet.yml, override with --config). A checkfleet.example.yml ships with the repo — copy it and adapt.

# checkfleet.yml
timeout_seconds: 30          # global deadline for a whole run (default 30)

checks:
  certs:
    warn_days: 30            # WARN when the cert expires within N days (default 30)
    crit_days: 7             # BAD  when it expires within N days (default 7)
    port: 443                # default port for targets/inventory hosts (default 443)
    targets:
      - example.com          # uses the default port
      - internal.example:8443
    ansible_inventory: /path/to/inventory   # optional: every host → target on `port`

  http:
    targets:
      - url: https://example.com/
        expect_status: 200   # expected HTTP status (default 200)
        max_latency_ms: 2000 # WARN if the response is slower than this
        expect_body: "ok"    # BAD if this substring is missing from the body

Top-level keys

Key Type Default Meaning
timeout_seconds int 30 Per-check (and per-attempt) deadline.
retries int 0 Retry a check that produced an ERROR finding (transient network/handshake), up to this many times.
retry_backoff_ms int 500 (when retries>0) Base backoff between attempts; doubles each retry.
max_concurrency int 0 (unbounded) Cap on how many checks run at once. Handy for large fleets so a run doesn’t open hundreds of connections at once. --max-concurrency overrides it.
labels map Global key/value labels attached to the outputs — see Global labels.
module_overrides map Per-module override of timeout_seconds/retries/retry_backoff_ms, keyed by module name. A zero field falls back to the global value.
include list Files or directories deep-merged under this config at load time — see Splitting config across files.
checks map One entry per module. A module runs only if its key is present.

Per-module tuning is handy when one module is slower or flakier than the rest — give a database check a shorter deadline and a couple of retries without changing the global settings:

timeout_seconds: 30
module_overrides:
  postgres: {timeout_seconds: 10, retries: 2}
  stream:   {timeout_seconds: 15}

A module that is not present in checks is skipped by check all, and check <name> for it fails with modulo "<name>" non configurato.

checks.certs

TLS certificate expiry. See Modules → certs.

Key Type Default Meaning
warn_days int 30 Days-to-expiry threshold for WARN.
crit_days int 7 Days-to-expiry threshold for BAD.
port int 443 Default port for targets and inventory hosts without an explicit :port.
targets list host or host:port entries.
ansible_inventory string Path to an Ansible INI inventory (file or directory). Every host becomes a target on port.

Targets and inventory hosts are merged and de-duplicated. At least one of targets / ansible_inventory should be set.

checks.http

HTTP probes. See Modules → http.

checks.http.targets is a list of:

Key Type Default Meaning
url string The URL to probe. Required.
expect_status int 200 Expected status code; a mismatch is BAD.
max_latency_ms int WARN if the response is slower. Omit to skip the latency check.
expect_body string BAD if this substring is absent from the body. Omit to skip.

checks.nats

NATS JetStream cluster health via the monitoring endpoints. See Modules → nats.

Key Type Default Meaning
targets list Monitoring endpoints as host or host:port.
port int 8222 Default monitoring port for targets/inventory hosts without one.
scheme string http http or https for the monitoring endpoint.
ansible_inventory string Ansible INI inventory; every host becomes a monitoring target on port.
expect_meta_leader string Expected meta-leader server_name; a mismatch is WARN.
expect_peers list Expected peer server_names. Unexpected members → WARN (ghost); expected-but-absent → BAD.
lag_warn int 100 Raft peer lag (entries) at/above which a peer is WARN.
lag_crit int 1000 Raft peer lag (entries) at/above which a peer is BAD.
checks:
  nats:
    port: 8222
    expect_meta_leader: nats-gw-01
    expect_peers: [nats-gw-01, nats-gw-02, nats-gw-03]
    lag_warn: 100
    lag_crit: 1000
    targets:
      - 10.21.10.18
      - 10.11.10.18:8222

checks.haproxy

HAProxy backend/server health via the CSV stats export. See Modules → haproxy.

Key Type Default Meaning
targets list Stats endpoints as host or host:port.
port int 8404 Default stats port for targets/inventory hosts without one.
scheme string http http or https.
path string /stats;csv Path of the CSV stats export.
ansible_inventory string Ansible INI inventory; every host becomes a stats target on port.
session_warn_pct int 0 (off) WARN when scur/slim reaches this percent.
auth_user string HTTP basic-auth user (optional).
auth_pass_env string Env var holding the basic-auth password. Never put the password in the config.
checks:
  haproxy:
    port: 8404
    path: /stats;csv
    session_warn_pct: 80
    auth_user: admin
    auth_pass_env: HAPROXY_STATS_PASS   # export HAPROXY_STATS_PASS=... in the environment
    targets:
      - 10.15.20.106:8404

checks.stream

HLS/DASH stream health from the manifest. See Modules → stream.

checks.stream.targets is a list of:

Key Type Default Meaning
url string Manifest URL: HLS .m3u8 (master or media) or DASH .mpd. Required.
name string the URL Display label for the findings.
min_variants int 0 (skip) Expected minimum ladder size (variants/representations).
live bool false Expect a live stream: check live-edge freshness, WARN if it’s VOD.
max_age_warn_seconds int 30 when live Live-edge age → WARN.
max_age_crit_seconds int 60 when live Live-edge age → BAD.
checks:
  stream:
    targets:
      - name: canale-live
        url: https://cdn.example/live/master.m3u8
        live: true
        min_variants: 3
      - name: vod-catalogo
        url: https://cdn.example/vod/movie/master.m3u8
        min_variants: 4

checks.patroni

Patroni-managed PostgreSQL cluster health via the Patroni REST API. See Modules → patroni.

Key Type Default Meaning
targets list Patroni REST endpoints as host or host:port.
port int 8008 Default API port for targets/inventory hosts without one.
scheme string http http or https.
ansible_inventory string Ansible INI inventory; every host becomes an API target on port.
lag_warn_bytes int 33554432 (32 MiB) Replica lag → WARN.
lag_crit_bytes int 134217728 (128 MiB) Replica lag → BAD.
checks:
  patroni:
    port: 8008
    lag_warn_bytes: 33554432
    lag_crit_bytes: 134217728
    targets:
      - 10.20.30.11
      - 10.20.30.12:8008

checks.consul

Consul cluster health via the HTTP API. See Modules → consul.

Key Type Default Meaning
targets list Consul HTTP API endpoints as host or host:port.
port int 8500 Default API port for targets/inventory hosts without one.
scheme string http http or https.
ansible_inventory string Ansible INI inventory; every host becomes an API target on port.
expect_peers int 0 (skip) Expected raft peers; below quorum → BAD, below expected → WARN.
token_env string Env var holding the ACL token (sent as X-Consul-Token). Never inline the token.
kv_keys list KV keys that must exist; a missing key is BAD.
checks:
  consul:
    port: 8500
    expect_peers: 3
    token_env: CONSUL_HTTP_TOKEN
    kv_keys:
      - config/checkfleet/enabled
    targets:
      - 10.20.30.11
      - 10.20.30.12:8500

checks.postgres

PostgreSQL health via read-only SQL. See Modules → postgres.

Top-level thresholds:

Key Type Default Meaning
lag_warn_bytes int 33554432 (32 MiB) Replica lag → WARN.
lag_crit_bytes int 134217728 (128 MiB) Replica lag → BAD.
conn_warn_pct int 80 WARN when connections reach this % of max_connections.
wraparound_warn_age int 1500000000 age(datfrozenxid) → WARN.
wraparound_crit_age int 1900000000 age(datfrozenxid) → BAD.
slot_warn_bytes int 536870912 (512 MiB) Inactive slot retained WAL → WARN.
slot_crit_bytes int 2147483648 (2 GiB) Inactive slot retained WAL → BAD.

checks.postgres.targets is a list of:

Key Type Default Meaning
dsn string libpq DSN or URL, without the password. Required.
name string the DSN Display label for the findings.
password_env string Env var holding the password. Never inline it.
checks:
  postgres:
    conn_warn_pct: 80
    targets:
      - name: pg-prod-primary
        dsn: "host=10.20.30.11 port=5432 user=monitor dbname=postgres sslmode=require"
        password_env: PG_PROD_PASS

The monitoring role needs only read access (it queries pg_stat_*, pg_database, pg_replication_slots, pg_settings).

checks.dns

DNS resolution health. See Modules → dns.

Top-level keys:

Key Type Default Meaning
resolvers list system Resolvers as host or host:port (default port 53). Empty → /etc/resolv.conf.
min_ttl_seconds int 0 (off) WARN when any answer’s TTL is below this.

checks.dns.targets is a list of:

Key Type Default Meaning
name string Domain to resolve. Required.
type string A Record type: A, AAAA, CNAME, NS, TXT, SOA.
expect list Expected value set; a different answer is BAD (drift). For SOA, compared against the serial.
checks:
  dns:
    resolvers: [10.20.30.53, 8.8.8.8]
    min_ttl_seconds: 30
    targets:
      - {name: example.com, type: A, expect: ["203.0.113.10"]}
      - {name: example.com, type: SOA}

Multi-stack profiles

--stack <name> overlays a per-stack file on the base config, so you can keep one set of defaults and a small override per environment. Given --config checkfleet.yml --stack prod, checkfleet loads checkfleet.yml then overlays checkfleet.prod.yml (same directory).

The merge is per module: a module present in the stack file replaces the base’s module entirely (so the module gets its own defaults again); a module absent from the stack is inherited from the base. Every module can be overridden this way. timeout_seconds is overridden only if the stack sets it. --stack works with both check and serve.

Compose several stacks by passing a comma-separated list — they overlay left-to-right, last wins, so you can layer environment on region on base:

checkfleet check all  --config checkfleet.yml --stack prod
checkfleet check all  --config checkfleet.yml --stack region-eu,prod   # prod wins
checkfleet serve      --config checkfleet.yml --stack region-eu,prod

Each stack file resolves its own include before it overlays the base.

# checkfleet.prod.yml — overrides only what differs from the base
checks:
  certs:
    targets: [edge.example.com]

Global labels

labels: attaches key/value metadata to a run — which environment, region, cluster — and carries it into the outputs for routing and dashboards:

labels:
  env: prod
  region: eu

Where they surface:

Output How labels appear
prometheus on every seriescheckfleet_finding_status{check="…",target="…",env="prod",region="eu"} (invalid label chars in a key become _).
json a top-level "labels": { … } object.
otlp as resource attributes on the metrics.
webhook (template) available as .Labels (e.g. ``).

Labels are operator metadata only — never secrets. Other formats (text, markdown, …) ignore them.

Splitting config across files (include)

A large fleet reads better split by team or service. include: pulls other files (or a whole directory) into one config at load time:

# checkfleet.yml
include:
  - conf.d/            # every *.yml / *.yaml in the directory, in sorted order
  - ../shared/dns.yml  # a single file, relative to THIS file
timeout_seconds: 30
checks:
  http:
    targets: [{url: https://app.example/health}]

Rules:

  • Paths are relative to the file doing the include (absolute paths work too). A directory contributes its *.yml/*.yaml entries, sorted by name — prefix them 10-, 20-, … to control order.
  • The merge is a deep merge: two files can each add different modules under checks:, and they combine. Redefining the same module (or any scalar/list like a module’s targets:) replaces it wholesale.
  • The including file wins over everything it includes; among includes, a later entry wins over an earlier one. Includes may nest.
  • ${…} interpolation runs per file, and an include cycle or a missing file is a clear load error.

include composes with --stack: the base and the stack file each resolve their own includes before the stack overlays the base.

checks.redis

Redis / Valkey health via INFO. See Modules → redis.

Key Type Default Meaning
targets list Endpoints as host or host:port.
port int 6379 Default port for targets/inventory hosts without one.
tls bool false Use TLS (rediss).
username string Optional ACL username.
password_env string Env var holding the password. Never inline it.
ansible_inventory string Ansible INI inventory; every host becomes a target on port.
mem_warn_pct int 80 WARN when used_memory reaches this % of maxmemory.
lag_warn_bytes int 16777216 (16 MiB) Replica offset lag → WARN.
lag_crit_bytes int 134217728 (128 MiB) Replica offset lag → BAD.
checks:
  redis:
    port: 6379
    password_env: REDIS_PASS
    mem_warn_pct: 80
    targets:
      - 10.20.30.40
      - 10.20.30.41:6380

checks.keycloak

Keycloak health via HTTP. See Modules → keycloak.

Key Type Default Meaning
base_url string Scheme + host (+ /auth prefix on old versions), no trailing slash.
health_url string Optional health endpoint (often on the management port :9000). Checked only when set.
realms list Realm names to verify via their OIDC discovery document.
checks:
  keycloak:
    base_url: https://auth.example.com
    health_url: https://auth.example.com:9000/health/ready
    realms: [main, partners]

checks.tcp

Generic TCP reachability. See Modules → tcp.

checks.tcp.targets is a list of:

Key Type Default Meaning
address string host:port to connect to. Required.
name string the address Display label.
tls bool false TLS handshake instead of a plain connect.
expect_banner string Substring the server banner must contain.
max_latency_ms int WARN if the connect is slower.
checks:
  tcp:
    targets:
      - {name: ssh, address: 10.20.30.9:22, expect_banner: "SSH-2.0"}
      - {name: rtmp, address: ingest.example.com:1935}

checks.tls

Deep TLS check. See Modules → tls.

Key Type Default Meaning
targets list host or host:port (default 443).
port int 443 Default port.
warn_days int 30 Leaf expiry → WARN.
crit_days int 7 Leaf expiry → BAD.
ansible_inventory string Ansible INI inventory; every host becomes a target.
checks:
  tls:
    targets: [auth.example.com, api.example.com:8443]

checks.ntp

NTP clock offset. See Modules → ntp.

Key Type Default Meaning
targets list host or host:port (default 123).
port int 123 Default port.
offset_warn_ms int 100 |offset| → WARN.
offset_crit_ms int 1000 |offset| → BAD.
checks:
  ntp:
    targets: [time.example.com, 0.pool.ntp.org]

checks.rabbitmq

RabbitMQ management API. See Modules → rabbitmq.

Key Type Default Meaning
targets list Management API endpoints host or host:port.
port int 15672 Default management port.
scheme string http http or https.
username string guest Basic-auth user.
password_env string Env var holding the password. Never inline.
queue_warn_depth int 1000 Queue messages → WARN.
queue_crit_depth int 50000 Queue messages → BAD.
checks:
  rabbitmq:
    username: monitoring
    password_env: RABBITMQ_PASS
    targets: [10.20.30.60]

checks.grpc

gRPC health checking (TLS/h2). See Modules → grpc.

checks.grpc.targets is a list of:

Key Type Default Meaning
address string host:port of the gRPC TLS endpoint. Required.
name string address Display label.
service string gRPC service to check; empty = whole-server.
insecure_skip_verify bool false Skip TLS cert verification (internal self-signed).
checks:
  grpc:
    targets:
      - {name: api, address: api.example.com:443, service: example.api.v1.API}

checks.ldap

LDAP bind + search. See Modules → ldap.

checks.ldap.targets is a list of:

Key Type Default Meaning
url string ldap://host:389 or ldaps://host:636. Required.
name string url Display label.
start_tls bool false StartTLS on a plain connection.
insecure_skip_verify bool false Skip TLS cert verification.
bind_dn string Bind DN; empty = anonymous.
password_env string Env var with the bind password. Never inline.
base_dn string Search base for the sanity search.
filter string (objectClass=*) Search filter.
min_entries int 1 (when base_dn set) Minimum results, else BAD.

checks.kafka

Kafka cluster health. See Modules → kafka.

Key Type Default Meaning
brokers list Seed brokers host:port. Required.
tls bool false Dial over TLS.
sasl_user string SASL username (enables SASL).
sasl_mechanism string plain plain, scram-sha-256, scram-sha-512.
sasl_password_env string Env var with the SASL password. Never inline.
expect_brokers int 0 Fewer brokers than this → WARN.
groups list Consumer groups whose total lag to check.
lag_warn int 1000 Group lag → WARN.
lag_crit int 100000 Group lag → BAD.
checks:
  kafka:
    brokers: [10.20.30.70:9092]
    expect_brokers: 3
    groups: [ingest-consumers]

No secrets in config

Keep credentials out of checkfleet.yml — checks never log or echo secrets, and example/config files must stay clean.

Dynamic values & secrets

Config values support ${…} interpolation, expanded before the file is parsed:

Token Expands to
${VAR} environment variable VAR (empty if unset)
${VAR:-default} VAR, or default when unset/empty
${file:/path} the trimmed contents of a file — Docker/Kubernetes secrets
timeout_seconds: ${CF_TIMEOUT:-30}
checks:
  redis:
    targets: ["${REDIS_HOST}:6379"]
    password_env: REDIS_PASSWORD          # module secrets still come from env…
  postgres:
    targets:
      - name: primary
        dsn: "postgres://app:${file:/run/secrets/pg_password}@db:5432/app"

A missing ${file:…} is a hard error. Use $${ for a literal ${. This keeps secrets out of checkfleet.yml while staying friendly to *_env module fields.

Maintenance windows

Suppress or downgrade findings during planned work so they don’t page. Each window matches by check/target glob (empty = all) and an optional from/to (RFC3339) range; the first matching, active window wins.

maintenance:
  - check: postgres            # mute (drop) all postgres findings in the range
    from: 2026-08-01T22:00:00Z
    to:   2026-08-01T23:30:00Z
  - target: "cdn.*"            # keep visible but cap BAD/ERROR at WARN
    action: warn               # message gets a " [maintenance]" note

action: mute (default) drops the finding; action: warn caps BAD/ERROR at WARN. Applies to check (before --exit-on-bad) and to serve.

Recurring windows — add daily: "HH:MM-HH:MM" (local clock, wraps past midnight) for a window that repeats every day, optionally restricted to weekdays. from/to still bound the overall validity (e.g. a nightly window only for one month).

maintenance:
  - check: postgres            # every night 01:00–03:00 local, muted
    daily: "01:00-03:00"
  - target: "cdn.*"            # only on weekends, capped at WARN
    daily: "00:00-23:59"
    weekdays: [Sat, Sun]
    action: warn