Monitoring & Logging¶
Modelship exposes Prometheus metrics through a single port via Ray's metrics agent. When enabled, all metrics — Ray cluster, Ray Serve, vLLM engine, and custom Modelship metrics — are available on one scrape endpoint.
Logging¶
Modelship uses a centralized logging system with structured output and request correlation. All application logs go through the modelship.* logger hierarchy, separate from library logs (Ray, vLLM, etc.).
Configuration¶
| Env Var | Default | Description |
|---|---|---|
MSHIP_LOG_LEVEL |
INFO |
App log level. Set to TRACE for request/response payloads, DEBUG for detailed diagnostics. Each level sets library logs to the next level up (e.g. DEBUG app → INFO libs). |
MSHIP_LOG_FORMAT |
text |
text for human-readable output, json for structured JSON lines (for log aggregation with ELK/Loki/Splunk). |
MSHIP_LOG_TARGET |
console |
Log target. console writes to stderr; syslog URIs ship logs to a remote syslog server (see below). |
OTEL_EXPORTER_OTLP_ENDPOINT |
— | When set, logs are also exported to an OpenTelemetry collector via OTLP (see below). |
Log Levels¶
Each level sets library logs (Ray, vLLM, transformers) to the next level up:
| Level | App logs (modelship.*) |
Library logs |
|---|---|---|
TRACE |
Request/response payloads (audio bytes, transcription text, chat messages, etc.) | DEBUG |
DEBUG |
Detailed diagnostics, per-chunk details | INFO |
INFO (default) |
Startup, deployment, request summaries | WARNING |
WARNING |
Warnings only | ERROR |
Request Correlation¶
Every API request is assigned a unique request ID that appears in all log lines for that request — both in the API gateway process and in the model deployment actor. This allows tracing a request end-to-end across Ray actor boundaries.
Text format example:
[2025-04-09 14:06:54] INFO modelship.api [a1b2c3d4] | chat_completion model=llama messages=3 stream=True max_tokens=512
JSON format example:
{"timestamp": "2025-04-09T14:06:54", "level": "INFO", "logger": "modelship.api", "message": "chat_completion model=llama messages=3 stream=True max_tokens=512", "request_id": "a1b2c3d4", "pid": 12345}
Logger Names¶
| Logger | Scope |
|---|---|
modelship.startup |
Application initialization and shutdown |
modelship.api |
API gateway endpoints |
modelship.api.auth |
Authentication middleware |
modelship.infer |
Base inference layer |
modelship.infer.deployment |
Ray Serve model deployment actor |
modelship.infer.vllm |
vLLM inference backend |
modelship.infer.diffusers |
Diffusers inference backend |
modelship.infer.diffusers.image |
Diffusers image generation |
modelship.infer.custom |
Custom/plugin inference backend |
modelship.plugin.<name> |
Individual plugins (kokoroonnx, orpheus, whispercpp) |
Syslog¶
Ship logs to a remote syslog server instead of stderr. Useful for centralized logging on bare-metal or Unraid setups without extra infrastructure.
# UDP (default)
python mship_deploy.py --log-target syslog://192.168.1.50:514
# TCP (reliable delivery)
python mship_deploy.py --log-target syslog+tcp://192.168.1.50:514
# Via environment variable
MSHIP_LOG_TARGET=syslog://192.168.1.50:514 python mship_deploy.py
Supported URI formats:
| URI | Protocol | Notes |
|---|---|---|
syslog://host:port |
UDP | Default, fire-and-forget |
syslog+tcp://host:port |
TCP | Reliable delivery |
syslog://host |
UDP | Port defaults to 514 |
The syslog target replaces the console handler — logs go to the syslog server only. The --log-format setting still applies (text or JSON).
OpenTelemetry¶
Export logs to an OpenTelemetry collector via OTLP. Unlike syslog, OTel is additive — logs still go to the console (or syslog) handler, and are also shipped to the collector.
First, install the optional dependencies:
Then configure the endpoint:
# Via CLI
python mship_deploy.py --otel-endpoint http://collector:4317
# Via environment variable
OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317 python mship_deploy.py
When OTel is enabled:
- Logs are exported via BatchLogRecordProcessor with OTLPLogExporter (gRPC)
- The service name is set to modelship
- RAY_TRACING_ENABLED=1 is set automatically so Ray workers also export traces
- HTTPS endpoints are detected from the URI scheme; all others use insecure connections
If the opentelemetry-sdk and opentelemetry-exporter-otlp packages are not installed, a warning is logged and OTel is skipped.
Architecture¶
Prometheus ──scrape──> Ray Metrics Agent (:8079)
|
|-- ray_node_* Ray cluster: GPU, CPU, memory
|-- ray_serve_* Ray Serve: HTTP requests, latency, replicas
|-- ray_vllm_* vLLM engine: KV cache, TTFT, tokens, queue
|-- ray_modelship_* Custom: per-model latency, errors, load time
Note: All metrics are prefixed with
ray_by Ray's metrics agent. vLLM metric names are also sanitized (:→_), so e.g. the vLLM-nativevllm:kv_cache_usage_percbecomesray_vllm_kv_cache_usage_perc.
Enabling Metrics¶
Metrics are enabled by default. Set MSHIP_METRICS=false to disable:
docker run --rm --shm-size=8g --gpus all \
-e HF_TOKEN=your_token \
-e MSHIP_METRICS=true \
-e MSHIP_RAY_DASHBOARD=0.0.0.0 \
-v ./models.yaml:/modelship/config/models.yaml \
-v ./models-cache:/.cache \
-p 8000:8000 -p 8079:8079 -p 8265:8265 \
ghcr.io/alez007/modelship:latest-cuda
The dashboard always starts and binds
127.0.0.1by default —MSHIP_RAY_DASHBOARD=0.0.0.0above is what makes the published8265port actually reachable. Only do this on a trusted/private network (see troubleshooting.md). Pair it with--ray-auth=tokento require a bearer token for the now-reachable dashboard — retrieve it withdocker exec <container> cat /home/modelship/.ray/auth_token.
| Env Var | Default | Description |
|---|---|---|
MSHIP_METRICS |
true |
Master toggle. Enables all metrics and the Ray metrics export port. |
RAY_METRICS_EXPORT_PORT |
8079 |
Port for the Ray metrics agent (only active when MSHIP_METRICS=true). |
Set MSHIP_METRICS=false to disable all metrics collection. When disabled, port 8079 is not exposed and there is zero overhead.
Connecting to Prometheus¶
Add Modelship as a scrape target in your prometheus.yml:
scrape_configs:
- job_name: modelship
scrape_interval: 15s
static_configs:
- targets: ["<modelship-host>:8079"]
For multi-node Ray clusters, use Ray's auto-generated service discovery file instead of static targets:
scrape_configs:
- job_name: modelship
file_sd_configs:
- files: ["/tmp/ray/prom_metrics_service_discovery.json"]
Connecting to Grafana¶
A pre-built Grafana dashboard is included at docs/grafana-dashboard.json.
To import it:
- Open Grafana and go to Dashboards > Import
- Upload
grafana-dashboard.jsonor paste its contents - Select your Prometheus datasource when prompted
At the top the dashboard has Gateway, Model, and Node dropdowns. The Gateway dropdown filters every ray_modelship_* panel to a single gateway (or All); the vLLM/Ray-Serve/GPU rows stay cluster-wide since those metrics aren't per-gateway (see The gateway dimension).
The dashboard has 9 rows:
| Row | What it shows | Metric sources |
|---|---|---|
| Overview | Request rate, error rate, in-flight requests, models loaded, client disconnects, auth failures | ray_modelship_* |
| Latency | Gateway P50/P95/P99, per-model latency, per-usecase latency (generate, TTS, image, STT, embed) | ray_modelship_* |
| vLLM Engine | KV cache usage, TTFT, inter-token latency, token throughput, queue depth, preemptions, prefix cache hit rate | ray_vllm_* |
| GPU & System | GPU utilization, GPU memory, CPU, system memory (cluster aggregate) | ray_node_* |
| Ray Serve | Health check latency, request count, deployment processing latency, HTTP request latency | ray_serve_* |
| Operational | Model load time, load failures, resource cleanup errors, streaming chunks/s | ray_modelship_* |
| Alerts | Error rate %, KV cache usage, queue depth, TTFT P99, client disconnects, preemptions, GPU memory | ray_modelship_*, ray_vllm_*, ray_node_* |
| Cluster / HA | Deploy lock, reservation outcomes, operator force-releases, coordinator-vs-replica routing generation, gateway watch errors, state-store latency/errors, deploy duration & model changes | ray_modelship_* |
| Per-Node Resources | GPU utilization/memory, CPU, system memory broken out by node (filtered by the Node dropdown) | ray_node_* |
Deploying via the Helm chart: set
grafanaDashboard.enabled=trueto ship this dashboard as a ConfigMap the Grafana sidecar auto-imports (andprometheusRule.enabled=truefor the alert rules below) — no manual import needed. See the chart README.
Alerting¶
A standalone Prometheus alerting rules file is included at docs/prometheus-alerts.yml. The Grafana dashboard also has a dedicated Alerts row with threshold lines on the key panels.
Importing Alert Rules¶
Add the rules file to your Prometheus config:
Then reload Prometheus (kill -HUP <pid> or POST /-/reload if --web.enable-lifecycle is set).
Alert Reference¶
Critical (page-worthy)¶
| Alert | Condition | For | Description |
|---|---|---|---|
ModelshipHighErrorRate |
Error rate > 5% of traffic | 5m | Significant portion of requests are failing |
ModelshipNoModelsLoaded |
models_loaded == 0 |
2m | Server is running but cannot serve requests |
ModelshipModelLoadFailure |
Any increase in model_load_failures_total |
0m | A model failed to initialize |
ModelshipKVCacheExhausted |
KV cache usage > 95% | 5m | Requests will queue or be preempted |
ModelshipStateStoreErrors |
Any state-store op errors | 5m | Durable HA state failing — self-heal at risk |
ModelshipDeployJobFailed |
kube_job_status_failed > 0 |
0m | Deploy RayJob failed; last upgrade may not be applied (needs kube-state-metrics) |
Warning (investigate)¶
| Alert | Condition | For | Description |
|---|---|---|---|
ModelshipHighP99Latency |
Gateway P99 > 30s | 5m | End-to-end latency is very high |
ModelshipHighQueueDepth |
Waiting requests > 10 | 5m | vLLM engine is falling behind |
ModelshipPreemptions |
Preemption rate > 0 | 5m | GPU memory pressure causing request eviction |
ModelshipClientDisconnects |
Disconnect rate > 1/min | 5m | Clients timing out or dropping connections |
ModelshipGPUMemoryPressure |
Available GPU memory < 1 GB | 5m | GPU is nearly out of memory |
ModelshipHighTTFT |
TTFT P99 > 5s | 5m | Users waiting too long for first token |
ModelshipDeployLockStuck |
deploy_lock_held == 1 for 10m |
0m | A deploy is hung holding the cluster-wide deploy lock |
ModelshipOperatorForceReleased |
Any operator force-release | 0m | A deploy operator died ungracefully; lock reclaimed |
ModelshipGatewayRoutingDivergence |
Replica generation < coordinator for 10m | 10m | A gateway replica is routing from a stale table |
ModelshipRayWorkerNotReady |
Ray worker pod not ready | 5m | Cluster capacity degraded (needs kube-state-metrics) |
Tuning Thresholds¶
All thresholds are starting points. Adjust based on your deployment:
- Error rate: 5% is aggressive — if you run small models that occasionally OOM, raise to 10%.
- P99 latency: 30s works for chat completions with long outputs. For embeddings or TTS, consider lowering to 5-10s by adding per-endpoint rules.
- Queue depth: 10 assumes a single vLLM instance. Scale proportionally with replicas.
- KV cache: 95% is the danger zone. If you use prefix caching heavily, 90% may be more appropriate.
- TTFT: 5s is generous. For interactive chat, consider 2-3s.
- GPU memory: 1 GB threshold assumes you're not running anything else on the GPU. Raise if you have shared workloads.
Health and readiness¶
Two endpoints are always available regardless of the metrics toggle.
/health — cheap liveness probe. Returns 200 as soon as the gateway is up (before model deployments finish):
/readyz — readiness + timing. Returns 200 when every expected model has a registered deployment; 503 with the same JSON body while any model is still pending. Bodies carry full state so a single poll tells you what's loaded, what's outstanding, and how long each model took to come up:
curl http://localhost:8000/readyz
# 200 when ready:
# {
# "status": "ok",
# "ready": true,
# "uptime_s": 420.5,
# "time_to_ready_s": 215.3,
# "models_loaded": ["kokoro", "llm", "nomic-embed", "whisper"],
# "models_expected": ["llm", "kokoro", "whisper", "nomic-embed"],
# "models_pending": [],
# "model_load_times_s": {"llm": 50.2, "kokoro": 8.1, "whisper": 120.5, "nomic-embed": 36.5}
# }
Per-model timings are gateway-measured: the gap between one model registering and the next (models deploy sequentially in mship_deploy.py), so the first model's entry includes any framework-level setup time preceding it.
Use /health for Kubernetes liveness probes and /readyz for readiness probes — /readyz returning 503 prevents a service from flipping traffic onto the pod before models are loaded.
Modelship Metrics Reference¶
Custom metrics are exported through Ray's metrics agent with a ray_ prefix. Per-model/per-gateway metrics use ray.serve.metrics (they're emitted inside Serve replicas); the HA control-plane metrics use ray.util.metrics (emitted by the deploy coordinator, state store, and deploy driver, which are not Serve replicas).
The gateway dimension¶
Every per-model and per-gateway metric below carries a gateway tag identifying which gateway emitted it, so multiple gateways sharing one Ray cluster stay distinguishable (the Grafana dashboard exposes a Gateway dropdown built on it). The tag is stamped automatically from MSHIP_GATEWAY_NAME — the deploy driver sets it and forwards it to every replica via runtime_env, so no call site passes it explicitly.
Cluster-scoped metrics are not per-gateway, because the thing they measure is shared cluster-wide: the deploy lock and reservations (one mutex per cluster), the state store (one shared backend), and all inherited ray_vllm_* / ray_serve_* / ray_node_* metrics (engine/cluster level). The dashboard's Gateway dropdown therefore filters the ray_modelship_* per-model panels but leaves the vLLM/GPU/Ray-Serve panels cluster-wide.
Gateway¶
| Metric | Type | Tags | Description |
|---|---|---|---|
ray_modelship_request_total |
Counter | model, endpoint, status, gateway |
Total requests by model and API method |
ray_modelship_request_duration_seconds |
Histogram | model, endpoint, gateway |
End-to-end request latency |
ray_modelship_request_errors_total |
Counter | model, endpoint, error_type, gateway |
Errors: validation_error, inference_error, stream_error, unhandled |
ray_modelship_request_in_progress |
Gauge | model, endpoint, gateway |
Currently processing requests |
ray_modelship_client_disconnects_total |
Counter | model, endpoint, gateway |
Client disconnected before response completed |
ray_modelship_stream_chunks_total |
Counter | model, gateway |
Streaming chunks emitted |
ray_modelship_auth_failures_total |
Counter | reason, gateway |
Requests rejected for invalid/missing API key (reason: missing, invalid) |
Model Deployment¶
| Metric | Type | Tags | Description |
|---|---|---|---|
ray_modelship_model_load_duration_seconds |
Histogram | model, loader, gateway |
Time to initialize a model |
ray_modelship_model_load_failures_total |
Counter | model, loader, gateway |
Failed model initializations |
ray_modelship_models_loaded |
Gauge | gateway |
Number of loaded and ready models |
Inference Timing¶
| Metric | Type | Tags | Description |
|---|---|---|---|
ray_modelship_generation_duration_seconds |
Histogram | model, gateway |
Chat/text generation latency |
ray_modelship_tts_generation_duration_seconds |
Histogram | model, gateway |
Text-to-speech latency |
ray_modelship_image_generation_duration_seconds |
Histogram | model, gateway |
Image generation latency |
ray_modelship_transcription_duration_seconds |
Histogram | model, gateway |
Speech-to-text latency |
ray_modelship_embedding_duration_seconds |
Histogram | model, gateway |
Embedding latency |
Resource Cleanup¶
| Metric | Type | Tags | Description |
|---|---|---|---|
ray_modelship_resource_cleanup_errors_total |
Counter | model, component, gateway |
Errors during engine/model cleanup |
HA Control Plane¶
These cover the multi-node / HA machinery deployed by the Helm chart (deploy coordinator, pluggable state store, gateway watch loop). The first six carry a gateway tag; the rest are cluster-scoped.
| Metric | Type | Tags | Description |
|---|---|---|---|
ray_modelship_gateway_reconciles_total |
Counter | gateway |
Routing reconciles a gateway replica applied from the coordinator |
ray_modelship_gateway_watch_errors_total |
Counter | gateway |
Errors in a gateway replica's coordinator watch loop (retried) |
ray_modelship_gateway_routing_generation |
Gauge | gateway |
Routing generation a gateway replica has reconciled to |
ray_modelship_coordinator_generation |
Gauge | gateway |
Coordinator's current routing generation (compare to the replica gauge to spot lag) |
ray_modelship_deploy_duration_seconds |
Histogram | gateway |
Wall-clock time for a deploy run to settle |
ray_modelship_deploy_models_changed_total |
Counter | gateway, action |
Models changed by a deploy (action: add, remove, evict) |
ray_modelship_deploy_lock_held |
Gauge | 1 while the cluster-wide deploy lock is held, else 0 | |
ray_modelship_deploy_reservations_total |
Counter | result |
Deploy-lock reservation attempts (result: granted, locked, insufficient_gpu, insufficient_cpu) |
ray_modelship_operator_force_release_total |
Counter | reason |
Locks force-released after ungraceful operator death (reason: probe_gone, unresponsive) |
ray_modelship_state_store_operations_total |
Counter | backend, op, result |
State-store ops (op: get/set/delete; result: ok/error) |
ray_modelship_state_store_operation_duration_seconds |
Histogram | backend, op |
State-store operation latency |
Built-in Metrics from vLLM and Ray¶
These are automatically available when MSHIP_METRICS=true — no additional configuration needed.
vLLM (ray_vllm_*)¶
vLLM metrics are routed through Ray's metrics agent via RayPrometheusStatLogger. The native vllm: prefix is sanitized to ray_vllm_.
ray_vllm_num_requests_running/ray_vllm_num_requests_waiting— queue depthray_vllm_kv_cache_usage_perc— KV cache utilization (0-1)ray_vllm_time_to_first_token_seconds— TTFT histogramray_vllm_inter_token_latency_seconds— ITL histogramray_vllm_e2e_request_latency_seconds— end-to-end latency histogramray_vllm_request_queue_time_seconds— time spent waiting in queueray_vllm_prompt_tokens_total/ray_vllm_generation_tokens_total— token throughput countersray_vllm_num_preemptions_total— memory pressure signalray_vllm_prefix_cache_hits_total/ray_vllm_prefix_cache_queries_total— cache efficiency
Full reference: vLLM Metrics Documentation
Ray Serve (ray_serve_*)¶
ray_serve_num_http_requests_total— request count by route, method, statusray_serve_http_request_latency_ms— request latency histogramray_serve_handle_request_counter_total— request count by deploymentray_serve_deployment_processing_latency_ms— per-replica processing timeray_serve_health_check_latency_ms— health check latency histogram
Full reference: Ray Serve Monitoring
Ray Cluster (ray_*)¶
ray_node_gpus_utilization— GPU utilization by deviceray_node_gram_used/ray_node_gram_available— GPU memoryray_node_cpu_utilization— CPU usageray_node_mem_used/ray_node_mem_total— system memory
Full reference: Ray Metrics