Valkey instances β
racletteJS uses Valkey (Redis-compatible) for server-side data: plugin caches, optional integration snapshots, and (via auth) sessions and OAuth state. Most deployments use one Valkey with no persistence (persistence: none). Additional instances or persistence modes are opt-in for specific durability needs β see Persistent Valkey for external data for caching volatile upstream APIs.
You do not need three Valkey servers. The auth settings sessions, oauthState, and providerTokens are role names that each point at an instance id (for example "cache"). All three roles can reference the same instance.
Deployment: backend containers only β
Valkey connection env vars (CACHE_URL, RACLETTE_VALKEY_*_URL) are read by the backend process only. Frontend containers do not connect to Valkey.
Raclette runs frontend and backend as separate Docker services:
| Stack | Containers (typical) |
|---|---|
| App | App frontend + app backend (+ MongoDB, Valkey, β¦) |
| Workbench | Workbench frontend + workbench backend (separate from the app stack) |
Set cache URLs on each backend service in Compose (or your orchestrator). App and workbench backends may use different CACHE_URL values if they are separate deployments.
When the workbench stack runs without its own MongoDB/Valkey containers (because the app stack already started shared ones with the same services.mongodb.name / services.cache.name), backends connect via those container names on the external Docker network raclette_shared, not via compose service aliases mongodb / cache.
Default: one ephemeral instance β
If you only define services.cache and do not set auth.valkeyInstances, everything uses the instance id cache:
| Concern | Instance id (default) | Typical durability |
|---|---|---|
Raclette sessions (auth:session:*) | cache | Ephemeral β cleared on Valkey restart |
OAuth CSRF state (auth:oauth_state:*) | cache | Ephemeral β 10 minute TTL |
| OAuth provider tokens (optional feature) | cache | Ephemeral unless you opt in elsewhere |
Login rate limits, plugin fastify.cache, asset tokens | cache (via fastify.cache) | Ephemeral |
Minimal raclette.config.js:
export default defineRacletteConfig({
services: {
cache: {
enabled: true,
port: 6379,
name: "raclette-cache",
volume: "raclette-cache",
},
},
backend: {
cache: {
persistence: "none", // default for the primary cache container
},
},
// auth.valkeyInstances can be omitted β all roles default to "cache"
})Runtime connection: environment variable CACHE_URL on the backend container (set automatically in dev from services.cache).
Optional: Valkey requirepass β
Set RACLETTE_VALKEY_PASSWORD in your .env before generating Compose (dev) or in production env. When set:
- Primary
cacheand everykind: valkeyservice start with--requirepass - Generated
CACHE_URL/RACLETTE_VALKEY_*_URLdefaults becomeredis://:${RACLETTE_VALKEY_PASSWORD}@host:6379 - Healthchecks authenticate with the same password
Leave the variable unset to keep unauthenticated Valkey (previous default). Full URL overrides (RACLETTE_CACHE_URL=redis://:secret@external:6379) still work.
Optional: multiple physical instances β
Use a second Valkey container when some data should survive a Valkey restart (for example RDB + AOF on that container only), while keeping sessions on ephemeral storage.
Common pattern:
cacheβ no persistence; sessions, OAuth state, rate limits, plugin caches.- Another Valkey β
kind: valkeyin config (dev Compose is generated automatically) or an external cluster wired viaRACLETTE_VALKEY_*_URL; use for durable integration snapshots or, if configured, OAuth provider tokens withpersistProviderTokens.
1. Declare services in raclette.config.js β
The primary instance is always services.cache β CACHE_URL β instance id cache.
Additional instances use any non-built-in service key with kind: valkey. raclette sets RACLETTE_VALKEY_<NAME>_URL on the backend (override with envVar on the service). The instance id on fastify.valkeys matches the service key (e.g. cachePersistent).
| Env variable | Instance id | When |
|---|---|---|
CACHE_URL | cache | Primary services.cache (always) |
RACLETTE_VALKEY_<NAME>_URL | service key in camelCase | Each kind: valkey entry (CACHE_PERSISTENT β cachePersistent) |
Example:
services: {
cache: {
enabled: true,
port: 6379,
name: "raclette-cache",
volume: "raclette-cache",
},
cachePersistent: {
kind: "valkey",
enabled: true,
port: 6380,
name: "raclette-cache-persistent",
volume: "raclette-cache-persistent",
persistence: "rdb+aof",
},
},
backend: {
cache: { persistence: "none" },
},
auth: {
valkeyInstances: {
sessions: "cache",
oauthState: "cache",
providerTokens: "cachePersistent", // only when the persistent instance is configured
},
},In dev, yarn raclette dev starts both containers and injects RACLETTE_VALKEY_CACHE_PERSISTENT_URL=redis://raclette-cache-persistent:6379 on the backend. No hand-written Compose fragment is required.
For an external persistent cluster (production or existing customer volume), set enabled: false on the config entry or omit it, and set RACLETTE_VALKEY_<NAME>_URL in your deployment environment.
2. Map auth roles to instance ids (auth.valkeyInstances) β
| Role | Config key | Used for | Default instance id |
|---|---|---|---|
| Sessions | sessions | auth:session:{sid}, user session index | cache |
| OAuth state | oauthState | auth:oauth_state:* (CSRF, ~10 min TTL) | cache |
| Provider tokens | providerTokens | {namespace}:oauth_tokens:{sid} when OAuth provider has persistProviderTokens: true | cache |
Example β single persistent Valkey for everything (customer project with one aof+rdb instance):
auth: {
valkeyInstances: {
sessions: "cache",
oauthState: "cache",
providerTokens: "cache",
},
},
// Only services.cache + CACHE_URL; no second instance required.Example β split ephemeral vs persistent:
auth: {
valkeyInstances: {
sessions: "cache",
oauthState: "cache",
providerTokens: "cachePersistent",
},
},If an instance id is missing at runtime (for example providerTokens: "cachePersistent" but RACLETTE_VALKEY_CACHE_PERSISTENT_URL is unset), the backend falls back to fastify.cache (the default cache connection) and logs a warning when resolving connections.
3. Per-provider override (OAuth) β
For a single integration that needs a different store, set on the OAuth provider document in MongoDB (config in workbench Settings β Authentication):
{
"persistProviderTokens": true,
"providerTokenNamespace": "your-plugin-key",
"providerTokenValkeyInstance": "cachePersistent",
"userInfoAuthorizationScheme": "JWT"
}Resolution order for provider token storage:
config.providerTokenValkeyInstanceon the OAuth provider- App/plugin global config (if your integration defines it)
auth.valkeyInstances.providerTokensauth.valkeyInstances.sessions- Instance id
cache
Plugins must not open Valkey directly for provider tokens. Use fastify.auth.getProviderTokenBlob / setProviderTokenBlob / deleteProviderTokenBlob so the correct instance is always used.
What uses which connection β
| Data | API / location | Instance selection |
|---|---|---|
| Session cookie β session record | auth module, GET/POST /auth/* | auth.valkeyInstances.sessions |
| OAuth CSRF state | OAuth routes | auth.valkeyInstances.oauthState |
| OAuth access/refresh blob | fastify.auth.*ProviderToken* | See resolution order above |
| Login lockout / attempts | /auth/login | fastify.cache (default cache) |
| Plugin entity cache | fastify.cache on plugin instance (prefixed by plugin key) | fastify.cache (default cache) |
| External API snapshots (optional) | Plugin createCacheService on fastify.valkeys.<id> or fastify.cache on one persistent Valkey | See Persistent external data |
| Asset download tokens | /auth/asset/:token | fastify.cache (default cache) |
| WebSocket session lookup | Socket handshake | fastify.cache (default cache) β use the same host as sessions when they share id cache |
Keep fastify.cache and auth.valkeyInstances.sessions on the same logical instance unless you have a deliberate split and understand socket/session consistency.
Environment variables β
| Variable | Instance id | When |
|---|---|---|
CACHE_URL | cache | Always (required) |
RACLETTE_VALKEY_<NAME>_URL | service key (camelCase from <NAME>) | Each additional Valkey instance |
<NAME> is the service key in SNAKE_CASE: cachePersistent β RACLETTE_VALKEY_CACHE_PERSISTENT_URL. Optional per-service envVar in config overrides the generated name.
Decision guide β
| Your situation | Recommendation |
|---|---|
| Standard raclette app, no upstream API tokens in Valkey | One services.cache, persistence: none, omit auth.valkeyInstances |
| One ops-managed Valkey with RDB+AOF for everything | One service; set all auth.valkeyInstances.* to "cache" |
| Sessions must flush on restart; upstream OAuth tokens should survive Valkey restart | Two services: ephemeral cache + kind: valkey persistent instance; route only providerTokens (and provider override) to persistent |
| Need separate OAuth state store (unusual) | Set oauthState to another registered instance id |
| Cache snapshots from volatile external APIs | Persistent external data β usually one Valkey with rdb+aof, or a second kind: valkey instance |
| One customer Valkey with aof+rdb for everything | Single services.cache + backend.cache.persistence: "rdb+aof"; plugins use fastify.cache |
Related β
- Migrate: ephemeral auth + persistent data β customer already on one
rdb+aofValkey adopting new core - Persistent Valkey for external data β upstream API snapshots,
rdb+aof, refresh patterns - Config boilerplate: persistent Valkey β copy-paste
raclette.config.js - Authentication overview β sessions, cookies, logout (clears provider tokens on the resolved instance)
- OAuth providers β
persistProviderTokensand providerconfig - raclette Config reference β
services.cache,kind: valkey,auth.valkeyInstances