Per-user usage limits in Open WebUI: the landscape, and what this platform runs¶
Filed 2026-09-06. Standing rule from the owner (same day): research first, including context7, before any code is written, and file the research here so decisions cite evidence.
Provenance, read this first¶
- §1 is an owner-provided survey (produced by an external assistant, pasted by the owner). Its seven links were checked reachable on 2026-09-06 (all HTTP 200). Their content has not been independently re-verified by the platform team, except where §2 says so.
- §2 is measured against this repo and the live deployment on 2026-09-06.
- Process note. The "max accounts" allowance tier (#580, #582) was built by extending the in-repo Redis budget tracker without this survey having been done first. The survey came from the owner afterwards. That is the gap the standing rule closes.
1. Owner-provided survey (2026-09-06)¶
To enforce user-specific usage and token limits in Open WebUI, several community-driven third-party packages and scripts have become the standard solutions. Because Open WebUI natively supports Functions (inlet Filters) and Pipes, these packages integrate right into the admin dashboard. [1, 2, 3]
1.1 Token- and cost-based limiters (most precise)¶
If the primary concern is financial (users burning through expensive tokens on paid APIs), these packages track exact input/output tokens. [3]
- Open WebUI Token Tracking (Dartmouth) [3] — the leading choice for hard token limits. It abstracts tokens into "credits" (e.g. 1 USD = 1,000 credits) to account for differing input/output pricing. Uses Open WebUI Pipes to intercept requests. Admins can set daily token-allowance pools per user or per permission group (e.g. standard users 1,000 credits per day, power users 3,000). [2, 3]
- PostgreSQL Token Usage Tracking Filter (Beau D'Amore) [4, 5] — a community filter that logs token usage into a PostgreSQL database and enforces automated limits. Because it relies on external DB persistence, limits do not reset when the Open WebUI container restarts.
1.2 Message-count and rate limiters (easiest to set up)¶
To stop users spamming requests or overloading a local Ollama server:
- Rate Limit Filter by @justinrahb [2] — installable via Workspace → Functions → Add
Function. Valves set a
sliding_window_limitandsliding_window_minutes(e.g. 100 messages per rolling 24 hours). The counter is in memory, so a server restart resets it. - Official Pipelines rate-limit filter [6, 7] — a middleware script in the official
Open WebUI Pipelines repository; enforces
requests_per_minute/requests_per_hourbefore a query reaches the LLM.
1.3 Alternative: LiteLLM proxy (enterprise grade)¶
If community functions feel too fragile, the industry-standard alternative is LiteLLM as a
proxy between Open WebUI and the AI providers. Open WebUI can forward user identity headers
(ENABLE_FORWARD_USER_INFO_HEADERS=True); LiteLLM reads them and enforces budgets, tracks
cost per user, and blocks requests automatically. [8, 9]
2. How this maps to mit.nonlocally.org (measured 2026-09-06)¶
| Survey option | Status here | Evidence |
|---|---|---|
| LiteLLM proxy with forwarded user headers (§1.3) | This is the architecture. Every model call goes through litellm-proxy; Open WebUI forwards X-OpenWebUI-User-Id/Email/Name/Role. |
k8s/litellm/, docs/usage-and-metering.md; budget_tracker.resolve_user() keys spend on the forwarded id |
| LiteLLM's native per-user budgets | Present but inert. custom_auth.py stamps max_budget on virtual keys, but LiteLLM only enforces it with its own database, and database_url is off (Prisma engine missing from the image). |
docs/usage-and-metering.md §"A second budget that is not enforced"; scripts/deploy_litellm.sh comment (#40, #42) |
| Cost-based hard cap per user (§1.1) | Implemented in-house as a LiteLLM callback, not as an Open WebUI Pipe: budget_tracker.py meters response_cost per user per calendar month in Redis and refuses with HTTP 429 at the cap; fail-open on infra errors. |
k8s/litellm/budget_tracker.py; deployment env MONTHLY_USER_BUDGET=10, MONTHLY_ADMIN_BUDGET=1000 |
| Per-group allowances (Dartmouth "credits per permission group") | Approximated by operator-assigned tiers (#580/#582): budget_tiers.json in the LiteLLM ConfigMap, cap = max(role cap, tier cap), time-limited, read at point of use. Groups are by user id, not Open WebUI groups. |
k8s/litellm/budget_tiers.json; tests/unit/test_budget_tiers.py |
| Self-service "how much have I used" | Built, not yet registered in Open WebUI (#525): usage_tools.py reads the tracker's Redis keys, including the recorded cap. |
usage_tools.py, usage_core.py |
| Message-count / sliding-window rate limits (§1.2) | Not deployed. No request-frequency limit exists; only the monthly cost cap. | grep of owui_functions/, k8s/litellm/ |
2.1 Verified with context7 (Open WebUI docs, 2026-09-06)¶
- Filters expose an
inlet(body, __user__)hook;__user__carriesid,email,name,roleand a per-uservalvesobject (UserValves). Admin-setValvesare Pydantic models rendered as UI. A global rate limiter is a documented pattern: track timestamps per__user__["id"]insideinlet()and raise when the window is exceeded. This is exactly the shape of the @justinrahb filter in §1.2, and confirms its stated limitation (in-memory state). Source:open-webui/docs—features/extensibility/plugin/functions/filter.mdx,plugin/development/valves.mdx,plugin/development/reserved-args.mdx.
3. If we revisit this (options, with the A/B rule)¶
Per the org rule, any adoption of an external package goes through a side-by-side A/B in a separate worktree, with the behaviour delta listed; nothing below is a decision.
- Keep the LiteLLM-side tracker (current). Pros: one enforcement point for every route (Open WebUI chat, API keys, tool servers); fail-open by design; already metered and tiered. Cons: custom code we maintain; per-user tiers are a JSON list, not Open WebUI groups.
- Dartmouth openwebui-token-tracking (Pipe). Would give daily credit pools per Open WebUI group with a DB behind it. Delta to check: it meters only traffic that goes through its Pipe (direct API-key callers to LiteLLM would bypass it), so it would be an addition to, not a replacement for, the proxy-side cap.
- Turn on LiteLLM's native budgets by fixing the Prisma image issue (#40, #42). Would
retire
budget_tracker.py's cap logic in favour of the vendor's, at the cost of running LiteLLM's database. The tier concept would map to LiteLLM teams/budgets. - A sliding-window rate limit is orthogonal to cost and not currently needed (no abuse signal in the spend data as of 2026-09-06: 6 accounts with spend, none near cap after the tier change).
Sources (owner-provided; reachability checked 2026-09-06)¶
- https://docs.openwebui.com/features/extensibility/plugin/functions/
- https://github.com/open-webui/open-webui/discussions/24407
- https://dartmouth.github.io/openwebui-token-tracking/
- https://www.damore.ai/blog/usage-tracking-filter/
- https://www.damore.ai/blog/usage-tracking-filter/
- https://github.com/open-webui/pipelines/blob/main/examples/filters/rate_limit_filter_pipeline.py
- https://github.com/open-webui/pipelines/blob/main/examples/filters/rate_limit_filter_pipeline.py
- https://docs.openwebui.com/reference/env-configuration/
- https://github.com/open-webui/open-webui/issues/23323