Hi all — we’re building a self-hosted LiveKit SIP-based voice-agent deployment that needs to scale, and as part of that we’ve been doing some load testing and tuning. We’d really appreciate a sanity check on our setup and any suggestions for squeezing more concurrent sessions out of the hardware.
**Hardware / topology (single node):**
- AWS EC2, **8 vCPU (aarch64 / Graviton), ~15 GiB RAM**, Ubuntu.
- All LiveKit components co-located on the one box: **livekit-server (SFU), livekit-sip, and the Python agent worker**, plus a local Redis. The SIP↔SFU media path is loopback.
- The SIP/RTP load generator runs on a **separate** host, so the numbers below are the SUT’s own load.
**Software:**
- livekit-server **1.13.4**
- livekit-agents **1.6.6** (Python 3.11), livekit-plugins-deepgram, silero VAD, and the `turn_detector` EnglishModel
- livekit-sip built from source (arm64), `lk` CLI 2.18
- Worker launched with `agent.py start` (production mode)
**Workload:** inbound SIP calls → livekit-sip → SFU → a Python `AgentSession` per call (STT + LLM + TTS + silero VAD + turn detection). ~3-minute, 8-turn conversations. To keep the load test deterministic, we stub the STT/LLM/TTS vendors with **local mocks** (fixed injected latencies) so the numbers reflect the LiveKit runtime under load rather than vendor round-trip time.
**Changes we’ve already made from defaults:**
1. **`rtc.use_ice_lite: true`** — this was essential. Without it, livekit-sip attempted ICE gathering against public STUN (`stun.l.google.com`) to negotiate the loopback SIP↔SFU WebRTC transport; on this box those queries time out and every INVITE past ~14 concurrent got `486`. ice-lite (host candidates only) fixed it. (Also `rtc.use_external_ip: false`, `bind_addresses: [127.0.0.1]`, node IP auto-detected to the private address, `rtc.port_range 50000–50200`, `tcp_port 7881`.)
2. **Worker admission opened up:** `load_threshold` 0.7 → **0.95**, `num_idle_processes` 8 (=cpu_count) → **24**.
3. `job_executor_type` left at the default (**process**, fork-per-job).
4. `preemptive_generation` toggled while testing.
**What we observe:** the box tops out around **~25 concurrent sessions** on 8 vCPU, and this is essentially the same whether worker admission is at the default 0.7 or opened to 0.95 — at 0.7 it cleanly refuses (`486`) at ~70% CPU; at 0.95 it accepts more but new-call setup then times out.
**Where we’d love your input:**
1. Are there worker or runtime settings we’re missing to raise **sessions-per-core**?
2. Would **`job_executor_type = thread`** (ThreadJobExecutor) meaningfully help throughput here vs the default process executor, given the GIL and our per-call silero VAD inference — or does the process executor remain the right choice at this scale?
3. Guidance on **`num_idle_processes`** / prewarming for bursty SIP arrival — is 24 sensible on 8 cores, or counterproductive?
6. Any known agent-runtime CPU optimizations (model/config flags, disabling features we don’t need) for a mock-backed pipeline agent?
Any corrections to the above or config we should change are very welcome — we want to make sure we’re configuring this well before we scale it out. Thanks!