Operations
This chapter covers running LiteBot day to day: where the logs are, what NODE_ENV changes, which health probes to poll, how language model providers fail and recover, what a restart does, how Redis fits in, what reloads without a restart, capacity, backups and a safe deploy routine. It assumes the binary under pm2 from Quickstart: binary.
Logs
LiteBot logs through winston. Every line goes to standard output, so pm2 captures it. With NODE_ENV=production, LiteBot also writes log files under the working directory.
| Where | What | Rotation |
|---|---|---|
pm2 logs litebot (files ~/.pm2/logs/litebot-out.log and litebot-error.log) |
Everything at LOG_LEVEL and above |
pm2 install pm2-logrotate |
logs/error.log |
Errors only, production mode | None built in; use logrotate |
logs/combined.log |
Everything, production mode | Same |
| Variable | Default | Meaning |
|---|---|---|
LOG_LEVEL |
debug |
Winston level: error, warn, info, debug. .env.example sets info; use info in production, because debug logs message previews for every turn |
LOG_FILE |
unset | Replaces both file names above with one path. Both file transports then write to the same file, so error lines appear twice. .env.example sets logs/app.log |
Console lines are coloured and look like info: POST /api/chat 200 812ms {...}. Every HTTP request gets one such line with method, path, status and duration. Grep on the words, not on the line start.
pm2 logs litebot --lines 200 --nostream | grep -E "error|unhealthy|Circuit breaker"
Lines worth knowing:
| Line contains | Meaning | Do this |
|---|---|---|
JWT_SECRET must be defined and at least 32 characters long |
Startup refused. | Fix JWT_SECRET in .env. |
MongoDB connection error then Failed to start server |
MongoDB unreachable at boot; the process exits and pm2 restarts it. | Check MONGODB_URI and the database host. |
Loaded N bots and M tools |
Boot finished loading configuration. | Compare N and M with what you expect. A bot file that fails validation is skipped and logged. |
Provider <name> marked unhealthy after 3 failures |
That provider is out of rotation. | See Provider health and recovery. |
All providers unhealthy, attempting with first available provider |
No provider is marked healthy. | Same section. |
Circuit breaker OPEN for <name> |
Calls to that provider are being rejected for 60 seconds (only with ENABLE_CIRCUIT_BREAKER=true). |
Check the provider's key, credit and status page. |
Redis connection failed - using memory fallback |
Redis is not reachable; sessions use memory and MongoDB. | Normal if you do not run Redis. |
GEMINI_API_KEY is not set but N bot(s) have voice.enabled=true |
Voice sessions for those bots will fail at setup. | Set GEMINI_API_KEY or turn voice off on those bots. |
Voice WS upgrade rejected |
A /voice connection had no valid token or API key. |
See Voice. |
Forced shutdown after timeout |
Shutdown took more than 10 seconds. | See Restarts and shutdown. |
What NODE_ENV changes
NODE_ENV defaults to development when unset. Many installations keep it at development because production mode switches off the reload endpoints and tightens the rate limit. Decide deliberately:
| Behaviour | development (or unset) |
production |
|---|---|---|
Rate limit on /api per client IP |
500 requests per 5 minutes | 100 requests per 15 minutes |
Log files under logs/ |
No | Yes |
| Error replies from the error handler | The real message (plus a stack trace when set to development exactly) |
Something went wrong! |
| Bot and prompt file watching | On | Only with ENABLE_HOT_RELOAD=true |
| Tool file watching | On | Off |
POST /api/bots/reload, POST /api/tools/reload |
Work | Return 403 |
API_RATE_LIMIT replaces the request count in either mode; the window stays 5 or 15 minutes. The limit applies to all /api routes except /api/documents and /api/collections, and it counts per client address (trust proxy is set to one hop, so behind a reverse proxy it uses X-Forwarded-For). A voice gateway or a WhatsApp integration sends all its traffic from one address: in production mode, raise API_RATE_LIMIT well above its peak turns per 15 minutes, or its requests get HTTP 429. WebSocket traffic is not rate limited.
Health probes
| Probe | Auth | Checks | Healthy reply | Unhealthy reply |
|---|---|---|---|---|
GET /health |
None | MongoDB ping | 200 {"status":"healthy"} |
503 {"status":"unhealthy"} |
GET /health/ready |
None | MongoDB ping; collections conversations, bots, leads, support_tickets exist; auto-dialer initialised when enabled |
200 {"ready":true} |
503 with ready: false and a message |
GET /health/detail |
Admin JWT or admin API key | MongoDB, Redis, vector database, auto-dialer | 200, see below | 401, 403 or 503 |
LiteBot creates the four required collections at boot, so /health/ready fails only when the database or the auto-dialer is broken.
curl -s http://127.0.0.1:3345/health/detail -H 'Authorization: Bearer <token>'
{
"status": "healthy",
"timestamp": "2026-10-24T07:19:33.032Z",
"uptime": 86234.5,
"environment": "development",
"version": "1.0.0",
"services": {
"mongodb": "healthy",
"redis": { "status": "disabled", "message": "Redis not available - using memory fallback", "connected": false },
"vectorDb": { "provider": "vectra", "status": "healthy" },
"autoDialer": { "initialized": false, "enabled": false, "lastError": null, "initializedAt": null }
}
}
Poll /health every 30 to 60 seconds and alert on anything but 200. /health does not test the language model providers: a server whose provider keys are all dead still answers healthy. Watch the provider status below as well, or send a scheduled test chat.
Provider health and recovery
LiteBot tracks each provider registered from .env. After 3 consecutive failed requests it marks the provider unhealthy and logs marked unhealthy. While a provider is unhealthy, a bot that asks for it is served by another healthy provider: DEFAULT_LLM_PROVIDER first, then the others in registration order. This substitution happens even with DISABLE_PROVIDER_FALLBACK=true; that setting only controls the extra fallback attempt made by the circuit breaker. If every provider is unhealthy, LiteBot tries the first registered one anyway. One successful request marks a provider healthy again.
Check the state:
curl -s http://127.0.0.1:3345/api/bots/providers/status -H 'Authorization: Bearer <token>'
{
"success": true,
"data": {
"providers": {
"gemini": { "healthy": false, "consecutiveFailures": 3, "lastError": "Request failed with status code 402", "provider": "GeminiProvider" },
"openrouter": { "healthy": true, "consecutiveFailures": 0, "provider": "OpenRouterProvider" }
},
"available": ["gemini", "openrouter"],
"total": 2,
"healthy": 1
}
}
Bring a provider back after you fix its key or credit:
| Action | Endpoint | Who |
|---|---|---|
| Health-check every provider and mark each healthy or unhealthy | POST /api/bots/providers/health-check |
Admin |
| Send one test message to one provider | POST /api/admin/providers/<name>/test |
Admin |
| Re-create all providers from the loaded settings | POST /api/admin/providers/reinitialize |
Admin |
| Restart | pm2 restart litebot --update-env |
Shell |
curl -s -X POST http://127.0.0.1:3345/api/bots/providers/health-check -H 'Authorization: Bearer <token>'
Newer builds also re-probe an unhealthy provider on their own: once per PROVIDER_RETRY_MS (default 60000), one real request is sent to it, and if it succeeds the provider returns to rotation (log line Probing unhealthy provider <name>). These builds also drop the bot's model name when another provider stands in, logging <name> serving with its default model. In older builds an unhealthy provider stays out until one of the actions above or a restart, and the stand-in provider receives the original bot's model name, which another provider usually rejects. If you run an older build, include the health-check call in your incident routine.
Per-tenant provider keys are outside this rotation: a tenant's own provider never falls back to the global keys. Reinitialising does not re-read .env; edits to .env need a restart. Provider settings are in LLM providers.
Restarts and shutdown
On SIGTERM or SIGINT (what pm2 restart and pm2 stop send), LiteBot closes chat and voice WebSockets, which ends every live voice session, stops its timers and background queue (up to 10 seconds), stops accepting HTTP connections and exits. If that takes longer than 10 seconds it forces the exit. pm2 waits only 1.6 seconds by default before it kills the process; give it the full window:
pm2 delete litebot
pm2 start ./litebot --name litebot --cwd /opt/litebot --kill-timeout 10000
pm2 save
An unhandled error or rejected promise also triggers this shutdown, and pm2 then starts a fresh process. Repeated restarts in pm2 list (the restart counter) point at such errors; read the lines before each Received ... starting graceful shutdown.
What survives a restart: chat sessions and their history, which are stored in MongoDB and reloaded when the next message arrives with the same sessionId. What does not: live voice sessions, in-flight chat turns, the in-memory caches, and provider health state (every provider starts healthy).
Sessions and Redis
Redis is optional. LiteBot always tries to connect to REDIS_HOST:REDIS_PORT (default localhost:6379); if that fails, or after 3 failed reconnect attempts, it logs a warning and uses memory plus MongoDB. Session lookups go through an in-process cache, then Redis when connected, then memory, then MongoDB.
| Variable | Default | Meaning |
|---|---|---|
REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, REDIS_DB |
localhost, 6379, none, 0 |
Connection |
REDIS_SESSION_TTL |
3600 |
Seconds a session stays in Redis |
REDIS_WRITE_THROUGH |
off unless true |
true stores sessions in Redis first and writes them to MongoDB in the background. .env.example sets true |
SESSION_IDLE_TIMEOUT_MS |
1800000 |
In-memory sessions idle this long are ended (session-end hooks run) |
SESSION_CLEANUP_INTERVAL_MS |
900000 |
How often that sweep runs |
Use Redis when you run more than about 50 concurrent conversations or several LiteBot processes. services.redis.status in /health/detail shows healthy, disabled (not connected) or unhealthy.
Hot reload
Bots and tools edited in the dashboard or through the API apply at once, without a restart. Files on disk and MongoDB edits behave as follows:
| Change | NODE_ENV not production |
NODE_ENV=production |
|---|---|---|
config/bots/*.json saved, added or deleted |
Reloaded or unloaded | Only with ENABLE_HOT_RELOAD=true |
config/prompts/*.txt saved |
Bots using the file reload | Only with ENABLE_HOT_RELOAD=true |
config/tools/*.js saved |
Reloaded | Never; restart |
| Tools stored in MongoDB | Polled every TOOLS_DB_POLL_MS (default 15000; 0 turns polling off) |
Same |
| Bot documents edited directly in MongoDB | POST /api/bots/reload (admin) or restart |
Restart |
.env |
Restart with --update-env |
Same |
Every .js file in config/tools/ is loaded as a tool, except index.js and names starting with . or template. Keep backup copies outside that folder. The admin endpoints POST /api/admin/bots/<botId>/reload and POST /api/admin/prompts/<file>.txt/reload reload one file on demand in either mode. GET /api/admin/hot-reload/status shows whether watching is on.
Capacity
| Limit | Default | Variable |
|---|---|---|
Requests per client IP on /api |
500 per 5 min, or 100 per 15 min in production | API_RATE_LIMIT |
| Voice sessions per process | 10 | MAX_VOICE_SESSIONS_CONCURRENT |
| Voice sessions per API key | 50 | API_KEY_DEFAULT_MAX_VOICE_CONCURRENT (key metadata overrides) |
| Voice sessions per logged-in user | 1 | MAX_VOICE_SESSIONS_PER_USER |
| Tool loop iterations per turn | 5 | REACT_MAX_ITERATIONS |
| LLM request timeout | 30000 ms (Ollama 120000) | LLM_REQUEST_TIMEOUT, <PROVIDER>_REQUEST_TIMEOUT |
| MongoDB connection pool | 5 to 50 | MONGODB_MIN_POOL_SIZE, MONGODB_MAX_POOL_SIZE |
Response time is dominated by the language model and by tool webhooks, not by LiteBot's own processing. One process is one Node.js event loop. Plan memory for the knowledge-base index in Vectra mode, which lives in the process. See Cost and performance.
Backups
| What | Back up? | Notes |
|---|---|---|
MongoDB bots, tools, tenants, users, roles, api_keys |
Yes, always | Configuration and access. tenants holds per-tenant provider keys, encrypted with a key derived from JWT_SECRET |
MongoDB conversations, leads, support_tickets |
Yes | Customer data written by chats and tools |
MongoDB knowledge_documents, kb_collections |
Yes | Knowledge-base text and collection list |
MongoDB campaigns, campaign_contacts, campaign_csv_uploads, auto_dialer_settings |
When the auto-dialer is used | |
MongoDB session_analytics, request_analytics, request_traces |
Optional | Usage, cost and per-turn traces; large |
config/ |
Yes | Bot, prompt and tool files |
data/vectra/ |
Yes, with Vectra | The vector index. Without it, re-index from knowledge_documents |
.env |
Yes, securely | Keys and JWT_SECRET. Losing JWT_SECRET makes stored tenant keys unreadable |
public/, the binary |
Keep the previous version | For rollback |
mongodump --uri="$MONGODB_URI" --db=litebot --out=/backup/litebot-$(date +%F)
Safe deploy routine
The full steps are in Upgrading. The short checklist:
- Copy the new binary as
litebot.newand the newpublic/to the server; compare checksums. - Wait for a quiet moment: check
pm2 logs litebotfor recentPOST /api/chatlines and the voice session count (GET /api/voice/stats, authenticated). - Back up
litebot,public/and.envwith a timestamp suffix. - Swap the binary and
public/, thenpm2 restart litebot --update-env. - Check
/health,/health/ready, theLoaded N bots and M toolsline and the provider status. - Send one test chat to a real bot, and watch the logs for ten minutes.
- If anything is wrong, restore the three backups and restart.
Where to go next
- Fix a symptom: Troubleshooting
- Lock the server down: Security
- Look up a variable: Environment reference
- Review traffic and spend: Conversations and analytics