Config walkthrough + virtual keys and budgets
Config walkthrough · virtual keys and budgets
TL;DR — a 30-second summarytldr
This chapter is the "answer key for every line" — the secrets file .env and the gateway file litellm.yaml: together these two files decide how secure the system is, how fast it runs and how it breaks. We will walk them line by line with explanations
Rule number 1: real values live in .env only (gitignored, chmod 600) — the example file holds nothing but CHANGE_ME values that must all be replaced
A virtual key is a secondary key issued per channel — each one has 3 properties: it allows only the models you specify, it has its own daily budget ceiling, and it can be revoked or rotated without touching the master key — this is why the 5-channel system lives together in an orderly way: one gate, many doors, every door metered
When to usewhen to use
Do this after bootstrap (chapter 3) — bootstrap will not raise the stack until .env is clean (no CHANGE_ME left)
- Open the .env file with an editor on the VPS and replace block by block following the sections below
- Generate keys in this pattern: random from
openssl rand -hex 16, prefixed with sk-
Issue keys as soon as the stack is up (chapters 3-4), and issue new ones every time you add a channel — chapters 7-8 will use these keys
| Key | Who uses it | Allowed models | Ceiling/day |
|---|---|---|---|
BOM_KEY_LINE | LINE bot | thai-chat-rt | $2 |
BOM_KEY_TELEGRAM | Telegram bot | thai-chat-rt | $2 |
BOM_KEY_MT5 | MT5 EA (read-only) | single analysis model | $1 |
BOM_KEY_WRITER | n8n article pipeline | bom-writer | $5 |
BOM_KEY_WEBUI | Web chat | curated list | $10 |
How-tohow to
The .env file — the full version with explanations (from configs/.env.example):
# ---------- LiteLLM ----------
# The master key = the homeowner's key; generate it once and keep it safe
LITELLM_MASTER_KEY=sk-CHANGE_ME # openssl rand -hex 16, then prefix sk-
LITELLM_UI_USERNAME=admin
LITELLM_UI_PASSWORD=CHANGE_ME # a strong password for the /ui page
# ---------- Postgres ----------
POSTGRES_USER=litellm
POSTGRES_PASSWORD=CHANGE_ME # the database lives inside the container network, not exposed
DATABASE_URL=postgresql://litellm:...@postgres:5432/litellm
# ---------- Open WebUI ----------
WEBUI_SECRET_KEY=CHANGE_ME # used to sign web-chat user sessions
# ---------- Mac via Tailscale ----------
# Format: http://100.x.y.z:11434 (the Mac's Tailscale IP from chapter 2)
# Iron rule: never a public IP
MAC_OLLAMA_BASE_URL=http://100.CHANGE_ME.CHANGE_ME.CHANGE_ME:11434
# ---------- Tier 3 (cloud fallback) ----------
# Want no cloud at all = leave it empty (100% hard-local)
CLOUD_FALLBACK_API_KEY=CHANGE_ME_or_leave_empty
# ---------- Per-channel keys ----------
# Generate them later via /key/generate, then paste them here (chapter 4)
BOM_KEY_LINE=sk-CHANGE_ME
BOM_KEY_TELEGRAM=sk-CHANGE_ME
BOM_KEY_MT5=sk-CHANGE_ME
BOM_KEY_WRITER=sk-CHANGE_ME
BOM_KEY_SMOKE=sk-CHANGE_MERemember: in this house, logs and reports only ever say "key loaded, length N" — never print the real value
After editing, run the 30-second check: chmod 600 .env and docker compose up -d, then confirm LiteLLM parses the config; if anything gets stuck → chapter 9
Issue keys straight through the LiteLLM API (one command per key):
curl -X POST http://127.0.0.1:4000/key/generate \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{"key_alias": "line-bot",
"max_budget": 2.0,
"budget_duration": "1d",
"models": ["thai-chat-rt"]}'
# The response carries a "key" field = the real key -> paste it into .env and the n8n credential store
# (in reports/logs say only "key loaded, length N")Principle: keys live only in .env (VPS) or the n8n credential store — never embedded in workflow JSON or code
Check the cumulative spend every day (this is the "receipt" source of numbers for the whole book):
-- SQL on the LiteLLM Postgres (run via docker exec)
SELECT date_trunc('day', "startTime") AS day,
count(*) AS calls,
round(sum(spend)::numeric, 4) AS usd
FROM "LiteLLM_SpendLogs"
GROUP BY 1 ORDER BY 1 DESC LIMIT 30;Real results from the first week of September 2026: 1,370 calls/$1.10, 1,470/$0.66, 3,448/$2.76 (the peak), 494/$0.31
If anything gets stuck → chapter 9
Config exampleconfig example
The litellm.yaml file — the most important parts, abridged (the full 134-line version lives in the repo):
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY # referenced from env only
max_budget: 5.0 # total cloud spend ceiling of $5/day — the anti-exploding-bill guard
budget_duration: 1d
litellm_settings:
drop_params: True # [Real lesson] parameters the proxy does not know get "silently dropped"
# a probe once proved repeat_penalty=8.0 went through while presence_penalty vanished
fallbacks: [{ "thai-chat": ["thai-chat-cpu", "thai-chat-cloud"] }] # the 3-step ladder from chapter 1
model_list:
# Tier 1 — the Mac (free, primary)
- model_name: thai-chat
litellm_params:
model: ollama_chat/qwen38-chat
api_base: os.environ/MAC_OLLAMA_BASE_URL # Tailscale only
num_ctx: 32768 # context floor
num_predict: 2048 # chat answer ceiling
# Bot route: think off + a tight answer ceiling
# [Real lesson] think on + low num_predict = the model burns its answer quota on "inner thoughts" and replies empty
- model_name: thai-chat-rt
litellm_params:
model: ollama_chat/qwen38-chat
extra_body: { "think": false }
num_predict: 1024
timeout: 51
# Writer route (article pipeline): a high ceiling so long reports are not cut off
- model_name: bom-writer
litellm_params:
num_ctx: 32768
num_predict: 8192 # safe completion floor (chapter 6)The shape of the whole file = one route per use case, one behavior per route; the trade-offs happen here, not in the Mac's Modelfile
Using a key on the caller side is easy — every channel calls the same place, differing only in the key and the model name:
# every channel points at: https://llm.your-domain/v1 (OpenAI-compatible 100%)
curl https://llm.example.com/v1/chat/completions \
-H "Authorization: Bearer $BOM_KEY_LINE" \
-d '{"model": "thai-chat-rt",
"messages": [{"role":"user","content":"How is gold doing this noon?"}]}'Use the wrong key or exceed the budget and the proxy answers 401/403 immediately — there is no way to drift into someone else's money
Pro tip — field-tested trickspro tip
FAQfaq
Q: I deployed with wrong values already — do I have to start over? — No. Fix .env and run docker compose up -d again; the only one that is painful to change is the master key, because every place that references it must be updated
Q: Where should .env be kept? — On the VPS only; never copy it to a laptop or Drive. For a real backup use a vault (e.g. Vaultwarden on the NAS)
For other questions see the FAQ
Q: If a key really leaks, how much is the damage? — Capped at that key's ceiling ($1–10/day) plus the time until we spot it in the spend log — the models the key allows are restricted too, so it cannot be used to call anything pricier
Q: Where is the dashboard? — At LiteLLM's /ui (behind Cloudflare Access); it has both the key list and the daily spend graph
See alsosee also
- Chapter 3 · The VPS = front of house — the home of these config files
- Chapter 7 · LINE/Telegram bots — uses the first two keys
- Chapter 8 · MT5 + pipeline — uses the last two
- Chapter 9 · Security — key hygiene across the whole system
Figuresfigures

Real screen recording of this lesson · click play to hear the summary · not investment advice
▶ Watch the manual video: https://youtu.be/O4hM39cgDCY