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:
- Reduced database costs — Offloading read-heavy workloads to a cache tier allows you to downsize your primary database instance or reduce read replica count, often saving 30–60% on database spend.
- Automatic failover — With Redis replication groups, ElastiCache detects primary node failures and promotes a read replica to primary within seconds, minimizing downtime without operator intervention.
- Scalability — You can scale out by adding read replicas (up to 5 for Redis non-cluster mode, or up to 500 shards with Redis cluster mode). You can also scale up by modifying instance types with minimal disruption.
- Security integration — ElastiCache integrates with AWS IAM, VPC, security groups, and supports encryption at rest (AWS KMS) and encryption in transit (TLS/SSL). You can also use Redis AUTH tokens for password-based access control.
- Data durability — Redis supports snapshot persistence to S3 and append-only file (AOF) logging, ensuring you can recover cache data across restarts. Memcached is purely volatile but benefits from automatic node replacement.
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
- You need data persistence — Redis snapshots and AOF allow cache data to survive reboots and failures.
- You require high availability — Redis replication groups with automatic failover provide near-zero downtime.
- You use complex data types — Redis supports strings, hashes, lists, sets, sorted sets, bitmaps, hyperloglogs, and streams.
- You need pub/sub messaging — Redis channels enable real-time notifications between application components.
- You benefit from Lua scripting — Atomic server-side scripts reduce network round-trips.
- You want geospatial queries — Redis sorted sets with geohashing support location-based operations.
- You require cluster mode — Horizontal sharding across up to 500 shards for massive datasets (hundreds of GBs in memory).
When to Choose Memcached
- You need the simplest possible caching — Memcached is a pure key-value store with minimal feature surface.
- You want multi-threaded performance — Memcached leverages multiple CPU cores natively, offering better raw throughput on large instances for simple workloads.
- You have ephemeral, non-critical data — If losing the cache is acceptable (you can always repopulate from the database), Memcached's lack of persistence is not a drawback.
- You need automatic sharding across nodes — Memcached clusters distribute keys across all nodes automatically via client-side hashing.
- You run legacy applications that already use Memcached and you want a drop-in managed replacement.
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:
- An AWS account with appropriate IAM permissions (at minimum,
elasticache:*andec2:DescribeSubnets,ec2:DescribeSecurityGroups). - A VPC with both public and private subnets if your application runs in AWS, or a VPC with a VPN/Direct Connect if your application is on-premises.
- The AWS CLI installed and configured (
aws configure) or access to the AWS Management Console. - Basic familiarity with your application's networking setup (subnet IDs, security group IDs).
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:
--num-cache-clusters 3— Total nodes: 1 primary + 2 replicas. Must be odd (≥3) for automatic failover quorum.--automatic-failover-enabled— Enables automatic promotion of a replica to primary if the current primary fails.--multi-az-enabled— Ensures replicas are distributed across different Availability Zones.--auth-token— Sets a Redis AUTH password. Required if transit encryption is enabled (for Redis versions before 7.0.1 where ACLs aren't supported). For Redis 7.0+, you can use ACLs instead.--at-rest-encryption-enabled— Encrypts snapshot data at rest using AWS KMS.--transit-encryption-enabled— Enforces TLS for all client connections.
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:
maxmemory-policy— Controls eviction behavior when memory is exhausted. Options include:volatile-lru— Evict least recently used keys with TTL set (safer for mixed workloads).allkeys-lru— Evict LRU keys across entire keyspace (best for pure cache use).volatile-ttl— Evict keys closest to expiration time.noeviction— Reject writes when memory full (use for queues/streams where data loss is unacceptable).
cluster-enabled— Must be set toyesin the parameter group before creating a cluster-mode replication group. This cannot be changed after cluster creation.slowlog-log-slower-than— Threshold in microseconds for logging slow commands. Set to10000(10ms) to catch problematic operations.client-output-buffer-limit— Prevents memory exhaustion from slow clients. Default limits are sensible but may need tuning for large pub/sub payloads.activedefrag— Enable active defragmentation on Redis 7.0+ to combat memory fragmentation over long uptimes.
Memcached-Specific Configuration Parameters
max_item_size— Maximum size of a single cached item. Default is 1MB. Increase to 2MB or 5MB if caching larger objects (but be mindful of slab fragmentation).slab_chunk_size— Initial chunk size for the slab allocator. Tuning this reduces memory waste if your item sizes cluster around specific values.lru_maintainer_thread— Dedicated LRU maintenance thread. Enable (yes) on multi-core instances for smoother eviction under load.disable_flush_all— Set toyesto prevent accidental cache-wide flush via theflush_allcommand in production.
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
- Always use connection pooling on the client side. Creating new TCP connections per request incurs TLS handshake overhead and can exhaust server-side file descriptors.
- Set reasonable pool sizes — 20–50 connections per application instance is typical. Monitor
curr_connectionsmetric to detect connection leaks. - Implement circuit breaker logic — if Redis is unreachable, degrade gracefully (serve stale data, return default values, or fall back to the database with rate limiting).
2. Key Design and Namespacing
- Use descriptive, colon-delimited key names:
user:profile:12345,session:auth:abc123token,product:inventory:sku-67890. - Include version hints in key names if your data schema evolves:
user:v2:profile:12345. - For Redis cluster mode, use hash tags (
{}) to co-locate related keys on the same shard:user:{12345}:profile,user:{12345}:orders. - Keep keys reasonably short — every byte of the key consumes memory. A 100-character key used 10 million times wastes ~1GB.
3. Memory Management
- Set
maxmemoryreservation — By default, ElastiCache uses the full instance RAM for data. Reserve 10–20% for OS overhead and replication buffers. Use thereserved-memory-percentparameter (Redis 7.0+). - Monitor
BytesUsedForCacheandEvictionsmetrics. A sudden spike in evictions indicates you're hitting memory limits — either scale up the node type or add shards.
🚀 Need a reliable AI agent for your project?
Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.
Get Started — $23.99/mo