Use these examples as starting points for EU-hosted Redis-compatible workloads. Replace REDIS_URL with the connection string from your instance Connect tab.
Prefer opaque internal IDs in keys and payloads. Avoid emails, names, addresses, or full personal records in Redis keys.
Examples
Use these examples as starting points for EU-hosted Redis-compatible workloads. Replace
REDIS_URLwith the connection string from your instance Connect tab.Prefer opaque internal IDs in keys and payloads. Avoid emails, names, addresses, or full personal records in Redis keys.
Environment#
REDIS_URL=rediss://:PASSWORD@INSTANCE.eu.arctickey.com:6379BullMQ#
import { Queue, Worker } from "bullmq"; const connection = { url: process.env.REDIS_URL, tls: { rejectUnauthorized: false }, }; export const invoices = new Queue("invoices", { connection, defaultJobOptions: { attempts: 3, backoff: { type: "exponential", delay: 1000 }, removeOnComplete: { age: 3600 }, removeOnFail: { age: 86400 }, }, }); await invoices.add("send", { invoiceId: "inv_123", accountId: "acc_123" }); new Worker( "invoices", async (job) => { await sendInvoice(job.data.invoiceId); }, { connection }, );For GDPR workflows, keep only IDs in queue payloads and load personal data from your primary database inside the worker.
Celery#
import os from celery import Celery redis_url = os.environ["REDIS_URL"] app = Celery( "worker", broker=redis_url, backend=redis_url, broker_use_ssl={"ssl_cert_reqs": "CERT_NONE"}, redis_backend_use_ssl={"ssl_cert_reqs": "CERT_NONE"}, ) app.conf.update( task_acks_late=True, task_reject_on_worker_lost=True, result_expires=3600, ) @app.task(bind=True, autoretry_for=(Exception,), retry_backoff=True, max_retries=3) def send_invoice(self, invoice_id: str): return deliver_invoice(invoice_id)Use short result retention when task results can contain personal data or business-sensitive metadata.
Laravel Horizon#
REDIS_URL=rediss://:PASSWORD@INSTANCE.eu.arctickey.com:6379 REDIS_CLIENT=predis QUEUE_CONNECTION=redis// config/database.php 'redis' => [ 'client' => env('REDIS_CLIENT', 'predis'), 'options' => [ 'scheme' => 'tls', 'parameters' => [ 'ssl' => ['verify_peer' => false, 'verify_peer_name' => false], ], ], 'default' => ['url' => env('REDIS_URL')], 'horizon' => ['url' => env('REDIS_URL')], ],// app/Jobs/SendInvoice.php final class SendInvoice implements ShouldQueue { public function __construct(public string $invoiceId) {} public function handle(): void { InvoiceMailer::send($this->invoiceId); } } SendInvoice::dispatch($invoice->id)->onQueue('invoices');Keep Horizon access behind your normal admin authentication and avoid exposing job payloads to broad support roles.
Rate Limiting#
import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!, { tls: { rejectUnauthorized: false }, }); export async function rateLimit(subject: string, limit = 100, windowSeconds = 60) { const key = `rate:${subject}:${Math.floor(Date.now() / (windowSeconds * 1000))}`; const count = await redis.incr(key); if (count === 1) { await redis.expire(key, windowSeconds); } return { allowed: count <= limit, remaining: Math.max(0, limit - count), resetSeconds: windowSeconds, }; }Use account IDs or hashed IP identifiers in keys. Do not store raw IP addresses longer than your policy requires.
Sessions#
import { randomUUID } from "node:crypto"; import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!, { tls: { rejectUnauthorized: false }, }); export async function createSession(userId: string) { const sessionId = randomUUID(); const ttl = 60 * 60 * 24; await redis .multi() .set(`session:${sessionId}`, JSON.stringify({ userId }), "EX", ttl) .sadd(`user:${userId}:sessions`, sessionId) .expire(`user:${userId}:sessions`, ttl) .exec(); return sessionId; } export async function logoutEverywhere(userId: string) { const sessionIds = await redis.smembers(`user:${userId}:sessions`); if (sessionIds.length > 0) { await redis.del(...sessionIds.map((id) => `session:${id}`)); } await redis.del(`user:${userId}:sessions`); }Use signed, HttpOnly cookies for session IDs and store the smallest possible server-side session record.
Caching#
import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!, { tls: { rejectUnauthorized: false }, }); export async function cached<T>( key: string, ttlSeconds: number, load: () => Promise<T>, ): Promise<T> { const cachedValue = await redis.get(key); if (cachedValue) { return JSON.parse(cachedValue) as T; } const freshValue = await load(); await redis.set(key, JSON.stringify(freshValue), "EX", ttlSeconds); return freshValue; } const product = await cached( `product:${productId}`, 300, () => db.products.findById(productId), );Set TTLs for cache keys that may contain derived personal data, and invalidate affected keys during account deletion workflows.
Production Checklist#
rediss://and TLS in every client.