Monitoring ElastiCache with CloudWatch: Key Metrics Developers Should Track#

When you deploy ElastiCache into production, the cluster doesn’t manage itself. Unlike managed relational databases where you might check things once a week, ElastiCache demands active monitoring to catch performance degradation, capacity issues, and replication problems before they impact your application. CloudWatch provides the visibility you need, but understanding which metrics matter most—and what they actually tell you—separates developers who run reliable systems from those who wake up to pager alerts at 3 AM.

This guide walks you through the essential CloudWatch metrics for ElastiCache, explains what each one means in practical terms, and shows you how to build a monitoring strategy that gives you real insight into cluster health and performance.

Why ElastiCache Monitoring Matters#

ElastiCache clusters sit between your application and your database or backend services. When they perform well, your application flies. When they degrade, the entire stack feels it—either through increased database load or slow response times. The challenge is that performance degradation in cache clusters often happens gradually. CPU creeps up one percent at a time. Evictions increase slowly. Network throughput builds quietly. By the time you notice something’s wrong at the application level, you’ve already been losing performance for hours.

CloudWatch metrics give you the early warning system you need. They let you see trends before they become crises and make data-driven decisions about scaling, optimization, and resource allocation.

CPU Utilization: When “High” Doesn’t Mean What You Think#

Most AWS services use CPU metrics fairly straightforwardly—high CPU means the service is working hard. ElastiCache complicates this picture by giving you two different CPU metrics, and understanding the difference between them is crucial.

CPUUtilization is the percentage of CPU capacity consumed on the ElastiCache node itself. This includes all operations: cache lookups, writes, evictions, replication overhead, and internal bookkeeping. It’s useful for understanding overall node load, but here’s the catch: if you’re running Redis, it’s fundamentally single-threaded. This means CPUUtilization can hit 100% while still leaving headroom for other operations on the underlying EC2 instance. You might see 80% CPUUtilization reported for your Redis cluster while the underlying instance actually has plenty of spare cycles available.

This is where EngineCPUUtilization enters the picture. This metric measures CPU usage specifically by the Redis or Memcached engine itself, excluding overhead from the ElastiCache service wrapper. For Redis clusters, this number is especially important because it directly reflects the pressure on Redis’s single event loop. When EngineCPUUtilization climbs toward 90%, you’re approaching the ceiling where the engine can’t process commands any faster, regardless of what the overall system metrics show.

In practice, here’s how to interpret these metrics for a Redis cluster: if CPUUtilization sits at 85% but EngineCPUUtilization is only 40%, you have room to optimize, perhaps by batching commands differently or adjusting your eviction policy. But if EngineCPUUtilization reaches 80%, you need to act—either scale horizontally by adding shards or vertically by moving to a larger node type.

For Memcached clusters, the situation is different. Memcached is multi-threaded, so CPUUtilization can scale more linearly with concurrent load. A Memcached cluster at 85% CPU is genuinely near capacity, while Redis at the same CPU level might handle significantly more throughput if you optimize your commands.

Cache Hits and Misses: The Ratio That Tells Your Real Story#

Every request to ElastiCache returns one of two outcomes: a cache hit (the data was there) or a cache miss (the data wasn’t there). CloudWatch gives you CacheHits and CacheMisses as separate counters, but the metric that actually matters is the ratio between them.

A healthy cache should have a hit rate above 80% in most scenarios. Some applications target 95% or higher. A hit rate below 50% suggests either that your cache is too small, your TTL values are too aggressive (data expires before you use it again), or you’re caching the wrong data.

The tricky part is interpreting what a “good” ratio actually means for your use case. A session cache that stores user login data might reasonably target 95% hit rate—if users are logging in repeatedly, you want to hit the cache almost always. A product recommendation cache that’s accessed by many users in parallel might run at 75% hit rate because popular products get frequent hits while niche products miss more often. An analytics cache that’s queried once per day might only achieve 10% hit rate, and that’s completely fine because the cache still saves you from expensive database queries during peak hours.

The pattern to watch for is change over time. If your hit ratio is normally 88% and suddenly drops to 72%, something changed. Maybe your dataset grew and no longer fits in memory. Maybe you deployed an inefficient query pattern. Maybe users are accessing different data than they used to. That drop is your signal to investigate.

To calculate and track hit rate, use a custom metric in CloudWatch: (CacheHits / (CacheHits + CacheMisses)) * 100. Most teams use CloudWatch Logs Insights or a simple Lambda function to compute this and push it as a custom metric daily.

Evictions: The Signal of Insufficient Memory#

When an ElastiCache cluster runs out of memory, it can’t just crash or reject requests. Instead, it evicts (removes) data according to your eviction policy. You might have set your policy to allkeys-lru (evict the least recently used keys when memory is full), or volatile-ttl (evict keys with the shortest TTL), or one of several other strategies. Whatever the policy, the Evictions metric tells you how many times this happened.

