Redis
In-memory data store for caching, sessions, queues, and real-time data. Sub-millisecond reads and writes.

Through TenantsDB, each tenant gets isolated key-value storage. On L1 shared instances the proxy prefixes every key with the tenant's namespace so tenants never see each other's data. On L2 dedicated instances each tenant has its own Redis process. Your app connects with any standard Redis client. No SDK needed.

Redis works differently from the SQL and MongoDB databases in TenantsDB. There is no schema, no tables, no blueprints to deploy, and no DDL. You create a Redis blueprint to define settings (default TTL, max keys, and per-pattern TTL rules), then create tenants. Each tenant connects and starts using Redis commands immediately.

Redis also uses a different connection-string layout: the proxy reads the username field to decide what to route to. The blueprint name reaches the workspace; a tenant ID reaches that tenant. The password is always your proxy_password. TLS is enabled via the rediss:// scheme (double-s) for all clients.


Credentials
TenantsDB issues three credential scopes. The proxy enforces which one can reach which target.

Every Redis connection uses a routing identifier as the username (either a blueprint name for workspace mode, or a tenant ID for tenant mode) and a proxy_password. Pick the scope by matching the reach you need.

Project scope

The proxy_password printed when you created the project (or any sk_ key with scope_type=project). Reaches workspaces and the control plane API. Cannot reach individual tenants through the wire proxy. Use for admin tooling, the dashboard, and CLI calls.

Do not pass the sk_ value as the Redis password. Use the project proxy_password instead. sk_ keys are for HTTP API calls only; the wire proxy rejects them on tenant routing as of the latest release.
Workspace scope

Generated by tdb apikeys create --scope-type workspace --scope-values myapp. Reaches the named workspace AND every tenant under that blueprint. Use for backend jobs that touch all customers of a blueprint, such as cache warming, key-space sweeps, and analytics ingest.

Tenant scope

Auto-generated on every tdb tenants create and returned in the response connection_string. Reaches one tenant only. Use this for the per-customer Redis connection in your application.

Need a read-only or write-only key for the same tenant? Create one with tdb apikeys create --role read --scope-type tenant --scope-values wayne.

Every credential also carries a role (admin, write, or read). The proxy logs in to the backend as a native Redis ACL user that enforces permissions at the Redis layer. A read key cannot execute SET even if scope allows the connection. Scope and role stack: defense in depth.

Connect to Workspace
Test your Redis setup and configure settings. Use the blueprint name as the username.
Shell
redis-cli --tls -u "rediss://myapp:tdb_d2bf66ed7898c448@redis.tenantsdb.com:6379/0"

The username field carries the blueprint name (myapp in the example above). The password is your project proxy_password or a workspace-scoped proxy_password for that blueprint.

Workspace connections are useful for testing settings, inspecting workspace-level keys, and running maintenance scripts that don't belong to any single tenant. Workspace keys are separate from tenant keys.

Redis has no schema or blueprint deployment. The blueprint exists to group tenants and hold settings (default TTL, max keys, and per-pattern TTL rules) that apply to every tenant of that blueprint.

Connect to Tenant
Each tenant connects with their own tenant ID as the username and their own per-tenant proxy_password.

Each tenant has its own proxy_password. The value is returned in the connection_string field of the tdb tenants create response. Save it alongside the tenant record in your application, keyed by tenant_id.

wayne
Shell
redis-cli --tls -u "rediss://wayne:tdb_4f2c9d1ab7e8350c@redis.tenantsdb.com:6379/0"
globex
Shell
redis-cli --tls -u "rediss://globex:tdb_e7b1f5c821a04d68@redis.tenantsdb.com:6379/0"
Same project, same host, different tenant ID in the username, different password. Each tenant carries its own proxy_password so credentials never travel across customers. Tenant wayne and tenant globex can both have a key called user:1 with different values, and a leaked wayne password cannot read globex.
If you connect using the project proxy_password or an sk_ key as a tenant password, authentication fails with: AUTH failed: ERR access denied. The wire proxy underlying reason is credential is project-scoped; direct-tenant connections require a tenant-scoped or workspace-scoped key. This is intentional. The project credential is for workspace and control plane access only.

