Storage (ClickHouse)
Point Kapkan at a ClickHouse server to keep attack and traffic history — the answer to "what hit us last Tuesday". Without it, Kapkan runs entirely in-process on live data.
You need a running ClickHouse server (any recent version) for this. Kapkan talks to its
HTTP interface, which listens on port 8123 by default — that is the port in the example
URL below. Installing ClickHouse is out of scope here; see clickhouse.com.
iBest-effort, never blocks detection
Persistence runs off a bounded queue with a non-blocking send. A slow or down ClickHouse
drops rows (counted in kapkan_storage_rows_total with result="dropped") rather than stalling
the engine's hot path.
Enabling
storage:
clickhouse:
url: "http://127.0.0.1:8123" # empty/absent disables persistence
database: "kapkan" # created if absent
username_env: "KAPKAN_CH_USER" # optional; credentials come from the env
password_env: "KAPKAN_CH_PASS"
ttl_days: 7 # rows auto-expire (per-row TTL)
flush_interval_seconds: 5
batch_size: 1000
queue_size: 100000
traffic_interval_seconds: 10
Credentials are read from the named environment variables (username_env / password_env),
never from the config file. Omit both if your ClickHouse accepts unauthenticated local
connections (the default default user often does) — set them only when your server requires auth.
Changing any storage setting requires a full restart — a config reload that touches the
storage block is rejected (reload: storage settings cannot change at runtime (restart required)).
Verify
After enabling storage, confirm rows are actually landing — persistence is best-effort and fails quietly. Two checks:
- Watch the
kapkan_storage_rows_total{result="written"}metric climb (vsresult="dropped"orresult="error"). See Metrics. - Query ClickHouse directly:
curl 'http://127.0.0.1:8123/?query=SELECT%20count()%20FROM%20kapkan.attack_events'
On a bad credential or permission problem the engine logs clickhouse schema init failed
at startup and clickhouse insert failed when a batch is rejected — check the logs first if
counts stay at zero.
How it works
Kapkan talks to ClickHouse's HTTP interface with the Go standard library — no driver dependency. The only external dependency is the ClickHouse server itself.
On start it idempotently creates three MergeTree tables:
| Table | Contents |
|---|---|
attack_events | Every attack start and end: type, direction, rates, the sample's top sources, ban state, and the detection reason. |
traffic | Periodic per-host rate and baseline snapshots (every traffic_interval_seconds). |
audit_events | Operator-attributed mutations: who issued each ban/unban/config-reload, when, and the outcome. See Audit log. |
Each carries a ttl_days per-row TTL, so retention is bounded without operator intervention.
The writer credential needs CREATE (database + tables) and INSERT rights on first run;
thereafter INSERT is enough. Schema-creation failures are logged at startup, not fatal.
The attack_events table's reason column holds the compact JSON of the detection
reason — why the attack fired (threshold provenance,
warm-up, protocol shares) — and is empty on the attack-ended row. Existing deployments gain the
column automatically: schema init runs an ADD COLUMN IF NOT EXISTS, skipped silently if the
writer credential lacks ALTER rights (fresh installs already have it).
Backpressure
Rows go onto a bounded queue (queue_size) with a non-blocking send and are flushed in
batches (batch_size / flush_interval_seconds). If ClickHouse is slow or down, rows are
dropped — counted as kapkan_storage_rows_total with result="dropped" — and the engine keeps
running. See Metrics.
What is and isn't persisted
The traffic table currently persists per-host snapshots only. When the optional geoip
block is configured (a MaxMind .mmdb database), Kapkan resolves per-source ASNs and
persists each attack's top ASNs in attack_events.top_asns (pipe-joined "AS<n> <org>",
empty when geoip is off). Per-ASN time-series and per-hostgroup totals are not yet
snapshotted — both are candidates for a follow-up.
Related
- Configuration reference — the full
storagekey list. - Audit log — the
audit_eventstable and its read endpoint. - Metrics — the
kapkan_storage_rows_totalcounter.