← Back to DevBytes

ElastiCache: Complete Setup and Configuration Guide

What is Amazon ElastiCache?

Amazon ElastiCache is a fully managed, in-memory caching service provided by AWS. It supports two popular open-source caching engines: Redis and Memcached. ElastiCache removes the operational burden of managing a cache infrastructure by handling hardware provisioning, software patching, failure detection and recovery, and automated backups. It delivers sub-millisecond latency at scale, making it ideal for use cases such as session storage, real-time leaderboards, rate limiting, database query caching, and message brokering with pub/sub.

At its core, ElastiCache sits between your application and your primary data store (often an RDS database or DynamoDB table), intercepting read requests and returning cached data without hitting the backend. This dramatically reduces database load and improves application response times. Because ElastiCache nodes run in-memory, data access is orders of magnitude faster than disk-based databases.

Why ElastiCache Matters

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

Modern web applications demand near-instant responses. Even a well-tuned relational database like Aurora PostgreSQL can take 3–10 milliseconds for a simple read query due to disk I/O and query parsing overhead. ElastiCache delivers reads in under 1 millisecond because data resides entirely in RAM. This performance delta is critical for applications with high concurrency, where cumulative database latency can degrade user experience and increase infrastructure costs.

Beyond raw speed, ElastiCache provides several tangible benefits:

ElastiCache Engine Options: Redis vs. Memcached

Choosing between Redis and Memcached is one of the first architectural decisions you'll make. Here's a detailed comparison to guide your choice:

When to Choose Redis

When to Choose Memcached

In practice, Redis dominates new workloads because of its richer feature set and high-availability capabilities. Memcached remains a strong choice for simple, horizontally-scaled caching tiers where performance-per-core matters most.

Setting Up ElastiCache — Complete Walkthrough

Prerequisites

Before creating your ElastiCache cluster, ensure you have the following in place:

Step 1 — Create a Subnet Group

ElastiCache requires a subnet group that specifies which subnets your cache nodes will launch into. For high availability with Redis, you should specify subnets in at least two different Availability Zones.

Using the AWS CLI:

aws elasticache create-cache-subnet-group \
    --cache-subnet-group-name my-cache-subnet-group \
    --cache-subnet-group-description "Subnet group for production Redis cluster" \
    --subnet-ids subnet-abc12345 subnet-def67890 subnet-ghi11223

To verify the subnet group was created:

aws elasticache describe-cache-subnet-groups \
    --cache-subnet-group-name my-cache-subnet-group

You can also create a subnet group through the AWS Console by navigating to ElastiCache → Subnet Groups → Create Subnet Group.

Step 2 — Create a Security Group

Your cache nodes need a security group that allows inbound connections on the appropriate port from your application instances. The default port is 6379 for Redis and 11211 for Memcached.

# Create the security group in your VPC
aws ec2 create-security-group \
    --group-name elasticache-access \
    --description "Allow inbound from app servers to ElastiCache" \
    --vpc-id vpc-0abcd1234efgh5678

# Add inbound rule for Redis port from your app security group
aws ec2 authorize-security-group-ingress \
    --group-id sg-0abcd1234efgh5678 \
    --protocol tcp \
    --port 6379 \
    --source-group sg-app-servers-xyz

# Alternatively, add inbound rule from a CIDR range
aws ec2 authorize-security-group-ingress \
    --group-id sg-0abcd1234efgh5678 \
    --protocol tcp \
    --port 6379 \
    --cidr 10.0.0.0/16

Important: Always restrict access to the minimum necessary source. Using source security group references (as shown above) is preferable to CIDR ranges because it dynamically resolves to the correct instances and avoids overly broad rules.

Step 3 — Create a Parameter Group (Optional but Recommended)

Parameter groups allow you to tune the cache engine's behavior. For Redis, common adjustments include setting a maxmemory policy, enabling cluster mode parameters, and configuring slowlog thresholds.

Create a custom parameter group for Redis 7.0:

aws elasticache create-cache-parameter-group \
    --cache-parameter-group-family redis7 \
    --cache-parameter-group-name my-redis7-params \
    --description "Custom parameters for Redis 7.0 production cluster"

Modify specific parameters. For example, to set the eviction policy to allkeys-lru (evict the least recently used keys across all keys when memory is full):

aws elasticache modify-cache-parameter-group \
    --cache-parameter-group-name my-redis7-params \
    --parameter-name-values \
      "ParameterName=maxmemory-policy,ParameterValue=allkeys-lru" \
      "ParameterName=slowlog-log-slower-than,ParameterValue=10000" \
      "ParameterName=slowlog-max-len,ParameterValue=128" \
      "ParameterName=timeout,ParameterValue=300" \
      "ParameterName=tcp-keepalive,ParameterValue=300"

