Skip to Content
InfraData storageInfra: Redis

Redis

Two Redis Stack instances on a DigitalOcean droplet: a cache on 6379, and the Ask Rusty vector index on 6380.

The runbook — deploying, recovery, rebuilding the host — lives with the code at infra/droplet/README.md. This page covers what it is and why, not how to operate it.

What is in there

Nothing authoritative. Everything rebuilds:

ContentsRebuilt by
Query caches (member, logbook_*, flyer_skills_levels, category)The API, lazily from MySQL as requests arrive
Base-data keys (faqs, skills, news, videos, materials, tunnels, countries × en/es/fr)loadRedisBaseData() at API startup, or npm run redis:warm
Vector index chunksThe Ask Rusty ingest cron

There are no sessions — authentication is stateless JWT. Losing the host costs a cache warm-up, not data, which is why replacing it is a low-risk operation.

Who reads it

api and www only. Both take REDIS_HOST and REDIS_PASSWORD from Infisical’s shared folder. admin has no Redis dependency.

www is read-only: it loads the base-data keys once at boot into app.locals, and getStoredData serves from there. That has two consequences worth knowing:

  • A www instance that starts while Redis is cold caches nothing useful for its whole lifetime. getStoredData re-reads in the background to recover.
  • Those keys must never be evicted. The cache instance therefore runs noeviction rather than allkeys-lru, despite being a cache — under LRU the base data is evictable, which surfaces as public pages rendering empty with nothing in the logs to explain it.

Two instances, not one

The index must never evict: a dropped chunk means retrieval quietly returns worse answers with no error anywhere. Keeping it separate gives it its own memory ceiling and lifecycle, so it can be flushed and rebuilt without touching the cache.

CACHE_ENABLED

Every Redis read and write is a no-op unless CACHE_ENABLED is exactly the string "true". "TRUE", "1" and "yes" all read as enabled to a human and are not.

This used to fail silently: setRedisValue returned true whether it wrote or skipped, so the base-data loader logged “Successfully set data” for all 17 keys while writing none of them. It now checks isCacheEnabled() and logs a single honest warning instead.

The client gives up permanently

The API’s Redis client uses reconnectStrategy: false and, after five failed attempts (about ten seconds), sets redisUnavailable for the lifetime of the process. Any outage longer than that — a droplet reboot, for instance — leaves the API serving everything from MySQL until it is redeployed. Working, slower, and silent about it after the initial errors.

Redeploy the API after any Redis interruption.

It is reachable from the internet

Redis listens on the droplet’s public address, protected by requirepass only. That is a tracked decision, not an oversight: the private VPC path works, but Infisical’s DigitalOcean sync strips the VPC attachment on every secret change, so a private deployment would break at an unpredictable later moment.

Tracked in TUN-832 , with the full reasoning in the runbook.

Not managed Redis

DigitalOcean no longer offers it — the engine list is Valkey 8 only, and Valkey does not include RediSearch. Self-hosted Redis Stack is a requirement for vector search, not a preference.

Last updated on