docs: document Redis as a per-repo compose sidecar
New Deploy section 'Supporting services live in your compose, not the
fleet': Redis is declared per-repo, never shared fleet-wide — a shared
instance would re-couple services the per-project network deliberately
isolates.
- ephemeral vs persistent as a conscious choice, both compose snippets in
content tabs (--save "" + no volume, vs --appendonly yes + ${MOUNTS_DIR})
- connect by compose service name (redis://redis:6379), never a host port
- caveats given weight: appendfsync everysec ~1s loss window, and mounts
are not backed up — the dividing line between queue state and Postgres
- SQLite noted as the no-container alternative for a single drainer
- the don'ts, plus one line on why ACLs aren't the answer here
- cross-links the redis lib on the Libraries page
Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
+102
@@ -196,3 +196,105 @@ flowchart LR
|
||||
**Rotating a secret** is a host-side edit — update the file and the service picks
|
||||
it up on restart. No rebuild, and nothing you run: flag it and we handle the
|
||||
restart.
|
||||
|
||||
## Supporting services live in your compose, not the fleet
|
||||
|
||||
Need Redis? **Declare it in your own `compose.yaml`, as a sidecar.** There is no shared
|
||||
Redis — nothing fleet-wide, nothing per-workspace, nothing ops provisions for you. A repo
|
||||
that needs Redis brings its own; a repo that doesn't adds nothing.
|
||||
|
||||
That's the whole point of the deploy model. Every service already runs in its own compose
|
||||
project on its own network so that one service falling over can't touch another. A shared
|
||||
Redis puts that coupling straight back: one process everything depends on, whose OOM, stray
|
||||
`FLUSHALL`, single-threaded stall, or restart becomes *everyone's* outage. A sidecar shares
|
||||
its owning repo's fate and nobody else's — and the isolation is free, because it rides the
|
||||
per-project network you already get. No ACLs, no key-prefix discipline, no shared
|
||||
credentials to manage.
|
||||
|
||||
Your app talks to it with the [`redis` lib](libraries.md) from the suite (async,
|
||||
config-free, kv/hash/ttl/pubsub), pointed at **`redis://redis:6379`** — the compose
|
||||
**service name**, not a host port. The sidecar comes up auto-namespaced on your project's
|
||||
network like every other container, exactly as the naming convention above describes.
|
||||
|
||||
### Ephemeral or persistent — pick deliberately
|
||||
|
||||
A sidecar Redis is **ephemeral by default**: restart it and the data is gone. That's
|
||||
correct for some workloads and quietly destructive for others, so make the call on purpose.
|
||||
Ask one question — *if this data vanished on a restart, would anything be lost?*
|
||||
|
||||
- **No → ephemeral.** A scratch cache, a dedupe set, rate-limit counters, transient data
|
||||
you can just re-fetch. Nothing to back up, nothing to grow.
|
||||
- **Yes → persistent.** An outbound webhook or notification queue, a job queue, anything
|
||||
that could be mid-flight when the process dies. Losing it drops real work.
|
||||
|
||||
=== "Ephemeral (cache / throwaway)"
|
||||
|
||||
Fine to lose on restart — no volume, no persistence, by design.
|
||||
|
||||
```yaml
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
command: redis-server --save "" --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||
# no volume: throwaway by design
|
||||
```
|
||||
|
||||
=== "Persistent (durable queue / state)"
|
||||
|
||||
Survives restart, rebuild, and reboot — the append-only file lives on the host mounts
|
||||
dir injected at deploy, the same `${MOUNTS_DIR}` mechanism described above. Redis just
|
||||
uses it as its backing store.
|
||||
|
||||
```yaml
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
command: redis-server --appendonly yes --appendfsync everysec
|
||||
volumes:
|
||||
- ${MOUNTS_DIR:-./mounts}/redis:/data # AOF persists on the host mounts dir
|
||||
```
|
||||
|
||||
The app connects the same way in both modes — `REDIS_URL: redis://redis:6379`. Only the
|
||||
durability changes.
|
||||
|
||||
!!! warning "Two things to know before you rely on a persistent sidecar"
|
||||
**`--appendfsync everysec` can lose ~1 second of the newest entries** on a hard crash.
|
||||
For a webhook queue that's an acceptable trade — just know it's there. Use
|
||||
`--appendfsync always` if you genuinely cannot drop a single entry (safer, slower).
|
||||
|
||||
**Data under the mounts dir is not backed up.** Mounts are excluded from the backup
|
||||
pipeline, and that's *right* for a queue: a lost queue means some notifications didn't
|
||||
fire, not that business data is gone. **That's the dividing line.** If losing this data
|
||||
would actually hurt, it isn't queue or cache state — it's a system of record, and it
|
||||
belongs in Postgres (which *is* backed up), not a local mount.
|
||||
|
||||
!!! tip "Sometimes you don't need Redis at all"
|
||||
For the dead-simple case — **one process draining its own queue** — a **SQLite file**
|
||||
under the mounts dir does the job with no extra container: atomic commits, crash-safe,
|
||||
zero infra. Reach for Redis when it's actually doing queue *things* (multiple workers,
|
||||
blocking pops, streams / consumer groups, pub/sub) or when it's already in your compose
|
||||
for something else.
|
||||
|
||||
### The rules
|
||||
|
||||
!!! danger "Don't do these"
|
||||
- **Don't map Redis to a host port.** No `ports: - "6379:6379"`. Two repos both
|
||||
grabbing host 6379 on the same box collide. Keep it internal to the compose network —
|
||||
nothing exposed, nothing to collide.
|
||||
- **Don't stand up a shared or fleet-wide Redis.** Per-repo means per-need. One Redis
|
||||
per *project*, shared by that project's containers if a repo runs several — never one
|
||||
per fleet.
|
||||
- **Don't treat persistent Redis as a database.** Queues and caches, yes. A durable
|
||||
system of record, no — that's Postgres, and unlike a mount it's backed up.
|
||||
|
||||
**You can't reach another repo's Redis** — different project, different network. That's not
|
||||
a restriction you have to work around; it's the isolation working *for* you. Nobody else's
|
||||
service can touch your cache or drain your queue either, and you never have to think about
|
||||
whose keys are whose.
|
||||
|
||||
!!! quote "What about ACLs?"
|
||||
A shared Redis *can* be secured — Redis 6+ ACLs scope users by command, key pattern, and
|
||||
channel. But ACLs don't solve resource contention or the noisy-neighbour problem, and
|
||||
they add real management burden, so the fleet uses per-repo sidecars instead. Reserve
|
||||
ACLs for the rare case of a deliberately shared, durable, backed-up Redis run as actual
|
||||
infrastructure.
|
||||
|
||||
Reference in New Issue
Block a user