Runbooks
GatewayHighErrorRate
Section titled “GatewayHighErrorRate”Severity: warning / critical — Component: Gateway
More than 5% (warning) or 20% (critical) of gateway requests return HTTP 5xx errors over the last 5 minutes.
Impact: Clients receive errors. Async job submissions or sync inference calls are failing.
Diagnosis
# Error rate by mode and service_typerate(GatewAI_requests_total{status=~"5.."}[5m])
# Gateway pod logskubectl logs -l app.kubernetes.io/name=gatewai-gateway -n <namespace> --tail=100
# Status code breakdownsum by (status, mode, service_type) (rate(GatewAI_requests_total[5m]))Common causes: S3 unavailable → check GatewAI_s3_errors_total. Redis unavailable → check GatewAI_redis_errors_total. Upstream inference service returning errors (sync mode).
Mitigation
- Check S3 and Redis connectivity from the gateway pod
- S3 down: async jobs fail at upload — check bucket and credentials
- Redis down: async submissions fail at enqueue — check Redis HA HAProxy endpoint
- Scale the gateway deployment if the issue is resource exhaustion
GatewayRateLimitErrors
Section titled “GatewayRateLimitErrors”Severity: warning — Component: Gateway
The gateway is failing to execute rate-limit checks against Redis. The rate limiter is fail-open: Redis errors allow the request through, so consumers are not blocked — but rate limiting is not enforced.
Impact: Rate limiting silently disabled for the affected service type. Consumers can exceed their configured limits.
Diagnosis
# Error rate per service typerate(GatewAI_ratelimit_errors_total[5m])kubectl logs -l app.kubernetes.io/name=gatewai-gateway -n <namespace> --tail=100 | grep "rate limit"Common causes: Redis unavailable or restarting; connection pool exhausted under high load; network partition between gateway pods and Redis.
Mitigation
- Check Redis HA status:
Terminal window kubectl get pods -l app=redis-ha -n <namespace>kubectl exec -it <haproxy-pod> -- redis-cli -h localhost -p 6380 PING - If Redis is down, other features are also affected (job records, cache, consumer tracking) — follow the Redis recovery procedure
- Once Redis recovers, the rate limiter resumes automatically (no restart needed)
GatewayRateLimitHighRejectionRate
Section titled “GatewayRateLimitHighRejectionRate”Severity: warning — Component: Gateway
More than 20% of rate-limit checks return rejected over the last 5 minutes.
Impact: Affected consumers receive 429 Too Many Requests with Retry-After. Legitimate traffic may be disrupted if limits are set too low.
Diagnosis
# Rejection rate per service typesum by (service_type) (rate(GatewAI_ratelimit_requests_total{result="rejected"}[5m]))/ sum by (service_type) (rate(GatewAI_ratelimit_requests_total[5m]))
# Which user types are hitting limits?sum by (service_type, user_type) (rate(GatewAI_ratelimit_consumer_hits_total[5m]))Common causes: traffic spike from a legitimate consumer; limits configured too low; aggressive client retries; abuse.
Mitigation
- Identify offending consumers via Redis:
Terminal window redis-cli ZREVRANGEBYSCORE llm:consumer:tokens:sa:prompt +inf -inf WITHSCORES LIMIT 0 10 - Compare current rates against configured limits in
config.yamlunderrate_limits - Temporarily raise limits if legitimate consumers are disrupted — update
config.yamlandPOST /-/reload - If abuse detected — disable the consumer at the APISIX level (revoke API key)
GatewayS3Errors
Section titled “GatewayS3Errors”Severity: warning — Component: Gateway
The gateway encountered S3 errors (upload, get, or delete) in the last 5 minutes.
Impact: Upload errors → async job submissions fail (500 to client). Get errors → completed job results cannot be fetched. Delete errors → orphaned files accumulate (no functional impact, storage cost).
Diagnosis
# Errors by operationsum by (operation) (increase(GatewAI_s3_errors_total[5m]))
# Gateway logskubectl logs -l app.kubernetes.io/name=gatewai-gateway -n <namespace> | grep -E "s3 (upload|result fetch) failed"
# Check S3 reachabilitykubectl exec -it <gateway-pod> -n <namespace> -- curl -I <s3-endpoint>Common causes: expired or incorrect credentials; bucket missing or wrong region; network issue to S3 endpoint; S3 service outage.
Mitigation
- Verify S3 credentials in the secret (
S3_ACCESS_KEY,S3_SECRET_KEY) - Check endpoint and region in gateway config
- Confirm bucket exists and credentials have read/write access
- If encryption is enabled, verify
ENCRYPTION_KEYmatches between gateway and relay
RelayInferenceSlow
Section titled “RelayInferenceSlow”Severity: warning — Component: Relay sidecar
The 95th percentile inference duration exceeds the configured threshold (default: 120 s).
Impact: Async jobs complete later than expected. KEDA may create additional pods if the queue keeps growing.
Diagnosis
# p95 inference duration by service_typehistogram_quantile(0.95, sum by (service_type, le) (rate(GatewAI_relay_inference_duration_seconds_bucket[10m])))
# Input file size distributionhistogram_quantile(0.95, sum by (service_type, le) (rate(GatewAI_relay_input_size_bytes_bucket[10m])))Common causes: large input files; GPU contention; model cold start after scale-from-zero; inference model degraded (OOM, throttling).
Mitigation
- Large inputs → expected behaviour; consider raising the alerting threshold or use
max_file_size_mb - Cold start → increase
queue_pop_timeoutin relay config - Check model pod resource limits — GPU memory pressure can slow inference significantly
RelayJobFailureRate
Section titled “RelayJobFailureRate”Severity: warning / critical — Component: Relay sidecar
More than 10% (warning) or 30% (critical) of jobs processed by relay sidecars are failing.
Impact: Async jobs complete with status failed. Clients are notified via webhook or see status: failed when polling.
Diagnosis
# Failure rate by service_typesum by (service_type) (rate(GatewAI_relay_jobs_total{status="failed"}[5m])) / sum by (service_type) (rate(GatewAI_relay_jobs_total[5m]))
# Relay pod logskubectl logs -l serving.kserve.io/inferenceservice=<name> -c kserve-container -n <namespace> --tail=100
# S3 download errorsincrease(GatewAI_relay_s3_errors_total[5m])Common causes: inference model returning non-2xx; S3 download failure; inference timeout; GPU OOM causing pod restart.
Mitigation
- Check relay logs for specific error (
inference error,s3 download failed) - If model is crashing: check GPU memory usage, reduce
containerConcurrency - If timeouts: increase
inference.timeoutin relay config - If S3 errors: check credentials and
ENCRYPTION_KEYconsistency between gateway and relay - Resubmit failed jobs via
POST /jobs/{service_type}(results cleaned up after TTL, 72h default)
RelayS3Errors
Section titled “RelayS3Errors”Severity: warning — Component: Relay sidecar
Relay sidecars encountered S3 errors (get/put/delete) in the last 5 minutes.
Impact: Get errors → relay cannot download input file → job marked failed. Put errors → inference result cannot be uploaded → job marked failed, result lost. Delete errors → input file not cleaned up (storage cost, no functional impact).
Diagnosis
# Errors by operationsum by (operation) (increase(GatewAI_relay_s3_errors_total[5m]))
# Relay logskubectl logs -l serving.kserve.io/inferenceservice=<name> -c kserve-container -n <namespace> | grep -E "s3|S3"
# Check if gateway S3 errors also firing (shared credentials issue)increase(GatewAI_s3_errors_total[5m])Common causes: S3 credentials not injected into relay pod; ENCRYPTION_KEY mismatch between gateway and relay; input file deleted before relay could download it; S3 endpoint unreachable from inference pod’s network namespace.
Mitigation
- Verify
S3_ACCESS_KEYandS3_SECRET_KEYenv vars in the KServe manifest - Confirm
ENCRYPTION_KEYis the same secret in both gateway and relay deployments - Check network policies — inference pods must reach the S3 endpoint
- If input files disappear prematurely: check
redis.pending_max_ageandlifecycle.job_ttl.pendingvs inference duration
Dead-letter queues
Section titled “Dead-letter queues”Two Redis lists collect items that could not be delivered or recovered automatically. They are not self-draining — inspect and act on them.
webhook:deadletter — job-completion webhooks that failed every retry (consumer endpoint down or returning 5xx). Watch increase(gatewai_webhook_deliveries_total{result="deadletter"}[1h]) and gatewai_webhook_retry_queue_depth (a growing depth means consumers are failing).
redis-cli LLEN webhook:deadletterredis-cli LRANGE webhook:deadletter 0 9 # {job_id, service_type, url, attempts, failed_at}The job result is still in S3/Redis (subject to TTL) — clients can poll GET /jobs/{type}/{id}. Once the consumer is healthy, re-notify from your own tooling, or LTRIM/DEL the list after handling.
relay:<model>:deadletter — async jobs a dead relay pod abandoned that the phase-0 reaper requeued gc.max_reap_attempts times without success (persistently crashing on that job). The job is marked failed. Watch increase(gatewai_async_jobs_reaped_total{outcome="deadletter"}[1h]).
redis-cli LLEN relay:whisper-large-v3:deadletterredis-cli LRANGE relay:whisper-large-v3:deadletter 0 -1A non-zero count usually means the job input reliably kills the relay (bad payload, OOM). Investigate the relay logs for those IDs before re-submitting; raise gc.max_reap_attempts only if the failures were transient (e.g. node churn), not deterministic.