- Python 98.7%
- Dockerfile 1.3%
The example compose pointed at the maintainer host with a concrete RFC1918
address, while the README documented host.docker.internal. Anyone cloning the
repo would proxy against a host they cannot reach, and the address of a private
setup stood in a public repo. Now ${OLLAMA_BASE_URL:-http://host.docker.internal:11434},
same default as the README. Safe for the running deployment: its container is
configured by the app definition (checked via docker inspect), not by this file.
|
||
|---|---|---|
| .github/workflows | ||
| .gitignore | ||
| analyzer.py | ||
| collector.py | ||
| docker-compose.yml | ||
| Dockerfile | ||
| logo.svg | ||
| main.py | ||
| README.md | ||
| requirements.txt | ||
| test_cloud_failover.py | ||
| test_history_size.py | ||
| test_parse_page.py | ||
| test_proxy_auth.py | ||
| test_proxy_stream.py | ||
ollama-usage-monitor
Transparent proxy for Ollama that sits between your client (Open WebUI, Hermes, etc.) and your local Ollama instance. It intercepts nothing by default — every /api/chat and /api/generate request passes through untouched.
But it also fetches your ollama.com cloud usage (session + weekly limits) and lets you check it via chat keywords or HTTP endpoints — without burning a single token.
Client --/api/chat--> Usage Monitor (port 9462) --/api/chat--> Ollama (local)
|
| GET /api/usage (every OLLAMA_DEBOUNCE_SECONDS)
v
ollama.com (cloud API)
What it tracks
| Field | Example | Meaning |
|---|---|---|
| Tier | pro |
Your ollama.com plan |
| Session | 16.6% (332/2000) |
Requests used this session (resets ~2h) |
| Weekly | 20.4% (1020/5000) |
Requests used this week (resets ~4d) |
| Burn rate | 15.88/h |
Average requests per hour |
| Models | deepseek-v4-flash: 82.5% |
Breakdown by model |
| Recommendation | ok / caution / limit |
Should you worry? |
Chat Keywords
Send any of these as the only content of a chat message to any model via the proxy:
| Keyword | Description |
|---|---|
usage |
Detailed usage data with model breakdown, burn rate, alerts |
summary |
Compact usage summary (tier, session, weekly, recommendation) |
history |
Last 20 usage snapshots |
refresh |
Force immediate refresh from ollama.com |
info / help |
Show all available commands and endpoints |
Keywords are stripped from the chat history — the model never sees them.
HTTP Endpoints
| Endpoint | Description |
|---|---|
GET /health |
Health check |
GET /usage |
Full usage data with analysis |
GET /usage/raw |
Raw usage data without analysis |
GET /usage/summary |
Human-readable summary |
GET /usage/history |
Last 20 usage snapshots |
GET /info |
Available commands and endpoints |
POST /usage/refresh |
Force refresh usage data |
Quick Start
services:
ollama-usage-monitor:
image: mrschnirschuh/ollama-usage-monitor:latest
container_name: ollama-usage-monitor
environment:
- OLLAMA_COOKIE=your_ollama_com_cookie
- OLLAMA_BASE_URL=http://host.docker.internal:11434
# Optional, recommended: require a bearer token on every endpoint but /health
- PROXY_TOKEN=${PROXY_TOKEN:-}
# Optional: restrict which browser origins may call the proxy (default '*')
- CORS_ORIGINS=https://openwebui.example.com
- OLLAMA_DEBOUNCE_SECONDS=30
- LOG_LEVEL=INFO
ports:
- '9462:9462'
restart: unless-stopped
volumes:
- ollama-usage-data:/data
volumes:
ollama-usage-data:
Security
The proxy forwards cloud requests with server-side credentials: for *:cloud models it
replaces the caller's Authorization header with a key from OLLAMA_CLOUD_KEYS and sends the
request to OLLAMA_CLOUD_URL. Every call is billed to your account.
Without PROXY_TOKEN, anyone who can reach the port gets those keys forwarded for them.
Auth is opt-in so existing deployments keep working after an image pull:
PROXY_TOKEN |
Behaviour |
|---|---|
| unset / empty | unchanged — no auth, but a loud WARNING line at startup |
| set | every endpoint except GET /health requires Authorization: Bearer <PROXY_TOKEN>, otherwise 401 {"detail":"Unauthorized"} and no upstream call |
Clients just send it as their API key (Open WebUI: Connection → API Key, Hermes: api_key).
Use HTTPS or a private network in front of the port — a bearer token on plain HTTP is readable
on the wire. CORS_ORIGINS (comma-separated) narrows which browser origins may call the proxy;
the default * is logged as a warning.
Environment Variables
| Variable | Default | Description |
|---|---|---|
OLLAMA_COOKIE |
— | ollama.com session cookie (required) |
OLLAMA_BASE_URL |
http://host.docker.internal:11434 |
Upstream Ollama URL |
OLLAMA_CLOUD_KEYS |
— | Comma-separated ollama.com API keys. When set, *:cloud models bypass the local Ollama and go straight to OLLAMA_CLOUD_URL; on 401/402/403/429 the next key from a different account takes the request. Empty = old behaviour (everything through the local Ollama) |
OLLAMA_CLOUD_URL |
https://ollama.com |
Upstream for cloud models when keys are set |
OLLAMA_DEBOUNCE_SECONDS |
30 |
Min seconds between ollama.com fetches (legacy name DEBOUNCE_SECONDS still works) |
LOG_LEVEL |
INFO |
Log level (DEBUG, INFO, WARNING, ERROR) |
PORT |
9462 |
Proxy listen port |
HISTORY_SIZE |
100 |
Number of snapshots kept in the history file; older ones are dropped. Invalid or empty values are ignored with a warning |
PROXY_TOKEN |
— | Opt-in bearer token. Set it and every endpoint except GET /health requires Authorization: Bearer <PROXY_TOKEN>. Empty = no auth (logged as a warning at startup) |
CORS_ORIGINS |
* |
Comma-separated browser origins allowed to call the proxy; * is logged as a warning |
Cloud key failover exists because Ollama's concurrency limit is per account (measured: ~13
concurrent requests per key, 429 too many concurrent requests beyond it). Two keys of the
same account share one pool and gain nothing — the second key must belong to another
account. Self-check: python3 test_cloud_failover.py.