When you need more than one instance
- Your health dashboard shows buffer depth consistently climbing during peak hours.
- A single agent can’t keep ingest and drain aligned even after tuning drain workers.
- You want redundancy — if one agent crashes, the application shouldn’t lose telemetry while it restarts.
NIGHTOWL_DRAIN_WORKERS before adding instances.
Scaling drain workers first
Each agent runs one ingest loop plus N drain workers. Drain workers claim rows from the SQLite buffer atomically, so they never race.Running multiple agents on one host (Linux)
Linux’sSO_REUSEPORT lets multiple processes bind the same TCP port; the kernel distributes accepted connections across them. Start agents with NIGHTOWL_SO_REUSEPORT=true:
nightowl-agent@1, nightowl-agent@2). Each process gets its own SQLite buffer file but writes to the same PostgreSQL database.
Running multiple agents across hosts
For horizontal scaling across machines, put a TCP load balancer (HAProxy, nginx stream module, or a cloud LB) in front of the agent pool. Point your application at the load balancer withNIGHTOWL_INGEST_URI — the host:port your app transmits telemetry to:
NIGHTOWL_INGEST_URI (app side) and NIGHTOWL_AGENT_HOST (agent side) are two different knobs. NIGHTOWL_AGENT_HOST only controls the address the agent binds to — set it to 0.0.0.0 (or a specific interface) so the agent accepts connections from other hosts. NIGHTOWL_INGEST_URI controls where the app transmits. Both default to loopback, which is why a co-located single-host install needs neither.PostgreSQL and PgBouncer
Every agent instance opens a pool of PostgreSQL connections. Without pooling, N agents × M drain workers quickly exhaustsmax_connections. The shipped docker-compose.yml includes a PgBouncer container on port 6432:
Sizing math
A rough budget for capacity planning:
If your app sends 40,000 payloads/s at peak, you need at least three agents and enough drain workers in aggregate to sustain 40,000 rows/s — roughly 8 workers, spread across the three instances, backed by a PostgreSQL instance that can keep up.
Verifying the fan-out
Open the health dashboard after starting the second agent. Each instance reports its own row in the Instances table with distinct process IDs, ingest rates, and buffer depths. The sum of the per-instance ingest rates should match your application’s outgoing traffic. If one instance is getting all the traffic, check thatSO_REUSEPORT is actually enabled — a common symptom is one agent doing 100% while the others sit at 0%.