Add Data
Redis supports strings, hashes, lists, sets, and sorted sets. No schema required.
Redis
# Strings
SET user:1 "Alice"
SET user:2 "Bob"

# Hashes (structured data)
HSET account:1 name "Alice" balance 1000
HSET account:2 name "Bob" balance 2000

# With expiration (seconds)
SET session:abc "session_data" EX 3600

# Lists (queues)
LPUSH queue:jobs "job1" "job2" "job3"

All standard Redis commands work through the proxy. The proxy transparently prefixes keys on L1 shared instances for tenant isolation. Your application never needs to handle prefixing.


Clients
Copy-paste examples for every supported client. Each example connects to tenant wayne using its per-tenant proxy_password. For workspace connections, replace the username with the blueprint name and the password with your project or workspace-scoped proxy_password.
ioredis (Node.js)
JavaScript
const Redis = require('ioredis');

const redis = new Redis(process.env.REDIS_URL);
// REDIS_URL=rediss://wayne:tdb_4f2c9d1ab7e8350c@redis.tenantsdb.com:6379/0

// String
await redis.set('user:1', 'Alice');
const name = await redis.get('user:1');

// Hash
await redis.hset('account:1', 'name', 'Alice', 'balance', '1000');
const balance = await redis.hget('account:1', 'balance');

// With expiration
await redis.set('session:abc', 'data', 'EX', 3600);
Install
npm install ioredis
ioredis handles the rediss:// scheme natively for TLS connections. Look up the per-tenant proxy_password by tenant ID in your application's secret store.
redis-py (Python)
Python
import redis
import os

r = redis.from_url(os.environ['REDIS_URL'])
# REDIS_URL=rediss://wayne:tdb_4f2c9d1ab7e8350c@redis.tenantsdb.com:6379/0

# String
r.set('user:1', 'Alice')
name = r.get('user:1')  # b"Alice"

# Hash
r.hset('account:1', mapping={'name': 'Alice', 'balance': '1000'})
balance = r.hget('account:1', 'balance')  # b"1000"

# With expiration
r.set('session:abc', 'data', ex=3600)
Install
pip install redis
redis-py returns bytes by default. Use decode_responses=True in the connection to get strings instead.
Go (go-redis)
Go
package main

import (
    "context"
    "fmt"
    "os"
    "time"

    "github.com/redis/go-redis/v9"
)

func main() {
    ctx := context.Background()

    opt, _ := redis.ParseURL(os.Getenv("REDIS_URL"))
    // REDIS_URL=rediss://wayne:tdb_4f2c9d1ab7e8350c@redis.tenantsdb.com:6379/0
    opt.Protocol = 2  // pin RESP2; some go-redis versions request RESP3 upgrades
    rdb := redis.NewClient(opt)

    // String
    rdb.Set(ctx, "user:1", "Alice", 0)
    name, _ := rdb.Get(ctx, "user:1").Result()
    fmt.Println(name)

    // Hash
    rdb.HSet(ctx, "account:1", "name", "Alice", "balance", "1000")
    balance, _ := rdb.HGet(ctx, "account:1", "balance").Result()
    fmt.Println(balance)

    // With expiration
    rdb.Set(ctx, "session:abc", "data", 60*time.Second)
}
Install
go get github.com/redis/go-redis/v9
go-redis parses the rediss:// scheme and enables TLS automatically. Setting opt.Protocol = 2 avoids RESP3 negotiation, which not all proxy paths support yet.

Proxy Behavior
Redis-specific details about how the proxy handles your commands.
Key Prefixing (L1)

On L1 shared instances, all tenants share the same Redis server. The proxy isolates tenants by automatically prefixing every key with the tenant's namespace. Your application sends SET user:1 "Alice" and the proxy stores it as tdb_xxx_tenant_wayne:user:1. On read, the prefix is stripped. Your application never sees the prefix.

On L2 dedicated instances each tenant has its own Redis process, so no prefixing is applied. The connection string stays the same in both modes; tenants can be moved between L1 and L2 without code changes.

Authentication

The proxy uses standard Redis AUTH with username and password (Redis 6+ ACL auth). Pass the routing identifier as the username and your proxy_password as the password.

