2026-08-20 09:32:04 +02:00
2026-08-20 09:32:04 +02:00
2026-08-20 09:32:04 +02:00
2026-08-20 09:32:04 +02:00
2026-08-20 09:32:04 +02:00
2026-08-20 09:32:04 +02:00
2026-08-20 09:32:04 +02:00
2026-08-20 09:32:04 +02:00
2026-08-20 09:32:04 +02:00

uptime-monitor

A small single-container Python 3 uptime monitor.

  • Monitors a configurable list of HTTP/HTTPS endpoints.
  • Each endpoint independently specifies: scheme (http/https), port, the DNS server used to resolve it, and the expected HTTP status code.
  • DNS resolution never uses the system/router-provided resolver — every lookup is sent explicitly and only to the DNS server configured for that endpoint (see "How DNS enforcement works" below).
  • Runs a full rolling pass over all endpoints every 10 minutes (configurable).
  • If any endpoint fails, all endpoints are immediately re-checked once, instead of waiting for the next scheduled pass.
  • Writes every check result to an InfluxDB 2.x bucket (uptime) over HTTPS, with certificate verification disabled for InfluxDB specifically (self-signed cert).
  • Everything — endpoint list, InfluxDB URL/token/org/bucket, intervals — lives in one YAML file that's bind-mounted into the container. Edit it and restart the container; no image rebuild required.

Setup

cp config.example.yaml config.yaml
# edit config.yaml: fill in your InfluxDB url/token/org, and your endpoints
docker compose up -d --build

Editing endpoints / InfluxDB settings later

vim config.yaml
docker compose restart uptime-monitor

No rebuild needed — config.yaml is bind-mounted (docker-compose.yml), never baked into the image (see .dockerignore).

Config reference (config.yaml)

influx:
  url: "https://influxdb.example.local:8086"
  token: "..."          # InfluxDB 2.x API token with write access
  org: "..."
  bucket: "uptime"
  verify_ssl: false      # keep false: influx uses a self-signed cert

check:
  interval_seconds: 600                  # 10 min rolling interval
  timeout_seconds: 10                     # default per-check timeout
  immediate_recheck_delay_seconds: 5      # pause before the failure-triggered recheck

endpoints:
  - name: "my-service"
    host: "service.example.com"
    scheme: "https"
    port: 443
    dns_server: "1.1.1.1"    # required, per-endpoint — no default DNS is ever used
    expected_status: 200
    path: "/"                # optional, default "/"
    timeout_seconds: 5       # optional, overrides check.timeout_seconds
    verify_tls: true         # optional, set false if THIS endpoint has a self-signed cert

Add/remove/edit entries under endpoints: freely.

How DNS enforcement works

For each check:

  1. A fresh dnspython resolver is created with configure=False (so it never reads /etc/resolv.conf) and pointed at only the endpoint's dns_server.
  2. That resolver performs the A-record lookup.
  3. The resulting IP is pinned for the single outgoing HTTP(S) request via a scoped patch of socket.getaddrinfo — so requests/urllib3 connects directly to that IP instead of resolving the hostname itself. The hostname in the URL is left untouched, so the Host header and TLS SNI/certificate checks behave exactly as they normally would.

At no point is the container's own (router-provided) DNS resolver consulted for endpoint hostnames.

Literal IPs in host: if host is already an IPv4/IPv6 address (e.g. 192.168.0.2), DNS resolution is skipped entirely — the monitor connects to it directly, and dns_server is optional/ignored for that endpoint. (Without this, a literal IP would be sent as a DNS query to whatever dns_server you configured, which returns NXDOMAIN — it is not a valid hostname — and the check would always fail.)

What gets written to InfluxDB

Measurement uptime_check, one point per endpoint per check:

  • tags: endpoint, host, scheme, port
  • fields: success (0/1), expected_status, status_code (if a response was received), response_time_ms, resolved_ip, error (if any)

Example Flux query:

from(bucket: "uptime")
  |> range(start: -24h)
  |> filter(fn: (r) => r._measurement == "uptime_check")
  |> filter(fn: (r) => r._field == "success")

Failure / immediate recheck behavior

  • Every check.interval_seconds, all endpoints are checked in sequence.
  • If any endpoint fails that pass, after immediate_recheck_delay_seconds all endpoints are checked again immediately (one extra pass).
  • The monitor then returns to the normal interval_seconds schedule. If the outage continues, the next scheduled pass will detect it again and trigger another single immediate recheck — it does not hammer the endpoint in a tight loop.

Local dev (without Docker)

pip install -r requirements.txt
CONFIG_PATH=./config.yaml python -m app.main
S
Description
No description provided
Readme
56 KiB
Languages
Python 97.2%
Dockerfile 2.8%