Skip to content

fix: replace tcpSocket probes with valkey-cli exec probes, switch to non-alpine valkey image#249

Closed
sheepdestroyer wants to merge 10 commits into
masterfrom
fix/valkey-healthcheck-busybox-nc
Closed

fix: replace tcpSocket probes with valkey-cli exec probes, switch to non-alpine valkey image#249
sheepdestroyer wants to merge 10 commits into
masterfrom
fix/valkey-healthcheck-busybox-nc

Conversation

@sheepdestroyer

@sheepdestroyer sheepdestroyer commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Problem

Both valkey-cache and valkey-lf containers were in an infinite SIGTERM restart loop (~every 30 seconds). valkey-cache received 66 SIGTERMs in 34 minutes.

Root Cause

podman play kube converts tcpSocket liveness probes into nc -z -v localhost <port> || exit 1 healthchecks. Alpine's BusyBox nc does not support -z, so the healthcheck always fails. With HealthcheckOnFailureAction=restart, this causes an infinite restart loop.

Fix

  • valkey-cache: tcpSocketexec valkey-cli ping (liveness + readiness)
  • valkey-lf: tcpSocketexec valkey-cli -a <pass> ping (liveness)
  • Image: valkey:9.1.0-alpinevalkey:9.1.0 (non-alpine has no nc, so no broken built-in healthcheck to conflict with)

Verification (dev)

  • 0 SIGTERMs on both valkey instances
  • 0 ECONNREFUSED in Langfuse web/worker logs
  • Both valkey instances reach healthy status
  • Langfuse web starts cleanly with no Redis errors
  • Chat completions: 200 OK in 0.94s

Summary by Sourcery

Switch Valkey containers to exec-based health probes and parameterized ports, make router stack deployment configurable via environment, and add dev-specific startup tooling.

New Features:

  • Add a start-dev.sh wrapper and .env.dev support to run a dev router pod on distinct ports alongside production.
  • Introduce a --pull mode in the stack startup script to fetch the latest router image from GHCR without rebuilding.

Bug Fixes:

  • Prevent Valkey cache and Langfuse Valkey instances from entering infinite SIGTERM restart loops by replacing tcpSocket liveness/readiness probes with valkey-cli exec probes and removing reliance on BusyBox nc.

Enhancements:

  • Parameterize all service ports, pod name, data root, and router image via environment variables and propagate them through pod.yaml, health checks, and application configs.
  • Introduce rendered configuration directories for LiteLLM and the router, including port-aware ClickHouse, LiteLLM, MinIO, and Langfuse settings, to cleanly separate prod and dev configurations.
  • Update stack startup script to support pulling the router image from GHCR, generate auxiliary configs (ClickHouse, LiteLLM, router), and use the configurable pod name throughout lifecycle operations.
  • Make router and LiteLLM applications port-aware via environment variables, including dashboard URLs and internal health checks.

Deployment:

  • Adjust Podman/Kubernetes pod specification to use a configurable pod name, non-alpine Valkey images, and hostPath volumes under a configurable data root, along with a ClickHouse port override config volume.

Summary by CodeRabbit

  • New Features

    • Added a dedicated development startup mode with environment-specific URLs, ports, service settings, and a locally built router image.
    • Added support for configurable service ports across the application stack.
    • Added an option to pull the latest deployment image during startup.
    • Improved service health checks, pod management, and MinIO initialization for development deployments.
  • Bug Fixes

    • Prevented environment configuration from unexpectedly overriding existing values.
    • Updated dashboard connectivity and infrastructure displays to reflect configured ports.
    • Improved persistent data file exclusion from version control.

boy added 9 commits July 11, 2026 12:18
…yments

Replaces all hardcoded port values and the pod name in pod.yaml,
start-stack.sh, litellm/config.yaml and litellm/entrypoint.py with
environment-variable-driven placeholders. A single pod.yaml now serves
both the production agent-router-pod and the development dev-router-pod.

Changes:
- pod.yaml: rewrite with *_PLACEHOLDER vars for pod name, all 13 service
  ports, and DATA_ROOT; add clickhouse-port-config volume; litellm-config
  volume now points to DATA_ROOT/litellm-rendered (rendered copy)
