Skip to main content

Redis Configuration

EmailEngine requires Redis as its primary data store for mailbox indexes, OAuth credentials, job queues, and webhook events. This guide covers how to configure Redis for EmailEngine and optimize its performance.

Connecting EmailEngine to Redis

Connection String Format

EmailEngine connects to Redis using a connection string specified via the EENGINE_REDIS environment variable or --dbs.redis command-line argument.

Format:

redis://[[username:]password@]host[:port][/database]

Examples:

# Local Redis (default port 6379, database 0)
EENGINE_REDIS="redis://localhost:6379/8"

# Local Redis with database 8
EENGINE_REDIS="redis://localhost:6379/8"

# Remote Redis with password
EENGINE_REDIS="redis://:mypassword@redis.example.com:6379"

# Redis with username and password (Redis 6+)
EENGINE_REDIS="redis://admin:mypassword@redis.example.com:6379"

# Redis over TLS
EENGINE_REDIS="rediss://redis.example.com:6380"

Deployment Examples

version: '3.8'

services:
redis:
image: redis:7-alpine
command: redis-server --save 60 1000 --save 300 10 --save 900 1 --maxmemory-policy noeviction
volumes:
- redis-data:/data
ports:
- "6379:6379"
restart: unless-stopped

emailengine:
image: postalsys/emailengine:latest
environment:
- EENGINE_REDIS=redis://redis:6379
- EENGINE_HOST=0.0.0.0
- EENGINE_PORT=3000
ports:
- "3000:3000"
depends_on:
- redis
restart: unless-stopped

volumes:
redis-data:

Connection Options

For advanced Redis configurations, you can pass connection options as query parameters:

# Connect with TLS
EENGINE_REDIS="rediss://redis.example.com:6380"

# Specify IPv4 or IPv6
EENGINE_REDIS="redis://redis.example.com:6379?family=4" # Force IPv4
EENGINE_REDIS="redis://redis.example.com:6379?family=6" # Force IPv6

Required Redis Configuration

EmailEngine requires specific Redis settings to function correctly.

Memory Eviction Policy (Required)

EmailEngine requires that all keys remain in memory. Set the eviction policy to noeviction:

maxmemory-policy noeviction

Why this matters: If Redis evicts mailbox indexes or OAuth tokens, EmailEngine must resynchronize entire mailboxes, which is expensive and time-consuming.

Verification:

redis-cli CONFIG GET maxmemory-policy

Set at runtime:

redis-cli CONFIG SET maxmemory-policy noeviction

Alternative (if memory limits required): Use a volatile-* policy so only keys with TTL are evicted:

maxmemory-policy volatile-lru

Never use: allkeys-* policies, as they may evict critical mailbox data.

Enable persistence to prevent data loss on Redis restarts.

# Save after 1 change in 15 minutes
save 900 1

# Save after 10 changes in 5 minutes
save 300 10

# Save after 10000 changes in 1 minute
save 60 10000

Why recommended: RDB creates periodic snapshots with minimal performance impact. Best for EmailEngine's write-heavy workload.

Verification:

redis-cli CONFIG GET save

Configure TCP keep-alive to maintain long-lived Pub/Sub connections:

tcp-keepalive 300

Why this matters: EmailEngine uses Redis Pub/Sub for real-time updates. Without keep-alive, NAT devices or load balancers may silently drop idle connections.

Redis Configuration File Example

Create a redis.conf file with EmailEngine-optimized settings:

# Bind to all interfaces (adjust for security)
bind 0.0.0.0

# Port
port 6379

# Memory limit (adjust based on your needs)
maxmemory 2gb

# Eviction policy - REQUIRED for EmailEngine
maxmemory-policy noeviction

# Persistence - RDB snapshots (RECOMMENDED)
save 900 1
save 300 10
save 60 10000

# Persistence - AOF (NOT RECOMMENDED for EmailEngine)
# Only enable if you have high-performance storage (20,000+ IOPS)
# and understand the performance impact
appendonly no

# TCP keep-alive
tcp-keepalive 300

# Log level
loglevel notice

# Log file
logfile /var/log/redis/redis.log

# Working directory
dir /var/lib/redis

Start Redis with config file:

redis-server /etc/redis/redis.conf

Verifying Connection

Test Redis Directly

# Test connectivity
redis-cli -h localhost -p 6379 ping
# Expected: PONG

Check EmailEngine Logs

EmailEngine uses JSON logging (pino). Log levels: 60=FATAL, 50=ERROR, 40=WARN, 30=INFO, 20=DEBUG, 10=TRACE.

Successful connection:

{"level":30,"time":1762176419767,"pid":93728,"msg":"EmailEngine starting up","version":"2.58.0"}
{"level":20,"time":1762176421071,"pid":93728,"msg":"Started API server thread","port":3000,"host":"127.0.0.1"}

No explicit "Redis connected" message. If "Started API server thread" appears (at debug level), Redis is connected.

Connection failure:

============================================================================================================
Failed to establish connection to Redis using "redis://127.0.0.1:16379"
Can not connect to the database. Redis might not be running.
============================================================================================================

Common errors:

Connection refused:

{"level":60,"time":1762176637410,"pid":2625,"msg":"EmailEngine starting up"}

Invalid hostname:

{"level":60,"msg":false,"err":{"message":"getaddrinfo ENOTFOUND invalid-host","code":"ENOTFOUND"}}
{"level":10,"msg":"Connection retry","times":1,"delay":1000}

Pretty format (development):

emailengine | pino-pretty

Data Stored in Redis

