Don't store it. Stream it. DING it.
brew install zuchka/tap/ding
curl -sf https://start.ding.ing | sh
DING is a stream-based alerting daemon. Pipe metrics into it. It evaluates rules. It fires alerts. That's it.
Single binary. No database. No agents. No cloud account. Works anywhere.
Copy the example config and validate it
cp ding.yaml.example ding.yaml
ding validate # exits 0 if valid, 1 with a clear error if not
Start DING
ding serve
# point at a specific config
ding serve --config /etc/myapp/alerts.yaml
# download and start in one shot
curl -sf https://start.ding.ing | sh && ding serve --config ./ding.yaml
Send an event
curl -X POST http://localhost:8080/ingest \
-H "Content-Type: application/json" \
-d '{"metric":"cpu_usage","value":97,"host":"web-01"}'
Or just pipe
your-app | ding serve
One YAML file. Lives in your repo. Ships with your code.
rules:
# fires on a single bad reading
- name: cpu_spike
match:
metric: cpu_usage
condition: value > 95
cooldown: 1m
message: "CPU spike on {{ .host }}: {{ .value }}%"
alert:
- notifier: stdout
# fires on sustained high CPU (windowed)
- name: cpu_sustained
match:
metric: cpu_usage
condition: avg(value) over 5m > 80
cooldown: 10m
message: "Sustained high CPU: {{ .avg }}% avg on {{ .host }}"
alert:
- notifier: stdout
All condition forms:
value > 95 # single event
avg(value) over 5m > 80 # average over window
max(value) over 1m >= 100
min(value) over 10s < 10
sum(value) over 30s > 0
count(value) over 2m > 50 # number of events, not sum of values
Template variables in message::
| Variable | When | Description |
|---|---|---|
.metric | always | metric name |
.value | always | raw event value |
.rule | always | rule name |
.fired_at | always | RFC3339 timestamp |
.host, .region, … | always | any label from the event |
.avg .max .min .sum .count | windowed only | aggregate result |
stdout is built-in. For webhooks, define them once and reference by name.
notifiers:
alert-slack:
type: webhook
url: https://hooks.slack.com/services/T.../B.../...
max_attempts: 3 # retries on 5xx (default: 3)
initial_backoff: 1s # doubles each attempt (default: 1s)
rules:
- name: cpu_spike
condition: value > 95
cooldown: 1m
alert:
- notifier: stdout # always available, no declaration needed
- notifier: alert-slack # send to both simultaneously
The webhook receives a JSON POST:
{"rule":"cpu_spike","message":"CPU spike on web-01: 97%",
"metric":"cpu_usage","value":97.0,"fired_at":"...","host":"web-01"}
4xx responses are dropped. 5xx responses are retried with exponential backoff.
avg(value) over 5m works with no database, just memoryweb-01 being loud doesn't silence web-02Benchmarked 2026-03-23 on Apple M3. Full methodology and raw results →
JSON lines:
{"metric": "cpu_usage", "value": 92.5, "host": "web-01"}
Prometheus text:
cpu_usage{host="web-01"} 92.5
| Method | Path | Description |
|---|---|---|
POST | /ingest | Send events |
GET | /health | Health check |
GET | /rules | List rules + cooldown state |
POST | /reload | Hot-reload config |
Reload config without restarting:
kill -HUP <pid>
# or
curl -X POST http://localhost:8080/reload
Survive restarts — persist cooldown state and windowed buffers to disk:
persistence:
state_file: /var/lib/ding/state.json
flush_interval: 30s
SIGTERM / SIGINT — drains in-flight requests, flushes state, exits 0.
Homebrew:
brew install zuchka/tap/ding
Binary:
curl -sf https://start.ding.ing | sh
Docker:
docker run -v ./ding.yaml:/etc/ding/ding.yaml \
ghcr.io/zuchka/ding