- start-stack.sh: load optional DEV_ENV_FILE overlay; resolve port/name
  defaults after env sourcing; add generate_clickhouse_config() and
  render_litellm_config() helpers; parameterize all agent-router-pod refs
  and hardcoded ports throughout cleanup, health-check, and minio helpers
- litellm/config.yaml: replace port 6379 / 5000 with *_PLACEHOLDER vars
  (rendered at deploy time via render_litellm_config)
- litellm/entrypoint.py: read POSTGRES_PORT / LITELLM_PORT from env
  instead of hardcoded 5432 / 4000; use setdefault to not clobber
  container-injected env vars
- .env: append production port defaults (POD_NAME, ROUTER_PORT, ...)
- .env.dev: new dev overlay (POD_NAME=dev-router-pod, +10 port offsets,
  dev.vendeuvre.lan URLs)
- start-dev.sh: thin wrapper that sets DEV_ENV_FILE + DATA_ROOT then
  delegates to start-stack.sh
- .gitignore: exclude dev-data/ (dev-stack runtime volumes)
…non-alpine valkey image

Root cause: podman play kube converts tcpSocket liveness probes into
'nc -z -v localhost <port> || exit 1' healthchecks. Alpine's BusyBox nc
does not support -z, so the healthcheck always fails. With
HealthcheckOnFailureAction=restart, this causes an infinite SIGTERM
restart loop (~every 30s).

Changes:
- valkey-cache: tcpSocket -> exec valkey-cli ping (liveness + readiness)
- valkey-lf: tcpSocket -> exec valkey-cli -a <pass> ping (liveness)
- Image: valkey:9.1.0-alpine -> valkey:9.1.0 (non-alpine has no nc,
  so no broken built-in healthcheck to conflict with)

Verified in dev: 0 SIGTERMs, 0 ECONNREFUSED, both valkey instances
healthy, Langfuse web/worker stable, chat completions 200 OK.
@sourcery-ai

sourcery-ai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This PR fixes valkey containers’ restart loops by switching their liveness/readiness probes to exec-based valkey-cli ping commands, moves to the non-alpine valkey image, and introduces a more configurable, env-driven Podman stack with port-aware configs and a new dev deployment flow.

Sequence diagram for new valkey exec-based health probes

sequenceDiagram
    participant kubelet
    participant valkey_cache_container
    participant valkey_cli
    participant valkey_server

    kubelet->>valkey_cache_container: livenessProbe exec [valkey-cli ping]
    valkey_cache_container->>valkey_cli: start process
    valkey_cli->>valkey_server: ping
    valkey_server-->>valkey_cli: PONG
    valkey_cli-->>valkey_cache_container: exit 0
    valkey_cache_container-->>kubelet: exec succeeded
    kubelet->>kubelet: mark container healthy

    Note over kubelet,valkey_cache_container: Similar exec valkey-cli -a REDIS_AUTH ping is used for valkey-lf
Loading

Flow diagram for env-driven pod rendering and dev deployment

flowchart TD
    A[start-dev.sh] --> B[set DEV_ENV_FILE=.env.dev]
    B --> C[start-stack.sh]
    C --> D[load .env]
    D --> E[load DEV_ENV_FILE]
    E --> F[set ports & POD_NAME from env]
    F --> G[generate_clickhouse_config]
    F --> H[render_litellm_config]
    F --> I[render_router_config]
    F --> J[render_pod_yaml]
    J --> K[podman play kube -]
    K --> L[stack running with port-aware configs]
Loading

File-Level Changes

Change Details Files
Replace valkey tcpSocket health probes with exec-based valkey-cli checks and switch to non-alpine valkey image.
  • Use valkey-cli ping for valkey-cache liveness and readiness probes.
  • Use valkey-cli -p/-a ping for valkey-lf liveness and readiness probes to respect auth and custom port.
  • Change valkey images from alpine variant to docker.io/valkey/valkey:9.1.0.
  • Add explicit port arguments to valkey-server/redis-server processes driven by placeholders for cache and Langfuse valkey instances.
pod.yaml
Make pod configuration fully driven by environment-based port and image placeholders and generate rendered configs at deploy time.
  • Add env-derived defaults for pod name, ports, router image, and data root in the stack startup script.
  • Export additional env vars and extend the pod.yaml placeholder list for ports, POD_NAME, DATA_ROOT, and router image.
  • Introduce raw (unquoted) placeholder replacement for pod name, data root, and integer port values.
  • Update container env vars and probes (LiteLLM, router, Postgres, ClickHouse, Langfuse, MinIO) to use placeholder-based ports.
  • Change hostPath volumes to use DATA_ROOT-based paths and add a new ClickHouse config volume.
