Files
computer-playbook/roles/svc-db-redis/README.md
kevinveenbirkenbachandClaude Opus 5.5 45d956e5ea fix(sidecars): cap an engine's sidecar at its provider's size
A Redis sidecar whose consumer declares no services.redis.mem_limit
runs in a container that inherits the consumer's primary entity limit.
Since the lookup replaced the old filter, maxmemory followed that limit
and rose from 204 MiB to between 819 and 3276 MiB in 17 roles.

lookup('service_memory_mb', ..., provider=<role>) caps the result at
the same share of the provider's own limit when the application does
not declare one for that service. A declared limit is not capped, and a
container smaller than the provider keeps its own share. The Redis
sidecar passes provider='svc-db-redis': those 17 roles get 409 MiB, the
seven with a 256 MiB container stay at 204.

The lookup now rejects unknown options and a provider that is no role
or declares no limit for the service, so a misspelled option fails
instead of silently lifting the cap.

The memcached sidecar's literal -m 64 takes the same lookup and keeps
its value.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 01:30:01 +02:00

130 lines
6.3 KiB
Markdown

# Redis
## Description
This Ansible role provides Redis in two interchangeable shapes, selected per consumer with the `shared` flag (mirroring `svc-db-postgres`):
- **Central** (`enabled: true`, `shared: true`): a standalone, manager-pinned Redis stack that many roles share. Each consumer gets its own ACL user restricted to the key-prefix `{entity}:*`.
- **Embedded** (`enabled: true`, `shared: false`): a Redis sidecar rendered into the consumer's own compose stack via `templates/service.yml.j2`.
## Overview
The central stack (`templates/compose.yml.j2`) runs `redis:alpine` with:
- `requirepass` for the `default` (admin) user, sourced from the `REDIS_PASSWORD` credential.
- AOF persistence for cached data.
- A `maxmemory` ceiling derived from the service `mem_limit` with an `allkeys-lru` policy.
- A bind on `127.0.0.1:6379` plus the shared cross-stack overlay network for consumers.
Per-consumer provisioning (`tasks/01_init.yml`) runs with `application_id=svc-db-redis` and `database_consumer_id=<consumer>`; it resolves the consumer's username, password and key-prefix via `lookup('engine', 'redis', <consumer>, ...)` and reconciles an idempotent `ACL SETUSER` restricted to `~{entity}:*`. The ACL users are recreated by the consumer's `01_init` on every deploy, so a container restart that drops the in-memory ACL set is healed on the next run.
The embedded snippet (`templates/service.yml.j2`) keeps the previous single-host behaviour: an unauthenticated in-memory Redis attached to the consumer stack's default network. Its `maxmemory` is 80 % of the consumer's `services.redis.mem_limit`; a consumer that declares none gets 80 % of the limit its container inherits, at most 80 % of this role's own `mem_limit`.
## Cosmos
The diagram places Redis in the Infinito.Nexus cosmos: the components it deploys (capabilities), the central services it consumes (dependencies), and its outward reach (federation and bridged external networks).
```mermaid
flowchart LR
subgraph deps [Dependencies]
dep_svc_bkp_volume_2_local["svc-bkp-volume-2-local 💻"]
dep_svc_net_tor["svc-net-tor 🐳🐝"]
end
subgraph role [svc-db-redis 🐳🐝]
svc_redis["redis"]
svc_tor["tor"]
svc_container_backup["container_backup"]
end
subgraph dependents [Dependents]
dpt_web_app_akaunting["web-app-akaunting 🐳🐝"]
dpt_web_app_baserow["web-app-baserow 🐳🐝"]
dpt_web_app_bookwyrm["web-app-bookwyrm 🐳🐝"]
dpt_web_app_decidim["web-app-decidim 🐳🐝"]
dpt_web_app_discourse["web-app-discourse 🐳🐝"]
dpt_web_app_erpnext["web-app-erpnext 🐳🐝"]
dpt_web_app_fider["web-app-fider 🐳🐝"]
dpt_web_app_flowise["web-app-flowise 🐳🐝"]
dpt_web_app_funkwhale["web-app-funkwhale 🐳🐝"]
dpt_web_app_gitea["web-app-gitea 🐳🐝"]
dpt_web_app_gitlab["web-app-gitlab 🐳🐝"]
dpt_web_app_magento["web-app-magento 🐳🐝"]
dpt_more["..."]
end
dep_svc_bkp_volume_2_local -- "1:1" --> svc_container_backup
dep_svc_net_tor -. "0..1" .-> svc_tor
svc_redis -- "1:1" --> dpt_more
svc_redis -. "0..1" .-> dpt_web_app_akaunting
svc_redis -. "0..1" .-> dpt_web_app_baserow
svc_redis -. "0..1" .-> dpt_web_app_bookwyrm
svc_redis -. "0..1" .-> dpt_web_app_decidim
svc_redis -. "0..1" .-> dpt_web_app_discourse
svc_redis -. "0..1" .-> dpt_web_app_erpnext
svc_redis -. "0..1" .-> dpt_web_app_fider
svc_redis -. "0..1" .-> dpt_web_app_flowise
svc_redis -. "0..1" .-> dpt_web_app_funkwhale
svc_redis -. "0..1" .-> dpt_web_app_gitea
svc_redis -. "0..1" .-> dpt_web_app_gitlab
svc_redis -. "0..1" .-> dpt_web_app_magento
```
Solid `1:1` edges are fixed relationships; dashed `0..1` edges are conditional (enabled only in matching deployments); red `0..0` edges are turned off in this role. Node markers show the role's deploy modes (💻 host, 🐳 compose, 🐝 swarm); ❌ marks a service that is explicitly turned off, and ⚙️ an Ansible role dependency declared in `meta/main.yml`.
## Features
- **Central or embedded** selected per consumer via `services.redis.shared`.
- **ACL isolation** one Redis user per consumer, scoped to `{entity}:*` keys.
- **Idempotent provisioning** ACL users reconciled on every deploy via `ACL SETUSER`.
- **Manager-pinned** central on-disk state stays node-local (never on NFS) in swarm.
- **Built-in healthcheck** authenticated `redis-cli ping`.
## Quick Setup
### Development
Clone, set up the workstation, and deploy Redis onto the local stack:
```bash
git clone https://github.com/infinito-nexus/core.git
cd core
make onboard
make compose-deploy mode=reinstall apps=svc-db-redis full_cycle=false
```
### Production
Run the published image to provision the inventory and deploy Redis to a managed server (the mounted volume persists the inventory):
```bash
APP=svc-db-redis
HOST=<your-server>
DOMAIN=<your-domain>
TLS_MODE=self_signed
SSH_PUBLIC_KEY="<your-ssh-public-key>"
docker run --rm -it \
-v "$PWD/inventories:/etc/infinito.nexus/inventories" \
-e APP="$APP" -e HOST="$HOST" -e DOMAIN="$DOMAIN" -e TLS_MODE="$TLS_MODE" -e SSH_PUBLIC_KEY="$SSH_PUBLIC_KEY" \
ghcr.io/infinito-nexus/core/debian bash -c '
INVENTORY=/etc/infinito.nexus/inventories/production
infinito administration inventory provision "$INVENTORY" \
--inventory-file "$INVENTORY/devices.yml" \
--host "$HOST" \
--include "$APP" \
--vars "{\"TLS_MODE\": \"$TLS_MODE\", \"DOMAIN_PRIMARY\": \"$DOMAIN\", \"users\": {\"administrator\": {\"authorized_keys\": [\"$SSH_PUBLIC_KEY\"]}}}" &&
infinito administration deploy dedicated "$INVENTORY/devices.yml" \
--password-file "$INVENTORY/.password" \
--diff -vv'
```
## Further Resources
- [Official Redis Docker image on Docker Hub](https://hub.docker.com/_/redis)
- [Redis ACL documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/)
- [Docker Compose reference](https://docs.docker.com/compose/compose-file/)
## Credits
Implemented by **[Kevin Veen-Birkenbach](https://www.veen.world)**.
Part of the [Infinito.Nexus Project](https://s.infinito.nexus/code) and maintained by [Kevin Veen-Birkenbach](https://www.veen.world).
Licensed under the [Infinito.Nexus Community License (Non-Commercial)](https://s.infinito.nexus/license).