> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentfront.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Redis Setup

> FrontMCP uses Redis for caching, session storage, and distributed state management.

<Tip>
  **Deploying to Vercel?** Consider [Vercel KV](/frontmcp/deployment/vercel-kv) for edge-compatible storage without managing Redis infrastructure.
</Tip>

## Requirements

| Environment | Redis | Notes |
| - | - | - |
| Development | Recommended | Enables full feature testing |
| Production | **Required** | Essential for multi-instance deployments |

## Development Setup

### Option 1: Docker Compose (Recommended)

Projects created with `frontmcp create --target node` include Docker files in the `ci/` folder:

```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
# Start Redis
docker compose -f ci/docker-compose.yml up redis -d

# Verify Redis is running
docker compose -f ci/docker-compose.yml exec redis redis-cli ping

# Start the full stack (app + Redis)
docker compose -f ci/docker-compose.yml up
```

<Tip>
  When running inside Docker, use `redis` (the service name) as `REDIS_HOST`, not `localhost`.
  Projects created with `frontmcp create` include a `ci/.env.docker` file with Docker-specific values.
</Tip>

### Option 2: Local Installation

```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
# macOS
brew install redis
brew services start redis

# Ubuntu/Debian
sudo apt install redis-server
sudo systemctl start redis
```

### Configuration

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { CachePlugin } from '@frontmcp/plugin-cache';

CachePlugin.init({
  type: 'redis',
  config: {
    host: process.env.REDIS_HOST || 'localhost',
    port: Number(process.env.REDIS_PORT || 6379),
    password: process.env.REDIS_PASSWORD,
    db: parseInt(process.env.REDIS_DB || '0'),
    tls: process.env.REDIS_TLS === 'true',
  }
});
```

<Tip>
  Already configuring the top-level `redis` block (or `redis.provider: 'vercel-kv'`) on `@FrontMcp`? Set `CachePlugin.init({ type: 'global-store' })` so the plugin automatically reuses that store without duplicating credentials.
</Tip>

For complete CachePlugin API documentation, see the [Cache Plugin Guide](/frontmcp/plugins/cache-plugin).

## Production Setup

<Warning>
  Redis is **required** in production. Without Redis, session data will be lost on restart and caching will not persist across instances.
</Warning>

### Managed Redis Services (Recommended)

| Provider | Service |
| - | - |
| AWS | ElastiCache for Redis |
| GCP | Memorystore for Redis |
| Azure | Azure Cache for Redis |
| Upstash | Serverless Redis |
| Redis Cloud | Redis Enterprise |

### Self-Hosted Production

For self-hosted Redis in production:

```yaml theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
# docker-compose.prod.yml
services:
  redis:
    image: redis:7-alpine
    command: >
      redis-server
      --appendonly yes
      --requirepass ${REDIS_PASSWORD}
      --maxmemory 256mb
      --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    restart: unless-stopped
```

### Security Best Practices

1. **Authentication**: Always set `REDIS_PASSWORD` in production
2. **Network**: Run Redis in private network, not exposed to internet
3. **TLS**: Enable TLS for encrypted connections
4. **Persistence**: Use AOF for durability (`appendonly yes`)

### Configuration Example

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
// src/main.ts
import 'reflect-metadata';
import { FrontMcp } from '@frontmcp/sdk';
import { CachePlugin } from '@frontmcp/plugin-cache';

@FrontMcp({
  info: { name: 'My Server', version: '1.0.0' },
  redis: {
    host: process.env.REDIS_HOST,
    port: Number(process.env.REDIS_PORT || 6379),
    password: process.env.REDIS_PASSWORD,
    tls: process.env.REDIS_TLS === 'true',
  },
  plugins: [
    CachePlugin.init({
      type: 'global-store',
      defaultTTL: 600,
    }),
  ],
})
export default class Server {}
```

Switch the `redis` block to `{ provider: 'vercel-kv' }` when deploying on Vercel KV—the Cache Plugin will keep reusing the same store without code changes.

### Running more than one instance

Redis lets several instances share sessions, but each instance still signs and encrypts with its own secrets. Give every instance the **same** values:

| Variable | What breaks when instances differ |
| - | - |
| `MCP_SESSION_SECRET` | Session ids are encrypted with it. An id minted under a different secret is not this deployment's: the server answers the request with HTTP 404 and the client re-initializes. This holds in every auth mode, including `public`, where the id is the caller's only credential. |
| `VAULT_SECRET` | Signs MCP 2026-07-28 `requestState` (falling back to `JWT_SECRET`, then a random per-process key). Under the per-process key, a multi-round tool (`elicit()` / `sample()`) whose next round lands on another instance asks its first question again. In production, `redis` or `transport.persistence` without `VAULT_SECRET`/`JWT_SECRET` logs a startup warning, and each rejected round logs a `hint` naming `VAULT_SECRET`. |