start-stack.sh
pod.yaml
Add port-aware runtime configuration for LiteLLM and router, including rendered config directories and dynamic URL/port resolution.
  • Render LiteLLM and router config.yaml into DATA_ROOT-based directories with substituted VALKEY and router ports.
  • Mount litellm-rendered and router-rendered volumes into containers instead of using source directories directly.
  • Update router config.yaml backend api_base URLs to reference a LiteLLM port placeholder.
  • Update router code to derive LiteLLM, Langfuse, and Valkey ports from environment variables and reflect them in dashboard and health checks.
  • Make LiteLLM entrypoint respect POSTGRES_PORT and LITELLM_PORT env vars for startup dependencies and proxy port.
start-stack.sh
router/config.yaml
router/main.py
litellm/config.yaml
litellm/entrypoint.py
pod.yaml
Improve deployment workflow with support for pulling router image from GHCR, dev overlays, and consistent pod lifecycle handling.
  • Add --pull/--pull-latest mode to start-stack.sh to pull the latest router image from GHCR instead of rebuilding.
  • Update restart/teardown logic to use POD_NAME for pod existence checks, stopping, and restarting.
  • Call ClickHouse config generation and config rendering helpers before pod recreation in all relevant code paths.
  • Add a start-dev.sh wrapper and .env.dev overlay support to stand up a dev pod with separate ports and config.
  • Change router image build/tag usage from localhost/llm-triage-router:latest to ghcr.io/sheepdestroyer/llm-routing:latest.
start-stack.sh
start-dev.sh
.gitignore
pod.yaml
Add ClickHouse port override configuration and wire Langfuse and MinIO to configurable ports.
  • Generate a ClickHouse config.d XML file with http, tcp, and interserver ports from environment variables.
  • Mount the ClickHouse port-override.xml into the ClickHouse container config.d directory.
  • Parameterize Langfuse Web and Worker DB, ClickHouse, Redis, and MinIO connection URLs and ports using placeholders.
  • Update MinIO health checks, mc alias commands, and bucket setup logic to use MINIO_S3_PORT and POD_NAME.
  • Change Langfuse NEXTAUTH_URL to a localhost+LANGFUSE_WEB_PORT-based URL for local deployments.
start-stack.sh
pod.yaml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@sheepdestroyer, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fe7743fc-a248-4dd0-bae4-6b8b8aeec006

📥 Commits

Reviewing files that changed from the base of the PR and between 9f9a3e4 and bba9418.

📒 Files selected for processing (1)
  • pod.yaml
📝 Walkthrough

Walkthrough

Adds a development environment overlay and makes stack deployment configurable through environment-derived ports, endpoints, images, storage roots, probes, pod names, and lifecycle commands. Router and LiteLLM runtime connectivity now use the same configured values.

Changes

Configurable development stack

Layer / File(s) Summary
Environment overlay and startup inputs
.env.dev, .gitignore, litellm/entrypoint.py, start-dev.sh, start-stack.sh
Adds development defaults, optional environment overlays, configurable startup ports, pull modes, and local base URL handling.
Service ports and deployment volumes
litellm/config.yaml, pod.yaml
Replaces fixed service ports, URLs, probes, and storage roots with rendered placeholders across Valkey, LiteLLM, PostgreSQL, ClickHouse, Langfuse, MinIO, and router containers.
Router endpoint and dashboard connectivity
router/config.yaml, router/main.py
Uses configured LiteLLM, Langfuse, Valkey, and router ports for backend routing, proxy requests, health checks, and infrastructure displays.
Rendering, deployment, and verification flow
start-stack.sh
Renders expanded placeholders, manages configurable pod lifecycle and backups, configures MinIO buckets, cleans configured ports, and verifies service readiness.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Developer
  participant start-dev.sh
  participant start-stack.sh
  participant Pod
  participant Router
  participant LiteLLM

  Developer->>start-dev.sh: start development stack
  start-dev.sh->>start-stack.sh: load .env.dev and forward arguments
  start-stack.sh->>Pod: render and deploy configured pod
  Router->>LiteLLM: route requests through configured LiteLLM port
  start-stack.sh->>Router: verify router pipeline
  start-stack.sh->>LiteLLM: verify LiteLLM readiness
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects a major part of the PR: Valkey probe fixes and switching to the non-alpine Valkey image.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/valkey-healthcheck-busybox-nc

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In litellm/entrypoint.py, the change from stripping quotes to using os.environ.setdefault means values containing surrounding quotes in /config/.env will now be passed through literally; if .env files can have quoted values you may want to preserve the prior strip behavior before setdefault.
  • The router image name is now hardcoded to ghcr.io/sheepdestroyer/llm-routing:latest in the build/pull paths while POD YAML uses ROUTER_IMAGE_PLACEHOLDER; consider deriving the build/pull image from the same ROUTER_IMAGE env so prod/dev/alternate registries stay consistent.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In litellm/entrypoint.py, the change from stripping quotes to using os.environ.setdefault means values containing surrounding quotes in /config/.env will now be passed through literally; if .env files can have quoted values you may want to preserve the prior strip behavior before setdefault.
