VoxCore runs as multiple independent uvicorn processes behind an nginx reverse proxy. Each process is a systemd service instance handling its own set of concurrent calls.
Architecture
Each worker is a single-process uvicorn instance (--workers 1). nginx distributes incoming connections using least_conn — the worker with the fewest active connections gets the next call.
total_capacity = MAX_CONCURRENT_CALLS × VOXCORE_WORKERS
| Setting | Default | Description |
|---|
MAX_CONCURRENT_CALLS | 5 | Per-worker limit (1 CPU core ≈ 5 calls) |
VOXCORE_WORKERS | 4 | Number of worker processes |
| Total | 20 | Concurrent calls per server |
When a worker reaches its limit, new connections are rejected with WebSocket close code 1008. nginx’s least_conn naturally routes to workers with available capacity.
Systemd configuration
VoxCore uses a systemd template unit. Worker N gets port 800N:
# /etc/systemd/system/voxcore@.service
[Unit]
Description=VoxCore Worker %i
After=network.target
[Service]
Type=simple
Environment=VOXCORE_PORT=800%i
ExecStart=/opt/voxcore/scripts/run_worker.sh
WorkingDirectory=/opt/voxcore
Restart=always
[Install]
WantedBy=multi-user.target
The run script starts uvicorn on the assigned port:
#!/bin/bash
# /opt/voxcore/scripts/run_worker.sh
exec uv run uvicorn voxcore.app:app \
--host 127.0.0.1 \
--port ${VOXCORE_PORT} \
--workers 1
Never change --workers in uvicorn. Each systemd instance IS one worker. Uvicorn’s --workers flag would fork additional processes, breaking the capacity model.
Service management
# Start/stop/restart all workers
systemctl restart 'voxcore@*'
# Restart a single worker (zero downtime — others keep serving)
systemctl restart voxcore@2
# View logs (all workers)
journalctl -u 'voxcore@*' -f
# View logs (single worker)
journalctl -u voxcore@3 -f
nginx configuration
# /etc/nginx/sites-available/default
upstream voxcore_workers {
least_conn;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
server 127.0.0.1:8003;
server 127.0.0.1:8004;
keepalive 32;
}
Workers only listen on 127.0.0.1 — all external traffic goes through nginx.