Skip to content

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:

StackContainers (typical)
AppApp frontend + app backend (+ MongoDB, Valkey, …)
WorkbenchWorkbench 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:

ConcernInstance id (default)Typical durability
Raclette sessions (auth:session:*)cacheEphemeral β€” cleared on Valkey restart
OAuth CSRF state (auth:oauth_state:*)cacheEphemeral β€” 10 minute TTL
OAuth provider tokens (optional feature)cacheEphemeral unless you opt in elsewhere
Login rate limits, plugin fastify.cache, asset tokenscache (via fastify.cache)Ephemeral

Minimal raclette.config.js:

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 cache and every kind: valkey service start with --requirepass
  • Generated CACHE_URL / RACLETTE_VALKEY_*_URL defaults become redis://:${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: valkey in config (dev Compose is generated automatically) or an external cluster wired via RACLETTE_VALKEY_*_URL; use for durable integration snapshots or, if configured, OAuth provider tokens with persistProviderTokens.

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 variableInstance idWhen
CACHE_URLcachePrimary services.cache (always)
RACLETTE_VALKEY_<NAME>_URLservice key in camelCaseEach kind: valkey entry (CACHE_PERSISTENT β†’ cachePersistent)

Example:

js
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) ​

RoleConfig keyUsed forDefault instance id
Sessionssessionsauth:session:{sid}, user session indexcache
OAuth stateoauthStateauth:oauth_state:* (CSRF, ~10 min TTL)cache
Provider tokensproviderTokens{namespace}:oauth_tokens:{sid} when OAuth provider has persistProviderTokens: truecache

Example β€” single persistent Valkey for everything (customer project with one aof+rdb instance):

js
auth: {
  valkeyInstances: {
    sessions: "cache",
    oauthState: "cache",
    providerTokens: "cache",
  },
},
// Only services.cache + CACHE_URL; no second instance required.

Example β€” split ephemeral vs persistent:

js
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):

json
{
  "persistProviderTokens": true,
  "providerTokenNamespace": "your-plugin-key",
  "providerTokenValkeyInstance": "cachePersistent",
  "userInfoAuthorizationScheme": "JWT"
}

Resolution order for provider token storage:

  1. config.providerTokenValkeyInstance on the OAuth provider
  2. App/plugin global config (if your integration defines it)
  3. auth.valkeyInstances.providerTokens
  4. auth.valkeyInstances.sessions
  5. 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 ​

DataAPI / locationInstance selection
Session cookie β†’ session recordauth module, GET/POST /auth/*auth.valkeyInstances.sessions
OAuth CSRF stateOAuth routesauth.valkeyInstances.oauthState
OAuth access/refresh blobfastify.auth.*ProviderToken*See resolution order above
Login lockout / attempts/auth/loginfastify.cache (default cache)
Plugin entity cachefastify.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 ValkeySee Persistent external data
Asset download tokens/auth/asset/:tokenfastify.cache (default cache)
WebSocket session lookupSocket handshakefastify.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 ​

VariableInstance idWhen
CACHE_URLcacheAlways (required)
RACLETTE_VALKEY_<NAME>_URLservice 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 situationRecommendation
Standard raclette app, no upstream API tokens in ValkeyOne services.cache, persistence: none, omit auth.valkeyInstances
One ops-managed Valkey with RDB+AOF for everythingOne service; set all auth.valkeyInstances.* to "cache"
Sessions must flush on restart; upstream OAuth tokens should survive Valkey restartTwo 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 APIsPersistent external data β€” usually one Valkey with rdb+aof, or a second kind: valkey instance
One customer Valkey with aof+rdb for everythingSingle services.cache + backend.cache.persistence: "rdb+aof"; plugins use fastify.cache