- The router image name is now hardcoded to ghcr.io/sheepdestroyer/llm-routing:latest in the build/pull paths while POD YAML uses ROUTER_IMAGE_PLACEHOLDER; consider deriving the build/pull image from the same ROUTER_IMAGE env so prod/dev/alternate registries stay consistent.

## Individual Comments

### Comment 1
<location path="pod.yaml" line_range="17-20" />
<code_context>
     - warning
-    image: docker.io/valkey/valkey:9.1.0-alpine
+    image: docker.io/valkey/valkey:9.1.0
     livenessProbe:
-      tcpSocket:
-        port: 6379
+      exec:
+        command:
+        - valkey-cli
+        - ping
       initialDelaySeconds: 5
</code_context>
<issue_to_address>
**issue (bug_risk):** Valkey cache liveness/readiness probes ignore the configured VALKEY_CACHE_PORT, which can cause false negatives when the port is customized.

The container is started with `--port 'VALKEY_CACHE_PORT_PLACEHOLDER'`, but the probes call `valkey-cli ping` without `-p`, so they always target 6379. If `VALKEY_CACHE_PORT` is customized, the probes will incorrectly mark the container unhealthy. Please update both probes to call `valkey-cli ping -p 'VALKEY_CACHE_PORT_PLACEHOLDER'` so they respect the configured port.
</issue_to_address>

### Comment 2
<location path="start-stack.sh" line_range="41-50" />
<code_context>
 # Ensure local volume directories exist on the host for Podman mounts
 mkdir -p valkey-data postgres-data langfuse-data clickhouse-data redis-lf-data minio-data

+PULL_MODE=false
</code_context>
<issue_to_address>
**issue (bug_risk):** Volume directories are created under the working directory, but the pod now mounts them from DATA_ROOT, which may point elsewhere.

The script still creates `valkey-data`, `postgres-data`, etc. in the current working directory, but `pod.yaml` now mounts `DATA_ROOT_PLACEHOLDER/...` via hostPath. If `DATA_ROOT != WORKDIR`, those directories under `${DATA_ROOT}` may not exist, and several hostPath volumes don’t use `DirectoryOrCreate`, so pod creation can fail. Please either create the directories under `${DATA_ROOT}` (e.g. `mkdir -p "$DATA_ROOT"/valkey-data ...`) or ensure `DATA_ROOT` and the working directory are aligned so the paths exist when the pod starts.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread pod.yaml
Comment thread start-stack.sh
Comment on lines 41 to 50
mkdir -p valkey-data postgres-data langfuse-data clickhouse-data redis-lf-data minio-data

PULL_MODE=false
FULL_REBUILD=false
REPLACE_MODE=false
if [ "${1:-}" = "--full-rebuild" ]; then
if [ "${1:-}" = "--pull" ] || [ "${1:-}" = "--pull-latest" ]; then
PULL_MODE=true
elif [ "${1:-}" = "--full-rebuild" ]; then
FULL_REBUILD=true
elif [ "${1:-}" = "--replace" ]; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Volume directories are created under the working directory, but the pod now mounts them from DATA_ROOT, which may point elsewhere.

The script still creates valkey-data, postgres-data, etc. in the current working directory, but pod.yaml now mounts DATA_ROOT_PLACEHOLDER/... via hostPath. If DATA_ROOT != WORKDIR, those directories under ${DATA_ROOT} may not exist, and several hostPath volumes don’t use DirectoryOrCreate, so pod creation can fail. Please either create the directories under ${DATA_ROOT} (e.g. mkdir -p "$DATA_ROOT"/valkey-data ...) or ensure DATA_ROOT and the working directory are aligned so the paths exist when the pod starts.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a development environment overlay and parameterizes port assignments, container names, and image tags across the stack (including Valkey, LiteLLM, Langfuse, Postgres, Clickhouse, and MinIO) to allow running dev and prod environments side-by-side. While these changes improve environment flexibility, several critical issues were identified: the Valkey cache health probes need to specify the parameterized port to avoid continuous restarts; environment variable parsing in the LiteLLM entrypoint must restore quote stripping to prevent conversion errors; the Podman build commands should use the configured $ROUTER_IMAGE variable instead of a hardcoded tag; and local volume directories should be pre-created under DATA_ROOT to prevent permission issues.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread pod.yaml
Comment on lines +18 to +30
exec:
command:
- valkey-cli
- ping
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
name: valkey-cache
readinessProbe:
tcpSocket:
port: 6379
exec:
command:
- valkey-cli
- ping

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The valkey-cache container is configured to run on a parameterized port (VALKEY_CACHE_PORT_PLACEHOLDER), which is set to 6389 in the dev environment. However, the liveness and readiness probes execute valkey-cli ping without specifying the port, causing them to default to 6379. This will cause the health checks to fail (or check the wrong instance) and trigger continuous container restarts. Specifying the port via -p fixes this.

      exec:
        command:
        - valkey-cli
        - -p
        - 'VALKEY_CACHE_PORT_PLACEHOLDER'
        - ping
      initialDelaySeconds: 5
      periodSeconds: 10
      timeoutSeconds: 2
    name: valkey-cache
    readinessProbe:
      exec:
        command:
        - valkey-cli
        - -p
        - 'VALKEY_CACHE_PORT_PLACEHOLDER'
        - ping

Comment thread litellm/entrypoint.py
Comment on lines 18 to +19
key, _, val = line.partition("=")
val = val.strip().strip('"').strip("'")
os.environ[key] = val
os.environ.setdefault(key, val)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The quote and whitespace stripping was accidentally removed during the refactoring to os.environ.setdefault. Without stripping, values in .env that are wrapped in quotes (e.g., POSTGRES_PORT="5442") will be loaded with literal quotes, causing parsing failures (such as ValueError when converting POSTGRES_PORT to an integer on line 48).

Suggested change
key, _, val = line.partition("=")
val = val.strip().strip('"').strip("'")
os.environ[key] = val
os.environ.setdefault(key, val)
key, _, val = line.partition("=")
val = val.strip().strip('"').strip("'")
os.environ.setdefault(key, val)

Comment thread start-stack.sh
if $FULL_REBUILD; then
echo "🔨 Building custom local triage router image..."
podman build -t localhost/llm-triage-router:latest -f router/Dockerfile router
podman build -t ghcr.io/sheepdestroyer/llm-routing:latest -f router/Dockerfile router

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The podman build command is hardcoded to tag the image as ghcr.io/sheepdestroyer/llm-routing:latest. However, in the dev environment (.env.dev), ROUTER_IMAGE is overridden to localhost/llm-routing-dev:latest. When running start-dev.sh --full-rebuild, the image is built with the production tag but the pod specification (pod.yaml) is rendered to look for the dev tag, causing the deployment to fail or use a stale image. Using the $ROUTER_IMAGE variable ensures the built image is tagged correctly in all environments.

Suggested change
podman build -t ghcr.io/sheepdestroyer/llm-routing:latest -f router/Dockerfile router
podman build -t "$ROUTER_IMAGE" -f router/Dockerfile router

Comment thread start-stack.sh
podman build -t localhost/llm-triage-router:latest -f router/Dockerfile router
if $FULL_REBUILD; then
echo "🔨 Building custom local triage router image..."
podman build -t ghcr.io/sheepdestroyer/llm-routing:latest -f router/Dockerfile router

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The podman build command is hardcoded to tag the image as ghcr.io/sheepdestroyer/llm-routing:latest. However, in the dev environment (.env.dev), ROUTER_IMAGE is overridden to localhost/llm-routing-dev:latest. When running start-dev.sh --full-rebuild, the image is built with the production tag but the pod specification (pod.yaml) is rendered to look for the dev tag, causing the deployment to fail or use a stale image. Using the $ROUTER_IMAGE variable ensures the built image is tagged correctly in all environments.

