Skip to content

fix(k8s): stop stripping the /kartik prefix before LiteLLM - #96

Merged
jcschaff merged 1 commit into
mainfrom
fix/litellm-ingress-preserve-prefix
Aug 12, 2026
Merged

fix(k8s): stop stripping the /kartik prefix before LiteLLM#96
jcschaff merged 1 commit into
mainfrom
fix/litellm-ingress-preserve-prefix

Conversation

@jcschaff

Copy link
Copy Markdown
Member

Follow-up to #95, which set SERVER_ROOT_PATH=/kartik but left the dashboard 404ing at /kartik/ui/.

What #95 got wrong. It assumed LiteLLM's root_path follows the usual FastAPI contract where the reverse proxy has already stripped the prefix. It doesn't — LiteLLM expects to receive /kartik/... and strip it itself (see normalize_route_for_root_path, which returns None for routes not under the root path).

With the prefix stripped by nginx and claimed by root_path, Starlette's mounted StaticFiles derives a child root_path of /kartik/ui while the actual request path is /ui/. The startswith check fails, it resolves the wrong file, and serves 404.html.

Confirmed empirically rather than by reading alone:

Request LiteLLM receives Result
/kartik/ui/ /ui/ 404, 10.9 KB (the 404.html fallback)
/kartik/kartik/ui/ /kartik/ui/ 200, 20.2 KB, <title>LiteLLM Dashboard</title>

The double-prefix trick makes nginx strip one and hand LiteLLM the other — which is exactly the shape it wants.

The API worked under either arrangement (/v1/* matches with or without the prefix), which is why this only surfaced once we looked at the UI.

The fix. rewrite-target is an ingress-wide annotation, so /kartik can't share backend-ingress/api needs the opposite behaviour. Split into a litellm-ingress with no rewrite, carrying the streaming timeouts and the same proxy-buffer-size: 16k that frontend-ingress needed for its session cookie (the dashboard login sets a JWT cookie).

Config-only, no image bump.

🤖 Generated with Claude Code

https://claude.ai/code/session_0174W6CHp7FMt7c9sKdBhbp1

The dashboard 404'd at /kartik/ui/ even with SERVER_ROOT_PATH set. LiteLLM
expects to RECEIVE the prefix and strip it itself -- its root_path is not the
usual "reverse proxy already stripped it" contract, contrary to what #95
assumed.

With the prefix stripped by nginx AND claimed by root_path, Starlette's mounted
StaticFiles computes a child root_path of /kartik/ui against an actual path of
/ui/, fails the startswith check, resolves the wrong file and serves 404.html.
Confirmed empirically: /kartik/kartik/ui/ returned 200 with the real dashboard
(nginx strips one prefix, LiteLLM gets the other) while /kartik/ui/ returned
404. The API was unaffected either way, which is why this only showed up now.

rewrite-target is ingress-wide, so /kartik cannot share backend-ingress, which
needs the opposite behaviour for /api. Split it into its own Ingress with no
rewrite, carrying the streaming timeouts and the same proxy-buffer-size bump
frontend-ingress needed for its session cookie.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0174W6CHp7FMt7c9sKdBhbp1
@jcschaff
jcschaff merged commit 2d86e2d into main Aug 12, 2026
@jcschaff
jcschaff deleted the fix/litellm-ingress-preserve-prefix branch August 12, 2026 18:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant