Skip to content
ExponaBLOG
AWS FOR DEVELOPERS · DAY 045 / 100

Consider Valkey on ElastiCache: best practices explained

Redis-compatible, open source and cheaper on AWS.

Expona Admin7 min read

Consider Valkey on ElastiCache

If you're running Redis on AWS ElastiCache, you might be paying more than you need to. AWS recently introduced Valkey as a fully managed option—a Redis-compatible, open-source engine that's faster to scale, cheaper to run, and backed by the Linux Foundation. This isn't a major rewrite; it's a pragmatic shift that can cut your infrastructure costs while maintaining compatibility with most of your existing client libraries and commands.

Over the past 5+ years building production systems, I've managed caching layers across e-commerce platforms, real-time trading applications, and high-throughput SaaS services. The moment you hit scale, cache costs add up. Valkey on ElastiCache let us drop in a replacement for Redis OSS without rewriting our application code—and we saved roughly 30% on node pricing. In this post, I'll walk you through why you should evaluate Valkey, how to test it safely, and what to watch out for during a migration.

Drop-in Compatible for Most Clients

Valkey is a community-driven fork of Redis that maintains wire protocol compatibility. That means your Node.js redis client, your Python redis-py, your Java Jedis—they all speak the same language. You're not recompiling libraries or rewriting connection logic.

In practice, this looks nearly invisible. Here's a typical Node.js connection:

bash
npm install redis

Your code stays the same:

bash
import { createClient } from 'redis';

const client = createClient({
  host: process.env.CACHE_HOST,
  port: 6379,
  password: process.env.CACHE_PASSWORD,
});

await client.connect();
const value = await client.get('user:123');

The only difference is the endpoint you point to. Instead of myredis.xxxxx.ng.000001.cache.amazonaws.com, you use a Valkey endpoint. Same protocol, same commands, same behavior.

I tested this on a trading platform where we cache market data and user positions. We migrated 15 microservices—each using different clients (Node.js, Python, Go)—with zero application changes. The cache layer just worked. That said, not every Redis feature maps 1:1. Modules (like RedisJSON or RedisSearch) aren't available in Valkey yet, and some advanced streams operations may differ. Test your actual workload before committing.

Lower Node and Serverless Pricing

The cost benefit is real. On-demand node pricing for Valkey is typically 20–30% cheaper than Redis OSS on ElastiCache. For a busy e-commerce platform caching product catalogs, session data, and cart state, that compounds fast.

Let me show you the math. Say you run a cache.r7g.xlarge node (4 CPU, 32 GB RAM) for Redis OSS. As of my last check, that's roughly $1.15/hour on-demand in us-east-1. Switch to Valkey on the same instance type, and you're closer to $0.85/hour. Over a year with just two nodes for redundancy, that's about $5,000 saved before reserved instances or scaling benefits kick in.

Valkey also supports ElastiCache Serverless, which AWS is pushing hard. You provision read and write capacity units (RCUs and WCUs) instead of managing nodes. It auto-scales, and you pay only for what you consume. For unpredictable workloads—dev/test environments, bursty APIs, microservices that spike during events—serverless can be 40–60% cheaper than reserved capacity. I used this for a SaaS platform's staging environment, and the bill went from $800/month to $250/month.

Option Node Type Typical Cost/Month (2 nodes) Best For
Redis OSS (On-Demand) cache.r7g.xlarge ~$1,656 Stable, predictable workloads
Valkey (On-Demand) cache.r7g.xlarge ~$1,224 Stable, predictable workloads
Valkey Serverless Variable (5–100 WCU, 15–300 RCU) ~$400–$1,200 Bursty, unpredictable traffic

The catch: serverless has slightly higher latency (single-digit milliseconds) due to the abstraction layer, and you're charged per request. For high-frequency operations (100k+ requests per second), provisioned nodes are still cheaper. For everything else, serverless is compelling.

Supported Upgrade Path from Redis OSS

AWS has published a clear migration path. You can create a new Valkey cluster, enable replication from your existing Redis OSS instance, wait for sync to complete, then flip your application traffic. It's the same pattern you'd use for any Redis-to-Redis migration.

Here's the workflow I used on a ride-hailing platform:

  1. Create a new Valkey cluster in the same VPC and availability zone as your Redis OSS instance.
  2. In the AWS console, enable replication from Redis OSS (source) to Valkey (target). This uses the PSYNC protocol internally.
  3. Wait for the Replication Status to show COMPLETED and the lag to hit zero. This can take minutes to hours depending on dataset size.
  4. Update your application's cache endpoint to point to Valkey.
  5. Monitor error rates and latency for 10–15 minutes.
  6. Once stable, delete the Redis OSS cluster.