Suggested change
podman build -t ghcr.io/sheepdestroyer/llm-routing:latest -f router/Dockerfile router
podman build -t "$ROUTER_IMAGE" -f router/Dockerfile router

Comment thread start-stack.sh
Comment on lines +98 to +99
DATA_ROOT="${DATA_ROOT:-${WORKDIR}/data}"
export POD_NAME ROUTER_PORT LITELLM_PORT LANGFUSE_WEB_PORT LANGFUSE_WORKER_PORT POSTGRES_PORT VALKEY_CACHE_PORT VALKEY_LF_PORT CLICKHOUSE_HTTP_PORT CLICKHOUSE_TCP_PORT CLICKHOUSE_INTERSERVER_PORT MINIO_S3_PORT MINIO_CONSOLE_PORT ROUTER_IMAGE DATA_ROOT

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since the volume mount paths in pod.yaml have been updated to use DATA_ROOT_PLACEHOLDER (defaulting to data/), the pre-creation of directories on line 41 (which still creates them in the repository root) is no longer effective. If these directories do not exist under DATA_ROOT, Podman may automatically create them as root-owned directories, leading to permission denied errors inside the containers. Pre-creating them under DATA_ROOT here resolves this issue.

Suggested change
DATA_ROOT="${DATA_ROOT:-${WORKDIR}/data}"
export POD_NAME ROUTER_PORT LITELLM_PORT LANGFUSE_WEB_PORT LANGFUSE_WORKER_PORT POSTGRES_PORT VALKEY_CACHE_PORT VALKEY_LF_PORT CLICKHOUSE_HTTP_PORT CLICKHOUSE_TCP_PORT CLICKHOUSE_INTERSERVER_PORT MINIO_S3_PORT MINIO_CONSOLE_PORT ROUTER_IMAGE DATA_ROOT
DATA_ROOT="${DATA_ROOT:-${WORKDIR}/data}"
mkdir -p "${DATA_ROOT}/valkey-data" "${DATA_ROOT}/postgres-data" "${DATA_ROOT}/langfuse-data" "${DATA_ROOT}/clickhouse-data" "${DATA_ROOT}/redis-lf-data" "${DATA_ROOT}/minio-data"
export POD_NAME ROUTER_PORT LITELLM_PORT LANGFUSE_WEB_PORT LANGFUSE_WORKER_PORT POSTGRES_PORT VALKEY_CACHE_PORT VALKEY_LF_PORT CLICKHOUSE_HTTP_PORT CLICKHOUSE_TCP_PORT CLICKHOUSE_INTERSERVER_PORT MINIO_S3_PORT MINIO_CONSOLE_PORT ROUTER_IMAGE DATA_ROOT

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
router/main.py (1)

60-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract a helper for the duplicated Valkey port resolution pattern.

The expression os.getenv("VALKEY_CACHE_PORT") or os.getenv("VALKEY_PORT", "6379") is repeated at lines 60, 2669, and 3888. Extracting a small helper would prevent drift if the resolution logic ever changes.

♻️ Proposed helper extraction
+def _valkey_port() -> str:
+    """Resolve the Valkey cache port from env, preferring VALKEY_CACHE_PORT."""
+    return os.getenv("VALKEY_CACHE_PORT") or os.getenv("VALKEY_PORT", "6379")
+
 def get_redis():
     ...
-                port = int(os.getenv("VALKEY_CACHE_PORT") or os.getenv("VALKEY_PORT", "6379"))
+                port = int(_valkey_port())

Then replace the other two occurrences (lines 2669 and 3888) with int(_valkey_port()) and _valkey_port() respectively.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@router/main.py` at line 60, Extract a small _valkey_port() helper that
centralizes the VALKEY_CACHE_PORT/VALKEY_PORT fallback resolution, including the
existing default. Replace the duplicated expressions in the port assignments
near the current occurrence and the two other call sites, using
int(_valkey_port()) where an integer is required and _valkey_port() where the
string value is expected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@litellm/entrypoint.py`:
- Line 19: Update the environment-loading logic around os.environ.setdefault to
normalize val before storing it: trim surrounding whitespace and unwrap
surrounding quotes, while preserving the existing setdefault behavior and key
handling.