For a Memcached parameter group, you might adjust the maximum item size or disable LRU eviction entirely (which causes writes to fail when memory is full — useful for strict cache-aside patterns).

Step 4 — Provision the ElastiCache Cluster

Now you're ready to create the actual cache cluster. Below are examples for both a Redis replication group (recommended for production) and a Memcached cluster.

Provisioning a Redis Replication Group (Highly Available)

This creates a 3-node Redis cluster with 1 primary and 2 replicas across multiple AZs, encryption enabled, automatic failover, and weekly snapshots.

aws elasticache create-replication-group \
    --replication-group-id my-redis-prod \
    --replication-group-description "Production Redis 7.0 HA cluster" \
    --engine redis \
    --engine-version 7.0 \
    --cache-node-type cache.r6g.xlarge \
    --num-cache-clusters 3 \
    --automatic-failover-enabled \
    --multi-az-enabled \
    --cache-subnet-group-name my-cache-subnet-group \
    --security-group-ids sg-0abcd1234efgh5678 \
    --cache-parameter-group-name my-redis7-params \
    --at-rest-encryption-enabled \
    --transit-encryption-enabled \
    --auth-token MyS3cur3AuthT0k3n!2024 \
    --snapshot-retention-limit 7 \
    --snapshot-window 03:00-04:00 \
    --tags Key=Environment,Value=production Key=Owner,Value=backend-team

Key parameters explained:

Provisioning a Redis Cluster Mode (Sharded)

For datasets larger than a single node can handle, use cluster mode with sharding:

aws elasticache create-replication-group \
    --replication-group-id my-redis-cluster \
    --replication-group-description "Sharded Redis 7.0 cluster mode" \
    --engine redis \
    --engine-version 7.0 \
    --cache-node-type cache.r6g.xlarge \
    --cache-parameter-group-name my-redis7-params \
    --cache-subnet-group-name my-cache-subnet-group \
    --security-group-ids sg-0abcd1234efgh5678 \
    --num-node-groups 3 \
    --replicas-per-node-group 1 \
    --automatic-failover-enabled \
    --multi-az-enabled \
    --cluster-enabled \
    --auth-token MyS3cur3AuthT0k3n!2024 \
    --at-rest-encryption-enabled \
    --transit-encryption-enabled

This creates 3 shards, each with 1 primary and 1 replica (6 nodes total). Data is automatically partitioned across shards using 16,384 hash slots.

Provisioning a Memcached Cluster

Memcached clusters are simpler — no replication, no persistence, no failover. You specify the number of nodes and the engine automatically distributes keyspace.

aws elasticache create-cache-cluster \
    --cache-cluster-id my-memcached-prod \
    --engine memcached \
    --engine-version 1.6.12 \
    --cache-node-type cache.m6g.xlarge \
    --num-cache-nodes 3 \
    --cache-subnet-group-name my-cache-subnet-group \
    --security-group-ids sg-0abcd1234efgh5678 \
    --az-mode cross-az \
    --tags Key=Environment,Value=production

Memcached uses client-side sharding, so your application's Memcached client library must support distributing keys across multiple endpoints. Most modern clients (like pymemcache or memcached-client for Node.js) handle this automatically via consistent hashing.

Step 5 — Connect and Test

Once your cluster status shows available (which can take 10–15 minutes for Redis, 5–10 for Memcached), retrieve the endpoint information:

# For Redis (primary endpoint for reads/writes, reader endpoint for reads)
aws elasticache describe-replication-groups \
    --replication-group-id my-redis-prod \
    --query 'ReplicationGroups[0].{PrimaryEndpoint:PrimaryEndpoint.Address,ReaderEndpoint:ReaderEndpoint.Address,Port:Port}'

# For Memcached (list all node endpoints)
aws elasticache describe-cache-clusters \
    --cache-cluster-id my-memcached-prod \
    --query 'CacheClusters[0].{Nodes:CacheNodes[*].Endpoint.Address,Port:Port}' \
    --show-cache-node-info

Test connectivity using the redis-cli or nc (netcat for Memcached):

# Connect to Redis with TLS and AUTH
redis-cli -h my-redis-prod.abc123.ng.0001.use1.cache.amazonaws.com \
    -p 6379 \
    --tls \
    -a 'MyS3cur3AuthT0k3n!2024' \
    --verbose

# Once connected, test basic commands
PING
SET mykey "Hello ElastiCache!"
GET mykey
INFO server

# Connect to Memcached (no auth, no TLS by default)
echo "stats" | nc my-memcached-prod.abc123.ng.0001.use1.cache.amazonaws.com 11211
echo "set testkey 0 3600 5\r\nHello" | nc my-memcached-prod.abc123.ng.0001.use1.cache.amazonaws.com 11211
echo "get testkey" | nc my-memcached-prod.abc123.ng.0001.use1.cache.amazonaws.com 11211