Data TypeTypical Size
Mailbox indexes1-2 MiB/account
OAuth tokens~1 KiB/account
Webhook queueVaries
Outbox queueVaries
Account metadata~2 KiB/account
Web sessions~500 bytes/session

Check memory usage:

redis-cli INFO memory | grep used_memory_human

Capacity Planning

Memory Sizing

Allocate 1–2 MiB of RAM per account and provision 2× the calculated baseline to accommodate:

  • Copy-on-write memory during RDB snapshots
  • Webhook and outbox queue bursts
  • Keep usage below 80% of provisioned memory

Example:

AccountsBase RAMProvisionTarget Usage
100100-200 MiB400 MiB< 320 MiB
1,0001-2 GiB4 GiB< 3.2 GiB
10,00010-20 GiB40 GiB< 32 GiB

Network Latency

Deploy Redis and EmailEngine in the same availability zone. Target RTT < 5ms (ideally < 1ms).

Measure latency:

redis-cli --latency --raw -h <redis-host>

Backups

Back up dump.rdb regularly:

#!/bin/bash
BACKUP_DIR="/backup/redis"
DATE=$(date +%Y%m%d_%H%M%S)
redis-cli BGSAVE
while [ $(redis-cli LASTSAVE) -eq $LASTSAVE ]; do sleep 1; done
cp /var/lib/redis/dump.rdb "$BACKUP_DIR/dump-$DATE.rdb"
find "$BACKUP_DIR" -name "dump-*.rdb" -mtime +7 -delete

Performance Tuning

Why AOF is Dangerous for EmailEngine

EmailEngine's write patterns make AOF persistence problematic:

  • Initial sync: 5,000-10,000+ writes/second per account
  • Continuous sync: 100-1,000 writes/second per active account
  • Large mailboxes: Sustained high writes for hours
  • Multiple accounts: Write loads multiply

AOF problems:

  • Logs every write to disk (requires 20,000+ IOPS sustained)
  • AOF rewrites lock Redis, causing timeouts
  • Rapid disk space consumption
  • Performance degradation

Solution: Use RDB snapshots instead. Lower overhead, minimal performance impact.

Memory Management

Monitor fragmentation:

redis-cli INFO memory | grep mem_fragmentation_ratio

Target: 1.0-1.5. If > 1.5, restart Redis to defragment:

redis-cli SHUTDOWN SAVE && redis-server /etc/redis/redis.conf

Connection Health

Normal: 2-5 connections per EmailEngine instance.

redis-cli CLIENT LIST | wc -l

Managed Redis Services

Compatibility Matrix

ServiceStatusNotes
Upstash RedisSupported with constraints1 MB command size limit affects large MIME blobs; free tier quotas are insufficient; must be located in the same cloud region
Amazon ElastiCachePartially supportedOperates in stand-alone mode, but data may be lost on node replacement unless Multi-AZ persistence is enabled
Azure Cache for RedisSupportedUse Basic, Standard, or Premium tier; verify persistence is enabled
Google Cloud MemorystoreSupportedUse Standard tier with replication for high availability
Redis CloudSupportedNative Redis service; ensure persistence and eviction policy are configured
MemuraiExperimentalPasses basic tests on Windows; no long-term performance data
DragonflyExperimentalRequires --default_lua_flags=allow-undeclared-keys; validate against production workloads
KeyDBExperimentalMulti-threaded fork of Redis; monitor replication lag and memory stability

Unsupported: Redis Cluster is incompatible with EmailEngine's Lua scripts that reference dynamic keys. Use a single Redis instance with persistence enabled.

Service-Specific Configuration

# Upstash connection string format
EENGINE_REDIS="rediss://:YOUR_PASSWORD@YOUR_ENDPOINT.upstash.io:6379"

Limitations:

  • 1 MB command size limit (affects large attachments in outbox)
  • Free tier: 10,000 commands/day is insufficient for production
  • Requires same-region deployment to minimize latency

Monitoring

Key Metrics

Memory:

redis-cli INFO memory | grep used_memory_human

Latency:

redis-cli --latency-history -i 1

Persistence:

redis-cli INFO persistence | grep rdb_last_save_time

Alert Thresholds

MetricWarningCritical
Memory usage> 70%> 85%
Memory fragmentation> 1.5> 2.0
Latency (p99)> 10ms> 50ms
Persistence lag> 60s> 300s

EmailEngine Prometheus Metrics

curl http://localhost:3000/metrics | grep redis

Example output:

redis_version{version="v7.2.7"} 1
redis_uptime_in_seconds 369345
redis_latency 103542
redis_connected_clients 34
redis_memory_used_bytes 279341568
redis_memory_max_bytes 17179869184
redis_mem_fragmentation_ratio 0.06
redis_instantaneous_ops_per_sec 597
redis_last_save_time 1762178720

Quick Reference

Deploy Redis in the same data center as EmailEngine, allocate sufficient memory, enable RDB persistence, and set eviction policy to noeviction.

Essential checklist:

  • Set maxmemory-policy noeviction
  • Enable RDB persistence (save 60 10000 300 10 900 1)
  • Set tcp-keepalive 300
  • Provision 2× base memory (1-2 MiB per account)
  • Keep usage < 80%
  • Target latency < 5ms
  • Avoid AOF (too high I/O overhead)
  • Avoid Redis Cluster (not supported)

Connection strings:

redis://localhost:6379              # Local
redis://:password@host:6379 # With password
rediss://:password@host:6380 # With TLS
redis://host:6379/8 # Specific database