In `@pod.yaml`:
- Around line 17-33: Update both the livenessProbe and readinessProbe command
arrays for valkey-cache to pass -p with VALKEY_CACHE_PORT_PLACEHOLDER, matching
the port-aware probe pattern used by the other services.

In `@router/main.py`:
- Around line 2669-2672: Update the Langfuse dashboard “Infrastructure Nodes”
display near the existing hardcoded :3001 value to use the same
LANGFUSE_WEB_PORT environment-variable fallback as the health check in the
startup checks. Keep the default port 3001 when the variable is unset and ensure
both displayed and checked endpoints stay consistent.

In `@start-stack.sh`:
- Line 712: Replace the hardcoded ghcr.io/sheepdestroyer/llm-routing:latest
image reference in the --full-rebuild build and --pull command paths with
${ROUTER_IMAGE}, including the related commands around those paths. Keep
pod.yaml deployment aligned with the same ROUTER_IMAGE value in all modes.

---

Nitpick comments:
In `@router/main.py`:
- Line 60: Extract a small _valkey_port() helper that centralizes the
VALKEY_CACHE_PORT/VALKEY_PORT fallback resolution, including the existing
default. Replace the duplicated expressions in the port assignments near the
current occurrence and the two other call sites, using int(_valkey_port()) where
an integer is required and _valkey_port() where the string value is expected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d01d62ff-a54e-4b20-a599-eb8a3fa8cb83

📥 Commits

Reviewing files that changed from the base of the PR and between 3c2296e and 9f9a3e4.

📒 Files selected for processing (9)
  • .env.dev
  • .gitignore
  • litellm/config.yaml
  • litellm/entrypoint.py
  • pod.yaml
  • router/config.yaml
  • router/main.py
  • start-dev.sh
  • start-stack.sh

Comment thread litellm/entrypoint.py
key, _, val = line.partition("=")
val = val.strip().strip('"').strip("'")
os.environ[key] = val
os.environ.setdefault(key, val)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the .env parsing logic in litellm/entrypoint.py lines 12-20
sed -n '12,20p' litellm/entrypoint.py

Repository: sheepdestroyer/LLM-Routing

Length of output: 469


Strip .env values before storing
line.strip() only trims the whole line; val is still assigned raw here, so quoted values and trailing spaces will be preserved in os.environ. Strip/unwrap val before setdefault.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@litellm/entrypoint.py` at line 19, Update the environment-loading logic
around os.environ.setdefault to normalize val before storing it: trim
surrounding whitespace and unwrap surrounding quotes, while preserving the
existing setdefault behavior and key handling.

Comment thread pod.yaml
Comment thread router/main.py
Comment on lines +2669 to +2672
check_tcp_port("127.0.0.1", int(os.getenv("VALKEY_CACHE_PORT") or os.getenv("VALKEY_PORT", "6379"))),
check_http_endpoint(f"http://127.0.0.1:{os.getenv('LITELLM_PORT', '4000')}/"),
check_http_endpoint("http://127.0.0.1:8080/health"),
check_http_endpoint("http://127.0.0.1:3001"),
check_http_endpoint(f"http://127.0.0.1:{os.getenv('LANGFUSE_WEB_PORT', '3001')}"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Langfuse dashboard display port not updated to match health check.

Line 2672 now uses LANGFUSE_WEB_PORT for the health check, but the dashboard "Infrastructure Nodes" display at line 3908 still shows :3001 hardcoded. If LANGFUSE_WEB_PORT is set to a non-default value, the dashboard will show a stale port.

🐛 Proposed fix for line 3908
-                        <span class="service-port">:3001</span>
+                        <span class="service-port">:{os.getenv('LANGFUSE_WEB_PORT', '3001')}</span>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@router/main.py` around lines 2669 - 2672, Update the Langfuse dashboard
“Infrastructure Nodes” display near the existing hardcoded :3001 value to use
the same LANGFUSE_WEB_PORT environment-variable fallback as the health check in
the startup checks. Keep the default port 3001 when the variable is unset and
ensure both displayed and checked endpoints stay consistent.