Connecting toUsernamePassword
A workspace Blueprint name (e.g., myapp) Project or workspace-scoped proxy_password
A tenant Tenant ID (e.g., wayne) Tenant-scoped or workspace-scoped proxy_password
The Redis password is the proxy_password from tdb tenants create or tdb apikeys create. It is not the sk_ value (which is for HTTP API calls only). Older docs that suggested using sk_ as the Redis password are obsolete and the wire proxy now rejects that pattern.
Credential Scope

The proxy enforces credential scope at the AUTH step. Project credentials can connect to workspaces. Workspace-scoped credentials can connect to their workspace AND every tenant of that blueprint. Tenant-scoped credentials can only connect to their specific tenant.

Using a project credential against a tenant fails with:

Error
AUTH failed: ERR access denied

The proxy log records the underlying reason: credential is project-scoped; direct-tenant connections require a tenant-scoped or workspace-scoped key. Using a workspace-scoped credential against a different blueprint's tenant produces the same generic AUTH failed response with a scope-mismatch reason in the log.

Role Enforcement

Each API key has a role (admin, write, or read) which the proxy maps to a native Redis ACL user on the backend connection. A read-role key attempting a SET fails from Redis itself, not just the proxy.

Through the wire proxy the failure looks like:

Error
(error) NOPERM this user has no permissions to run the 'set' command

Through the control plane POST /tenants/{id}/query endpoint the same operation returns HTTP 403 with code: permission_denied and the Redis NOPERM message in the error body. Same underlying Redis ACL user, two different transports.

The role users also restrict the key space they can touch. On L1 the ACL pattern is scoped to ~tdb_xxx_tenant_wayne:*, blocking cross-tenant KEYS or SCAN attempts at the Redis layer even if the proxy were bypassed.

Cross-Protocol Guard

If you connect to a workspace or tenant whose blueprint targets a different database type (PostgreSQL, MySQL, MongoDB), the proxy rejects the connection with a clear pointer to the right proxy:

Error
blueprint "pg_test" is PostgreSQL, not Redis. Connect via the postgresql proxy instead.
Connection Limits & Auth-Ban

The proxy enforces per-IP connection limits and a wire-level auth-ban tracker. These apply to Redis the same way they apply to PostgreSQL, MySQL, and MongoDB. Full values and rejection message formats live in Connection Limits & Rejection Behavior.

For Redis specifically, IP-level rejections arrive as a standard -ERR reply with the connection rejected: prefix. Standard clients (ioredis, redis-py, go-redis, jedis) surface this as a normal connection error. Example:

Error
(error) ERR connection rejected: your IP is temporarily rate-limited after repeated failed auth attempts, retry in 47s

The TTL portion (retry in 47s) decrements on each retry while the ban is active, so client retry logic can parse it for accurate backoff. The auth-ban tracker counts failed AUTH commands, so wrong-password retry storms from broken application config will trigger it. Distinct from scope rejections (which surface as AUTH failed: ERR access denied), IP-level rejections use the connection rejected: prefix so client-side error handling can branch on them cleanly.

Settings Enforcement

Redis settings are configured per workspace and differ from the SQL and MongoDB settings.

SettingDescription
default_ttl Default expiration (seconds) applied to keys that do not have an explicit TTL set.
max_keys Maximum number of keys allowed per tenant.
patterns Pattern-specific TTL rules. Each pattern (e.g., session:*) can have its own TTL and an enforced flag that overrides user-set TTLs.
When more than one rule could apply to a key, precedence runs in this order: an enforced pattern wins over everything and replaces an explicit EX or PX on the write. A non-enforced pattern or default_ttl applies only when the write sets no expiry of its own. Patterns are always checked before default_ttl.
TLS

All connections require TLS. Use the rediss:// scheme (double-s) in your connection URL. TLS is terminated at the edge by HAProxy. Every Redis client library supports rediss:// natively.

Limits

There are no proxy-level size restrictions. The Redis native limit of 512MB per value applies. Keep in mind that large values use their full size in memory on the Redis server. Connection strings stay the same when tenants migrate between L1 and L2.