A zero eviction rate is ideal. It means your cluster has enough memory for your working set—the data you actively use. But zero evictions is a luxury; most production clusters experience some evictions, especially if you’re optimizing for cost and running lean on memory.

The concerning pattern is rising evictions. If your eviction count increases by 10,000 per day but increases by 100,000 per day a week later, you have a problem. Your working set is growing, either because your application is caching more data or because your user base has grown. Left unchecked, evictions will eventually become so frequent that your cache effectiveness drops to near zero—you’re evicting hot data just to make room for new requests.

Evictions also consume CPU because the cache engine must identify which keys to remove, deallocate memory, and update internal data structures. High evictions can actually drive up CPU utilization even while the cache becomes less effective. This creates a vicious cycle: memory pressure causes evictions, which consume CPU and evict useful data, which reduces hit rate, which causes the application to issue more requests, which adds more data to the cache, which causes more evictions.

The right response depends on your architecture. If you’re running a single-shard cluster, you need to scale up to a larger node type. If you’re using a cluster-mode enabled cluster, you can add shards to increase total memory. If you’re willing to pay the penalty, you can also reduce TTL values or implement more aggressive data expiration on the application side.

Replication Lag: Invisible Risk in Multi-AZ Deployments#

If you’ve deployed ElastiCache with Multi-AZ enabled, you have a primary node and a read replica across availability zones. This setup gives you automatic failover if the primary fails, which is essential for production. CloudWatch provides a ReplicationLag metric that measures the delay (in seconds) between when data is written to the primary and when it appears on the replica.

Under normal conditions, replication lag should be nearly zero—measured in milliseconds. Most well-tuned clusters maintain lag under 100 milliseconds. If replication lag climbs to several seconds, you have a problem.

High replication lag can happen for several reasons. Network congestion between availability zones is one cause; replication data is flowing slower than expected. Another cause is that the primary node is under such heavy write load that it can’t keep up with replicating all changes quickly. A third cause is a misconfigured or failing replica that’s falling behind.

Why does this matter? During normal operation, it’s mostly academic—your application reads from the primary anyway. But if the primary fails and you auto-failover to the replica, that replica becomes the new primary. If the replica was 5 seconds behind, you’ve lost the last 5 seconds of writes. Any data written in those 5 seconds is gone. For some applications, that’s acceptable. For financial transactions or critical user data, it’s catastrophic.

The way to catch this is to set a CloudWatch alarm on ReplicationLag. Keep it tight—alarm if lag exceeds 500 milliseconds, for example. When the alarm fires, investigate immediately. Check if the primary is overloaded (look at EngineCPUUtilization), check if there’s network congestion, check the replica logs for errors. This is not a “check on it eventually” situation; it’s a sign that your failover safety is compromised.

Network Throughput: Diagnosing the I/O Ceiling#

NetworkBytesIn and NetworkBytesOut measure the raw network traffic flowing to and from your ElastiCache cluster. These metrics are useful for capacity planning and for understanding traffic patterns.

If your application suddenly starts pushing significantly more traffic to the cache, you’ll see it here. If you deploy a new feature that makes 100x more cache requests per second, NetworkBytesOut will spike. This is valuable information for two reasons: first, it tells you whether your new feature is working (you intended to cache more), and second, it helps you understand if you need to scale.

Network I/O is one of the limiting factors on ElastiCache performance, just like CPU and memory. A cache.r6g.xlarge node, for example, has a network performance rating of “Up to 10 Gigabit.” In practice, you won’t hit that ceiling for most workloads, but in extremely high-throughput scenarios, network becomes the constraint.

To put this in perspective: if you’re pushing 1 GB per second through a single cache node, you’re near the ceiling for most node types. Across 100 million requests per second of small key-value pairs, you might only use 2-3 GB per second of network. The point is, check these metrics alongside your request count metrics. If requests are growing linearly but bytes-in is growing faster than linear, your values are getting larger, which might be worth optimizing.

Setting Up Actionable Alarms#

Having metrics is useless if you don’t act on them. CloudWatch alarms are the bridge between metric visibility and operational response. Here’s how to think about alarms for ElastiCache:

For CPU, set a two-tier alarm strategy. Create a “warning” alarm at 75% CPUUtilization or 70% EngineCPUUtilization that triggers a Slack notification or sends a message to your ops channel. This is not an emergency; it’s a heads-up to start thinking about capacity. Create a separate “critical” alarm at 90% or higher that pages on-call engineers and possibly triggers an automatic scaling action (though be careful with automatic scaling for cache clusters—sometimes you want human judgment on the response).

For hit ratio, set a threshold alarm that triggers when hit rate drops below your target. If you target 85% hit rate, set an alarm for anything below 80% sustained over 5 minutes. This gives you time to rule out transient dips while catching real problems.