Comment thread start-stack.sh
if $FULL_REBUILD; then
echo "🔨 Building custom local triage router image..."
podman build -t localhost/llm-triage-router:latest -f router/Dockerfile router
podman build -t ghcr.io/sheepdestroyer/llm-routing:latest -f router/Dockerfile router

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Build and pull commands hardcode GHCR image instead of ${ROUTER_IMAGE}.

The --full-rebuild path builds ghcr.io/sheepdestroyer/llm-routing:latest and --pull pulls the same, but pod.yaml deploys whatever ROUTER_IMAGE resolves to. In dev mode (ROUTER_IMAGE=localhost/llm-routing-dev:latest), --full-rebuild builds to the wrong tag and --pull fetches the wrong image, while the pod tries to run localhost/llm-routing-dev:latest — causing a deployment failure or stale-image run.

🔧 Proposed fix: use `${ROUTER_IMAGE}` consistently
 # Line 712 (pod-exists, FULL_REBUILD)
-        podman build -t ghcr.io/sheepdestroyer/llm-routing:latest -f router/Dockerfile router
+        podman build -t "${ROUTER_IMAGE}" -f router/Dockerfile router

 # Line 723 (pod-exists, PULL_MODE)
-        podman pull ghcr.io/sheepdestroyer/llm-routing:latest
+        podman pull "${ROUTER_IMAGE}"

 # Line 764 (no-pod, FULL_REBUILD)
-        podman build -t ghcr.io/sheepdestroyer/llm-routing:latest -f router/Dockerfile router
+        podman build -t "${ROUTER_IMAGE}" -f router/Dockerfile router

 # Line 767 (no-pod, else/pull)
-        podman pull ghcr.io/sheepdestroyer/llm-routing:latest
+        podman pull "${ROUTER_IMAGE}"

Also applies to: 723-723, 764-764, 767-767

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@start-stack.sh` at line 712, Replace the hardcoded
ghcr.io/sheepdestroyer/llm-routing:latest image reference in the --full-rebuild
build and --pull command paths with ${ROUTER_IMAGE}, including the related
commands around those paths. Keep pod.yaml deployment aligned with the same
ROUTER_IMAGE value in all modes.

Sourcery review: valkey-cli ping without -p targets default 6379,
causing false negatives when VALKEY_CACHE_PORT is customized (e.g. dev=6389).
Now matches the port-aware pattern already used by valkey-lf probes.
@sheepdestroyer

Copy link
Copy Markdown
Owner Author

Thanks for the reviews. Here's my assessment:

Fixed (pushed):

  • valkey-cache probes now include -p VALKEY_CACHE_PORT_PLACEHOLDER — good catch, the probes would have targeted default 6379 instead of the configured port.

Not in this commit (pre-existing on parent branch chore/learn-deployment-guidelines):

  • litellm/entrypoint.py quote stripping — this commit only touches pod.yaml. The entrypoint change is from a prior commit on the parent branch.
  • start-stack.sh hardcoded ghcr.io/sheepdestroyer/llm-routing:latest in build/pull paths — same, not in this diff.
  • start-stack.sh volume dirs under WORKDIR vs DATA_ROOT — same, not in this diff.
  • router/main.py _valkey_port() helper / hardcoded :3001 — same, not in this diff.

These are valid issues but belong in a separate PR against the parent branch. This PR is scoped to the valkey healthcheck fix only.

sheepdestroyer pushed a commit that referenced this pull request Jul 11, 2026
- litellm/entrypoint.py: restore quote stripping before setdefault
  (prevents literal '"5432"' from breaking int() parsing)
- start-stack.sh: use ROUTER_IMAGE var instead of hardcoded
  ghcr.io/sheepdestroyer/llm-routing:latest in build/pull paths
- start-stack.sh: create volume dirs under DATA_ROOT instead of
  WORKDIR (fixes pod creation when DATA_ROOT != WORKDIR)
- start-stack.sh: add placeholder validation to render_litellm_config
  and chmod 644 on rendered configs for non-root container access
- scripts/backup.sh: use POD_NAME from .env instead of hardcoded
  agent-router-pod-postgres-db (fixes dev backup failures)
- router/main.py: extract _valkey_port() helper to DRY the
  VALKEY_CACHE_PORT/VALKEY_PORT fallback (3 call sites)
- router/main.py: replace hardcoded :3001 with LANGFUSE_WEB_PORT
  env var in dashboard Infrastructure Nodes display
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