Connecting to ElastiCache from Your Application

Below are production-ready code examples for connecting to ElastiCache Redis and Memcached from popular programming languages. Each example includes connection pooling, error handling, and TLS configuration.

Python — Redis Connection (redis-py)

import redis
import os
from redis.connection import ConnectionPool

# Connection pooling is critical for production — 
# creating a new connection per request will overwhelm the server.
REDIS_HOST = os.environ.get("REDIS_HOST", "my-redis-prod.abc123.ng.0001.use1.cache.amazonaws.com")
REDIS_PORT = int(os.environ.get("REDIS_PORT", 6379))
REDIS_AUTH = os.environ.get("REDIS_AUTH_TOKEN", "MyS3cur3AuthT0k3n!2024")
REDIS_USE_TLS = os.environ.get("REDIS_USE_TLS", "true").lower() == "true"

pool = ConnectionPool(
    host=REDIS_HOST,
    port=REDIS_PORT,
    password=REDIS_AUTH,
    ssl=REDIS_USE_TLS,
    ssl_cert_reqs="required",
    max_connections=50,
    socket_keepalive=True,
    socket_connect_timeout=2,
    socket_timeout=2,
    retry_on_timeout=True,
    health_check_interval=30,
)

def get_redis_client():
    """Return a Redis client from the shared connection pool."""
    return redis.Redis(connection_pool=pool)

# Usage example: cache database query results
def get_user_profile(user_id: int):
    r = get_redis_client()
    cache_key = f"user:profile:{user_id}"
    
    # Try cache first
    cached = r.get(cache_key)
    if cached:
        return json.loads(cached)
    
    # Cache miss — query database and populate cache
    profile = db.query_user_profile(user_id)
    if profile:
        # Cache for 10 minutes (600 seconds)
        r.setex(cache_key, 600, json.dumps(profile))
    return profile

# For cluster mode, use RedisCluster client:
# from redis.cluster import RedisCluster
# rc = RedisCluster(
#     host=REDIS_HOST,
#     port=REDIS_PORT,
#     password=REDIS_AUTH,
#     ssl=True,
# )

Python — Memcached Connection (pymemcache)

from pymemcache.client.hash import HashClient
from pymemcache.client.base import Client
import os

# Memcached cluster uses consistent hashing across nodes
MEMCACHED_NODES = [
    ("my-memcached-prod.abc123.ng.0001.use1.cache.amazonaws.com", 11211),
    ("my-memcached-prod.abc123.ng.0002.use1.cache.amazonaws.com", 11211),
    ("my-memcached-prod.abc123.ng.0003.use1.cache.amazonaws.com", 11211),
]

client = HashClient(
    servers=MEMCACHED_NODES,
    hash_function=fnv1a_32,
    timeout=1.0,
    connect_timeout=1.0,
    retry_attempts=2,
    retry_timeout=0.5,
    # If you enabled SASL auth (rare for Memcached):
    # username='your-sasl-user',
    # password='your-sasl-pass',
)

def cache_query_result(query_hash: str, result: bytes, ttl: int = 300):
    """Store a query result in Memcached with TTL (seconds)."""
    success = client.set(query_hash, result, expire=ttl)
    return success

def get_cached_query(query_hash: str):
    """Retrieve cached query result, return None on miss."""
    result = client.get(query_hash)
    return result

Node.js — Redis Connection (ioredis)

const Redis = require('ioredis');

// For non-cluster Redis with TLS
const redis = new Redis({
  host: process.env.REDIS_HOST || 'my-redis-prod.abc123.ng.0001.use1.cache.amazonaws.com',
  port: parseInt(process.env.REDIS_PORT, 10) || 6379,
  password: process.env.REDIS_AUTH_TOKEN || 'MyS3cur3AuthT0k3n!2024',
  tls: {
    rejectUnauthorized: true,
  },
  maxRetriesPerRequest: 3,
  retryStrategy(times) {
    if (times > 10) return null; // stop retrying after 10 attempts
    return Math.min(times * 50, 2000); // exponential backoff capped at 2s
  },
  enableReadyCheck: true,
  lazyConnect: false,
});

redis.on('connect', () => console.log('Connected to ElastiCache Redis'));
redis.on('error', (err) => console.error('Redis error:', err));

// Cache middleware example for Express
async function cacheMiddleware(req, res, next) {
  const cacheKey = `page:${req.originalUrl}`;
  try {
    const cached = await redis.get(cacheKey);
    if (cached) {
      res.setHeader('X-Cache', 'HIT');
      return res.send(JSON.parse(cached));
    }
    res.setHeader('X-Cache', 'MISS');
    // Override res.send to cache the response
    const originalSend = res.send.bind(res);
    res.send = function (body) {
      redis.setex(cacheKey, 300, JSON.stringify(body));
      return originalSend(body);
    };
    next();
  } catch (err) {
    next(); // proceed without caching on Redis error
  }
}