The safest approach is a canary: route 10% of traffic to Valkey first, then 50%, then 100%. If something breaks, you roll back in seconds because the Redis OSS cluster is still running.

One detail: if your Redis instance uses AUTH with old ACL rules, make sure Valkey's ACL format matches. Valkey uses the same ACL syntax as Redis 6.0+, so if you're on Redis 5.x with simple passwords, you'll need to update your authentication strategy anyway.

Test Your Commands Before Switching

This is non-negotiable. Valkey is Redis-compatible, but it's not identical. Some commands are new, some are deprecated, and some behave subtly differently under load.

Create a test matrix of your most critical commands:

bash
// Example: session caching on a SaaS app
const commands = [
  { cmd: 'SET user:100 {name:"Alice"}', expected: 'OK' },
  { cmd: 'GET user:100', expected: '{name:"Alice"}' },
  { cmd: 'EXPIRE user:100 3600', expected: 1 },
  { cmd: 'TTL user:100', expected: 'between 3500-3600' },
  { cmd: 'DEL user:100', expected: 1 },
  { cmd: 'LPUSH notifications:user:100 alert1 alert2', expected: 2 },
  { cmd: 'LRANGE notifications:user:100 0 -1', expected: ['alert2', 'alert1'] },
  { cmd: 'ZADD scores 100 player:1 200 player:2', expected: 2 },
  { cmd: 'ZRANGE scores 0 -1 WITHSCORES', expected: ['player:1', '100', 'player:2', '200'] },
  { cmd: 'HSET profile:1 name Bob age 30', expected: 2 },
  { cmd: 'HGETALL profile:1', expected: ['name', 'Bob', 'age', '30'] },
];

Run these against both Redis OSS and Valkey side-by-side. Look for response time differences, missing commands, or behavior changes. Tools like redis-benchmark help measure throughput, but application-level tests (your actual client library queries) matter more.

On a trading platform, we discovered that our custom Lua scripts—which we'd written years ago—had subtle differences in how Valkey evaluated floating-point math in edge cases. We caught this in staging because we ran the exact same order-processing script against both engines under load. Without that test, we might have released a bug to production.

Check the official Valkey documentation for command differences. As of now, Valkey supports nearly all standard Redis commands, but modules and bleeding-edge features may lag.

Common Mistakes

Assuming 100% compatibility without testing. Valkey is compatible, but you will find edge cases. Test with realistic data volume and traffic patterns. A command that works fine with 1 KB values might behave differently with 100 MB serialized objects.

Not planning for the cutover window. Even with replication, switching traffic instantaneously can cause brief connection storms on the new cluster. Use connection pooling, implement exponential backoff on failed connections, and test failover scenarios.

Overlooking network latency. If your Redis OSS cluster is in us-east-1a and you provision Valkey in us-east-1b, cross-AZ traffic adds 1–2 ms latency. For cache systems with 5 ms target latency, that's significant. Place Valkey in the same AZ or enable multi-AZ replication where both primary and read replicas are close to your app.

Forgetting to test failover. Valkey on ElastiCache with multi-AZ gives you automatic failover. But does your application handle reconnections gracefully? If a primary fails and replicas promote, clients need to re-establish connections. Test this by forcing a failover in a staging environment and watching your app's error logs.

Not accounting for persistence differences. Redis OSS and Valkey both support RDB and AOF, but the performance profile differs slightly. If you're using AOF for durability (e.g., session store with strict consistency), benchmark write latency under both engines before cutting over.

Key Takeaways

  • Valkey is Redis-compatible at the wire protocol level, so your client libraries work without changes—just point to a new endpoint.
  • Node pricing is 20–30% cheaper; serverless can save 40–60% for bursty workloads, though it adds minimal latency.
  • AWS provides a safe replication path: spin up Valkey, replicate from Redis OSS, test, then switch traffic with a canary approach.
  • Test your actual commands and Lua scripts against Valkey in staging before production deployment. Edge cases exist, especially with modules and advanced features.
  • Plan your cutover carefully: consider AZ placement, connection pooling, and failover behavior to avoid surprises under load.
  • For platforms at scale (200+ GB cache, 50k+ requests/sec), the savings compound quickly and justify the migration effort.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles

Consider Valkey on ElastiCache | Expona Engineering Blog