An anonymous session minted by one instance is honored by any instance with the same `MCP_SESSION_SECRET`: the instance that receives the request recreates the transport from the stored session (in distributed mode it relays to, or takes over from, the node recorded with that session).

### When Redis is unreachable at startup

Not every Redis consumer reacts the same way when Redis is down as the server starts:

* **`redis` and `transport.persistence`** log the failure (`[TransportService] Failed to connect to redis - session persistence disabled`) and the server starts. The connection is retried in the background with exponential backoff (1s doubling to 30s); once Redis is reachable, sessions are persisted again and `/readyz` turns healthy without a restart. Until then, sessions live on the instance that created them.
* Every Redis connection FrontMCP opens reconnects on its own and logs connection errors at a rate-limited interval (with a count of suppressed repeats) rather than once per retry.
* **`throttle.storage`** fails closed: rate limits are a security control, so startup aborts with `GuardStorageUnavailableError` (`throttle.storage (redis) is unavailable: …`). This is the default in production, where storage `fallback` defaults to `'error'`. If Redis goes away while the server is running, a rate-limited call is refused with the same `GuardStorageUnavailableError` rather than an internal error. To keep serving with per-instance counters instead, opt in explicitly (a mid-run outage then logs one warning, uses per-instance counters, and returns to Redis once it answers):

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@FrontMcp({
  throttle: {
    enabled: true,
    storage: {
      type: 'redis',
      redis: { config: { host: process.env.REDIS_HOST ?? 'localhost', port: 6379 } },
      fallback: 'memory', // per-instance counters while Redis is down
    },
    global: { maxRequests: 1000, windowMs: 60_000, partitionBy: 'ip' },
  },
})
```

`throttle.storage` takes the `@frontmcp/utils` storage shape (`{ type: 'redis', redis: { config } }` or `{ type: 'redis', redis: { url } }`), not the top-level `redis` shape.

## Environment Variables

### Redis Configuration

| Variable | Required | Default | Description |
| - | - | - | - |
| `REDIS_HOST` | Yes | `localhost` | Redis server hostname |
| `REDIS_PORT` | No | `6379` | Redis server port |
| `REDIS_PASSWORD` | Prod only | - | Redis authentication password |
| `REDIS_DB` | No | `0` | Redis database number |
| `REDIS_TLS` | No | `false` | Enable TLS encryption |
| `REDIS_KEY_PREFIX` | No | `mcp:` | Key prefix for cache namespacing (CachePlugin uses content hashing by default) |

### Plugin Secrets

| Variable | Required | Default | Description |
| - | - | - | - |
| `REMEMBER_SECRET` | Prod only | Auto-generated | Encryption secret for RememberPlugin (32+ byte base64-encoded string) |

<Info>
  In development, `REMEMBER_SECRET` is auto-generated and stored in `.frontmcp/remember-secret.json`. Add this file to `.gitignore`. In production, always set `REMEMBER_SECRET` explicitly to ensure consistent encryption across instances.
</Info>

## Health Checks

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import Redis from 'ioredis';

async function checkRedisHealth(): Promise<boolean> {
  const redis = new Redis({
    host: process.env.REDIS_HOST,
    port: Number(process.env.REDIS_PORT || 6379),
    password: process.env.REDIS_PASSWORD,
    lazyConnect: true,
  });

  try {
    await redis.connect();
    await redis.ping();
    await redis.quit();
    return true;
  } catch {
    return false;
  }
}
```

## Troubleshooting

### Connection Refused

* Verify Redis is running: `redis-cli ping`
* Check firewall rules
* Verify host/port configuration
* If startup fails, or rate-limited calls are refused, with `GuardStorageUnavailableError`, the `throttle.storage` backend is down; see [When Redis is unreachable at startup](#when-redis-is-unreachable-at-startup)

### Clients keep re-initializing or tools repeat their questions

* A `404` for a session the client just used, on a load-balanced deployment, means the instances run with different `MCP_SESSION_SECRET` values
* A 2026-07-28 tool that asks its first question again after it was answered, with `mcp-20260728: rejected requestState` and `reason: 'bad-signature'` in the log, means `VAULT_SECRET` (or `JWT_SECRET`) is unset or differs between instances

### Authentication Failed

* Ensure `REDIS_PASSWORD` matches server config
* Check for special characters in password (may need URL encoding)

### Memory Issues

* Set `maxmemory` and `maxmemory-policy` in Redis config
* Monitor with `redis-cli INFO memory`

### Docker Network Issues

When using Docker Compose, the app container should use `redis` as the hostname (the service name), not `localhost`:

```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
# Inside Docker container
REDIS_HOST=redis

# Outside Docker (local development)
REDIS_HOST=localhost
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.