Zero dependencies · 6 MB binary

HTTP endpoint prober.
Dead simple.

Configure the endpoints you care about, set an interval, and get a Telegram message the moment something goes down — and another when it recovers. No agents, no dashboards, no YAML sprawl.

cactus — config.yaml
2026/06/02 09:00:00 cactus started with 3 probe(s)
2026/06/02 09:00:00 [public-api] OK
2026/06/02 09:00:00 [auth-api] OK
2026/06/02 09:00:00 [payment-api] OK

2026/06/02 09:01:00 [public-api] OK
2026/06/02 09:01:00 [auth-api] OK
2026/06/02 09:01:00 [payment-api] FAIL got status 503, want 200
→ Telegram  [FIRING] payment-api is down · URL: https://pay.example.com/health

2026/06/02 09:02:00 [payment-api] OK
→ Telegram  [RESOLVED] payment-api is back up
What's in the box

Everything you need. Nothing you don't.

One binary, one config file. Every probe runs in its own goroutine — no probe blocks another.

⇉

Fully parallel

Each probe runs in its own goroutine with its own ticker. A slow or hanging endpoint never delays the others.

◎

Alert on transition only

You get one message when something breaks and one when it recovers. No repeated noise for sustained failures.

⌘

YAML config

Method, URL, interval, headers, Basic Auth, and expected status — all per probe, all in one readable file.

✈

Telegram receiver

Plug in a bot token and chat ID and you're done. The notifier interface makes it easy to add more receivers later.

↩

Follows redirects

The HTTP client follows up to 10 redirects automatically. What you configure is the final resolved status code.

◈

Tiny static binary

6 MB stripped, built on scratch. Runs on any Linux host or in a minimal Docker container with no runtime deps.

Architecture

One loop per probe. Four packages.

Each probe goroutine is independent. State transitions (up→down, down→up) are detected locally and fan out to all configured receivers.

📄
Config
config.Load()
→
🔁
Probe
prober.Run()
→
🔔
Alert
notifier.Notifier
→
✈
Telegram
Bot API
Quick start

Running in under a minute.

Pull the image or build from source — then point it at your config.

# Docker (recommended) $ docker pull ghcr.io/alileza/cactus:latest $ docker run -v $(pwd)/config.yaml:/config.yaml ghcr.io/alileza/cactus # Or build from source (requires Go 1.26+) $ git clone git@github.com:alileza/cactus.git $ cd cactus $ go build -ldflags="-s -w" -o cactus . $ ./cactus --config config.yaml
# config.yaml probes: - name: public-api url: https://example.com/health method: GET interval: 30s expected_status: 200 - name: protected-api url: https://example.com/admin method: GET interval: 60s auth: username: user password: pass expected_status: 200 receivers: telegram: bot_token: "YOUR_BOT_TOKEN" chat_id: "YOUR_CHAT_ID"