Skip to content

Select language

Current language: English

Redis Production Configuration Guide

Redis is an optional, soft dependency in OmniRoute — the application degrades gracefully (in-memory fallbacks) when Redis is unavailable. In production, tuning Redis reduces latency for four distinct workloads:

Workload Driver Client Factory Key Pattern
Rate limiting rateLimiter.ts getRedisClient() — lazy ioredis singleton <prefix>rl:* Lua‑atomic rate limit windows
Auth cache apiKeys.ts Reuses rateLimiter’s client <prefix>auth:api_key:<sha256> with TTL
Quota store redisQuotaStore.ts Separate getRedisClient(url) singleton <prefix>quota:* configurable per-instance
Warmup circuit breaker redisCircuitBreakerStore.ts Separate client in circuitBreakerFactory.ts <prefix>warmup:cb:<connectionId>

All four workloads share one namespace prefix so OmniRoute can co-exist with other apps on a single Redis instance (e.g. 127.0.0.1:6379). See Key Namespacing.


Setting Value Where
REDIS_URL env var redis://redis:6379 (compose), optional rateLimiter.ts:5, .env.example
REDIS_KEY_PREFIX env var omniroute: (default) rateLimiter.ts, redisQuotaStore.ts, redisCircuitBreakerStore.ts, .env.example
QUOTA_STORE_REDIS_URL env var separate, can differ from REDIS_URL quota/storeFactory.ts
QUOTA_STORE_DRIVER "sqlite" (default), "redis" optional quota/storeFactory.ts
ioredis maxRetriesPerRequest 3 rateLimiter.ts client creation
enableReadyCheck not set (ioredis default: true) —
lazyConnect not set (ioredis default: false) —
retryStrategy not set (ioredis default: 200ms base, exponential) —
TLS / password / DB index not configured —
Sentinel / Cluster not configured — standalone single-node only —

OmniRoute shares a Redis instance with whatever else runs on the host. Without a namespace, keys like auth:api_key:<sha256> or rl:* could collide with keys from other applications using the same Redis (this instance runs Redis on 127.0.0.1:6379 alongside other services).

Set REDIS_KEY_PREFIX to a non-empty string to prefix every OmniRoute key:

Terminal window
# .env — all OmniRoute keys become omniroute:rl:*, omniroute:auth:*, omniroute:quota:*, omniroute:warmup:cb:*
REDIS_KEY_PREFIX=omniroute:
  • Default: omniroute: (applied when REDIS_KEY_PREFIX is unset or blank).
  • Applied to: rate limiter + auth cache (shared ioredis client via keyPrefix) and the quota store (KEY_PREFIX = "${REDIS_KEY_PREFIX}quota") and the warmup circuit breaker (KEY_PREFIX = "${REDIS_KEY_PREFIX}warmup:cb:").
  • Changing the prefix when keys already exist in Redis orphans the old keys (they expire via TTL / LRU). Safe to change; no migration needed. The one exception is a warmup circuit-breaker key for a connection marked forbidden: it is persisted without a TTL, so list leftovers with redis-cli --scan --pattern '<old-prefix>warmup:cb:*' and delete them.
  • ioredis keyPrefix automatically prepends the prefix on writes and strips it on reads, so application code never sees the prefix.

1. Connection Pool / Client Options (ioredis Redis constructor)

Section titled “1. Connection Pool / Client Options (ioredis Redis constructor)”

The current code creates a single new Redis(url) with no custom options. For production multi‑replica deployments, pass a client factory in the code or wrap getRedisClient():

const redis = new Redis(REDIS_URL, {
maxRetriesPerRequest: null, // no retry limit; let retryStrategy decide
enableReadyCheck: true, // verify server is ready before accepting calls
lazyConnect: true, // don't connect on construction; wait for first call
retryStrategy: (times) => {
if (times > 10) return null; // give up after 10 retries → reconnect later
return Math.min(times * 200, 5000); // 200ms, 400ms, …, 5s cap
},
enableAutoPipelining: true, // coalesce concurrent commands into one TCP write
keepAlive: 10000, // TCP keep‑alive every 10s
});

Key trade-offs:

  • maxRetriesPerRequest: null + retryStrategy — preferred for production so transient Redis restarts don’t immediately fail every request. The in-memory fallback in checkRateLimit() absorbs the failure path.
  • lazyConnect: true — avoids a startup dependency on Redis being up before the server begins accepting connections.
  • enableAutoPipelining: true — reduces round-trips for concurrent rate-limit checks; beneficial at >50 RPS on a single connection.

2. Redis Server Configuration (redis.conf)

