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:
| Contents | Rebuilt 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 chunks | The 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
wwwinstance that starts while Redis is cold caches nothing useful for its whole lifetime.getStoredDatare-reads in the background to recover. - Those keys must never be evicted. The cache instance therefore runs
noevictionrather thanallkeys-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.