// For Redis Cluster Mode, use:
// const Redis = require('ioredis');
// const cluster = new Redis.Cluster([
//   { host: '...configuration-endpoint...', port: 6379 }
// ], {
//   redisOptions: { password: 'auth-token', tls: {} }
// });

Java — Redis Connection (Jedis with Spring Boot)

// application.yml for Spring Boot with ElastiCache Redis
// spring:
//   redis:
//     host: my-redis-prod.abc123.ng.0001.use1.cache.amazonaws.com
//     port: 6379
//     password: MyS3cur3AuthT0k3n!2024
//     ssl: true
//     timeout: 2000ms
//     lettuce:
//       pool:
//         max-active: 50
//         max-idle: 10
//         min-idle: 5
//         max-wait: 2000ms

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.concurrent.TimeUnit;

@Service
public class UserCacheService {
    
    private final RedisTemplate redisTemplate;
    
    public UserCacheService(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    @Cacheable(value = "users", key = "#userId")
    public UserProfile getUserProfile(String userId) {
        // This method body only executes on cache miss
        return databaseRepository.findUserProfile(userId);
    }
    
    // Manual cache operation with TTL
    public void cacheWithExpiry(String key, Object value, long ttlSeconds) {
        redisTemplate.opsForValue()
            .set(key, value, ttlSeconds, TimeUnit.SECONDS);
    }
    
    public Object getFromCache(String key) {
        return redisTemplate.opsForValue().get(key);
    }
}

// Custom Redis configuration bean for TLS:
// @Configuration
// public class RedisConfig {
//     @Bean
//     public LettuceClientConfigurationBuilderCustomizer customizer() {
//         return builder -> builder.useSsl().disablePeerVerification();
//     }
// }

Configuration Deep Dive

Redis-Specific Configuration Parameters

The following parameter group settings are critical for production Redis workloads:

Memcached-Specific Configuration Parameters

Cluster Mode vs. Non-Cluster Mode (Redis)

This is a fundamental architectural decision that affects scaling, client libraries, and cost:

Feature Non-Cluster Mode Cluster Mode
Max data size Limited by single node RAM (up to ~450GB with cache.r7g.16xlarge) Up to 500 shards × node RAM (hundreds of TB theoretically)
Scaling out Add read replicas (up to 5) Add shards online with ModifyReplicationGroupShardConfiguration
Write scaling Single primary — all writes hit one node Writes distributed across all shard primaries
Multi-key operations Supported freely (e.g., MGET, MSET, SUNION across keys) Only if all keys hash to the same slot (use hash tags)
Client library Standard Redis client Cluster-aware client required (or use a cluster proxy)
Failover Manual or automatic with replicas Automatic per-shard failover built-in
Configuration endpoint Primary endpoint + reader endpoint Single configuration endpoint for cluster discovery

Encryption, Authentication, and Backups

Encryption at rest uses AWS KMS to encrypt underlying EBS volumes (for snapshot persistence) and snapshot data in S3. You must enable this at cluster creation time — it cannot be enabled later. The default AWS managed CMK (aws/elasticache) is sufficient for most cases, but you can specify a customer-managed CMK for compliance requirements.

Encryption in transit enforces TLS for all connections to the cache. For Redis, this requires either an AUTH token (Redis 6.x and earlier) or ACL-based authentication (Redis 7.0+). Memcached supports SASL authentication when transit encryption is enabled, but this is less common in practice.

Redis ACLs (Access Control Lists) are available in Redis 7.0+ and provide fine-grained user-level permissions. You can create users with restricted command sets:

# Example: Create a read-only user for analytics queries
redis-cli -h my-redis-prod.abc123.ng.0001.use1.cache.amazonaws.com \
    --tls -a 'MyS3cur3AuthT0k3n!2024' \
    ACL SETUSER analytics \
    on >analytics_password \
    +@read \
    +ping \
    -@dangerous \
    ~cache:analytics:*

Backup strategy: For Redis, enable automatic snapshots with a retention window of at least 7 days. Snapshots are incremental after the first full snapshot, minimizing performance impact. Schedule your snapshot window during off-peak hours (e.g., 03:00–04:00 UTC). You can also trigger manual snapshots before major application deployments:

aws elasticache create-snapshot \
    --replication-group-id my-redis-prod \
    --snapshot-name pre-deploy-v2.3.0-backup

For Memcached, there are no snapshots. Ensure your application can fully repopulate the cache from the database on a cold start. Implement a cache warming strategy (e.g., on deployment, run a script that pre-loads frequently accessed keys).

Best Practices for ElastiCache in Production

1. Connection Management

2. Key Design and Namespacing

3. Memory Management