For evictions, use a rate-of-change alarm rather than a simple threshold. It’s fine to have 1,000 evictions per day. It’s not fine to have 100,000 evictions per day and rising. Set an alarm that fires if evictions in the last hour exceeded evictions from the same hour yesterday by more than 50%, for example.

For replication lag, keep the threshold tight. Alarm if lag exceeds 500 milliseconds sustained over 2 minutes. This is a real problem that needs immediate attention.

For network bytes, set an informational alarm that tracks your baseline. If you normally see 50 MB/second out and suddenly see 500 MB/second, that’s worth understanding. You probably don’t need to page anyone at 2 AM for this, but it should go into your monitoring dashboard and trigger during business hours.

All of these alarms should have actions—they should do something when they fire. That might be sending a notification, running a Lambda function to gather diagnostic data, or triggering an auto-scaling action. An alarm that fires silently is worse than no alarm at all because it gives you false confidence that you’re monitoring when you’re actually ignoring problems.

Using Redis SLOWLOG to Find Expensive Commands#

CloudWatch metrics show you that something is wrong, but they often don’t tell you what is wrong. High CPU and low hit rate might suggest that your application is running expensive commands, but you need deeper visibility to confirm.

Redis provides a built-in SLOWLOG that records every command taking longer than a threshold (default 10,000 microseconds). You can connect to your Redis cluster using the Redis CLI and query the slowlog directly.

redis-cli -h your-cache-cluster-endpoint -p 6379
> SLOWLOG GET 10

This returns the 10 most recent slow commands, including the timestamp, duration in microseconds, the command itself, and the client IP. If you’re seeing high CPU, running SLOWLOG GET often reveals the culprit: maybe your application is calling KEYS (which requires scanning the entire keyspace), or running complex Lua scripts, or performing massive MGET operations with thousands of keys.

The pattern is to hook SLOWLOG into your monitoring. You might write a Lambda function that periodically connects to your cluster, retrieves the slowlog, and stores entries in CloudWatch Logs or an S3 bucket for analysis. Most teams pair this with alerts: if the slowlog shows 50 slow commands in the last 5 minutes when you normally see 2-3, that’s a signal that something changed.

A common culprit is the KEYS command. New developers often write code like:

# Bad - scans entire keyspace
keys = redis_client.keys('user:*')
for key in keys:
    redis_client.delete(key)

In a large cluster, this command can take seconds and will block all other operations. The right approach is to use SCAN with a cursor, or better yet, use a different data structure or key naming strategy that doesn’t require full-keyspace scanning.

Another expensive pattern is unbounded MGET or MSET operations. Asking for 10,000 keys in a single MGET call will consume significant network and CPU, and will block the event loop from processing other requests. If you need to fetch many keys, consider batching them into smaller groups (500-1,000 per batch) and parallelizing the requests.

Putting It Together: A Complete Monitoring Strategy#

A complete monitoring strategy for ElastiCache isn’t just about individual metrics—it’s about how they work together to give you a complete picture.

Start by establishing your baseline. For the first week or two after deployment, don’t set overly aggressive alarms. Instead, collect data on what “normal” looks like for your specific application. A cache for user sessions will look completely different from a cache for product inventory or analytics results. Your normal CPU utilization, hit rate, eviction rate, and network throughput are specific to your workload.

Once you have a baseline, set alarms relative to it. A 50% increase in CPU might be normal if you’re doing a marketing push, but a 200% increase suggests something is broken. A drop in hit rate from 87% to 75% is worth investigating; a drop from 87% to 40% demands immediate action.

Create a dashboard that shows the most important metrics: hit ratio, evictions, CPU utilization, and replication lag if Multi-AZ is enabled. This dashboard should be accessible to everyone on the team and should be something you glance at during your morning coffee. Trends matter more than absolute values—you’re looking for things going up or down, not just for them to be high or low.

Finally, pair monitoring with a response plan. What happens when the CPU alarm fires? Someone should be able to immediately check replication lag, hit rate, and slowlog to understand whether the problem is capacity, a bad query, or something else. What happens when hit ratio drops? You should have a checklist: has the dataset grown? Has TTL changed? Did we deploy new code that accesses different data?

Monitoring is not about collecting pretty graphs. It’s about building confidence that you’ll know when something is wrong before your customers tell you about it.

Conclusion#

Effective ElastiCache monitoring comes down to understanding a handful of key metrics and knowing what they tell you about cluster health and performance. CPU utilization (both flavors), cache hit ratio, evictions, replication lag, and network throughput form the core of any serious monitoring strategy. Layer in alarm actions, pair metrics with logs and slowlog data, and you’ve built a system that gives you real operational visibility.

The developers who maintain the most reliable cache clusters aren’t necessarily the ones who build the most sophisticated applications—they’re the ones who treat monitoring as a first-class concern, who understand what their metrics mean in context, and who act on early warning signs before they become outages. Start with the metrics covered here, establish a baseline for your specific workload, and build alarms that will actually notify you of real problems. Your production systems will be more stable for it.