Section titled “2. Redis Server Configuration (redis.conf)”
# Memory
maxmemory 80% # leave room for OS page cache
maxmemory-policy allkeys-lru # evict stale auth cache entries under pressure
# Persistence (optional — OmniRoute is crash‑safe without it)
save 300 1 # snapshot at least every 5 min if ≥1 key changed
appendonly no # AOF not needed; data is regeneratable
appendfsync no # no fsync overhead (RDB is sufficient)
# Networking
timeout 0 # no idle disconnect
tcp-keepalive 300 # 5 min keep‑alive
tcp-backlog 511 # connection backlog for bursty load
# Performance
hz 10 # default; 100 for latency‑sensitive
activedefrag yes # auto‑defragment when fragmentation >10%

Trade-off for maxmemory-policy allkeys-lru: Auth cache entries may be evicted under memory pressure. This is safe — setCachedApiKey always re-populates on miss, and the SQLite fallback is authoritative. The rate-limiter Lua script creates small keys that are short-lived by design.

The prod compose (docker-compose.prod.yml) uses redis:8.6.2-alpine. Add:

redis:
image: redis:8.6.2-alpine
command: [
"redis-server",
"--maxmemory", "512mb",
"--maxmemory-policy", "allkeys-lru",
"--activedefrag", "yes",
"--save", "300 1",
]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
start_period: 5s

4. Multi‑Instance / Scaling Considerations

Section titled “4. Multi‑Instance / Scaling Considerations”

Single Redis for all replicas — the rate-limiter Lua script depends on a single authoritative key space. Multiple Redis instances behind replicas would lose atomicity and double the budget. Use a single Redis (or Redis Sentinel cluster with failover) for all application replicas.

Connection count: Each application replica opens 2 TCP connections to Redis (rate limiter client + quota store client). At 10 replicas → 20 connections, well within a default Redis instance’s 10k connection ceiling.

Expose via health-check endpoint:

// src/app/api/monitoring/health/route.ts already calls rateLimiter functions
// Add Redis-specific checks:
// 1. PING latency via ioredis .ping()
// 2. Memory usage via INFO memory
// 3. Connection count via INFO clients
// 4. Hit rate for maxmemory-policy (evicted_keys / keyspace_hits)

Key metrics to watch:

  • Evicted keys / sec — if persistently non-zero, increase maxmemory
  • Blocked clients — non-zero suggests slow Lua scripts or high contention
  • Rejected connections — connection limit hit; rare at 20 connections

flowchart LR
subgraph App["App Replica"]
RL[rateLimiter.ts]
AK[apiKeys.ts]
QS[redisQuotaStore.ts]
end
RL -- "REDIS_URL" --> R1[(Redis\nshared)]
AK -- "reuses RL's client" --> R1
QS -- "QUOTA_STORE_REDIS_URL" --> R2[(Redis\nquota store)]
R1 --> R2 -- "can be same instance" --> R1

File Purpose
src/shared/utils/rateLimiter.ts Primary Redis client, Lua rate-limit script, in-memory fallback
src/lib/db/apiKeys.ts Auth cache — Redis→SQLite fallback
src/lib/quota/redisQuotaStore.ts Separate Redis client for optional quota store
src/lib/quota/storeFactory.ts Switches between sqlite and redis quota drivers
docker-compose.prod.yml Prod Redis container (image redis:8.6.2-alpine)
.env.example Redis env vars documentation
src/app/api/local/redis/ API routes for dev container orchestration
bin/cli/commands/redis.mjs CLI commands for dev container orchestration

undefined (a58000c7685f)

HagiCode

HagiCode is an agentic coding workspace. Rich, convenient UI interactions make planning, agent execution, and project work approachable and easy to navigate, creating an engaging development experience.

Explore an engaging development experience through rich, convenient UI interactions.

Visit HagiCodeRead the documentation
  • SmartOpenSpec workflows turn intent into an executable path, from proposal to design, tasks, and a shipped change.
  • EfficientMulti-agent execution keeps research, implementation, and review moving in parallel, across several repositories and the Agent CLIs you already use.
  • InterestingRich, convenient UI interactions make planning, agent execution, and project work approachable and easy to navigate.

Download for Windows

Get it from Microsoft StoreAll downloads

Turn ideas into shipped software.

HagiCode is an agentic coding workspace. OpenSpec workflows plan the change, multi-agent execution carries it out, and Hero Scenario makes the long run visible, so ideas become shipped software.

Smart
OpenSpec workflows turn intent into a path from proposal to shipped change.
Efficient
Multi-agent execution runs research, build, and review in parallel.
Fun
Rich UI interactions make planning, agent work, and projects easier to follow.