Examples

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.

Environment#

Terminal
REDIS_URL=rediss://:PASSWORD@INSTANCE.eu.arctickey.com:6379

BullMQ#

TypeScript
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#

Python
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#

Environment
REDIS_URL=rediss://:PASSWORD@INSTANCE.eu.arctickey.com:6379 REDIS_CLIENT=predis QUEUE_CONNECTION=redis
PHP
// 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')], ],
PHP
// 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#

TypeScript
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#

TypeScript
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#

TypeScript
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#

  • Use rediss:// and TLS in every client.
  • Store secrets in environment variables, not source code.
  • Use IDs in keys and payloads instead of personal data.
  • Set TTLs for caches, sessions, rate limits, and task results.
  • Monitor memory, connected clients, command rate, latency, and failed backups.
  • Document retention behavior for queues and sessions in your internal data map.