# Valkey on AWS ElastiCache: A Hands-On Guide

- URL: https://karandeepsingh.ca/posts/valkey-aws-elasticache-guide/
- Author: Karandeep Singh
- Published: 2026-07-25
- Series: Aurora PostgreSQL on AWS
- Tags: valkey, aws, python
- Summary: A start-to-finish walkthrough of running Valkey on AWS ElastiCache, built from a real hands-on session, complete with the security-group mistake, the TLS surprise, and a small health-check script.

I finally sat down and ran **Valkey on AWS ElastiCache** end to end, from an empty console to a working health-check script, and took notes on every wrong turn. This is that walkthrough. If you have ever wondered how to stand up a managed Valkey cache, reach it from your own code, and test it properly, this guide covers the whole path, including the two or three things that quietly go wrong the first time.

**Valkey on AWS ElastiCache** is worth knowing because Valkey is the open-source, community-run fork of Redis (now under the Linux Foundation), and AWS made it a first-class ElastiCache engine at a lower price than the Redis OSS option. You get an in-memory data store for caching, sessions, queues, and rate limiting, without running the servers yourself. I did this on a single tiny node so the whole exercise cost pennies.

**info:**
**What you need to follow along:** an AWS account, a region picked (I used `us-east-1`), and permission to create ElastiCache, EC2, and security-group resources. I was signed in as an admin, so I skipped the IAM policy step. If you are not an admin, attach `AmazonElastiCacheFullAccess` plus `AmazonEC2ReadOnlyAccess` to your user.

## Why Valkey on AWS, and what it costs

Valkey speaks the same protocol as Redis, so your existing clients and commands (`SET`, `GET`, `DEL`, `TTL`) all work unchanged. On ElastiCache you pick between **Serverless** (fastest to start, TLS always on, a floor around $6/month) and a **node-based cluster** where you choose the server size and pay per hour.

For a demo, the node-based path is cheaper and clearer. I used a single `cache.t4g.micro` node. On **Valkey** that is **$0.0128 per hour** in us-east-1 — about **$0.31 for a full day**, or roughly **$9.30** if you left it up for a month.

That hourly rate is worth a second look, because it is the 20% Valkey discount made concrete: the same `cache.t4g.micro` node is **$0.016/hour on Redis OSS or Memcached**, and 0.0128 ÷ 0.016 is exactly 0.8. You are not reading a rounded marketing number — the discount is in the rate card.

The golden rule that keeps this a pennies exercise regardless: delete everything the same day.

## Valkey, Redis, or Memcached?

ElastiCache actually offers three engines, and if you are new to caching it helps to know what each one is:

- **Redis OSS** is a hugely popular in-memory data store. It is more than a cache: alongside simple `GET`/`SET` it gives you real data structures (lists, hashes, sets, sorted sets, streams), optional persistence to disk, replication, and pub/sub messaging. In early 2024 Redis changed its license to terms that are no longer open source.
- **Valkey** is the community's answer to that license change: a fork of Redis 7.2, kept open source under the Linux Foundation and backed by AWS, Google, Oracle, and others. It speaks the same protocol and commands as Redis, so it is a drop-in replacement, and AWS prices it about 20% cheaper than Redis OSS. That is why I reached for Valkey here.
- **Memcached** is the old-timer: a bare-bones, multi-threaded cache that stores plain key/value pairs and nothing more. No data structures, no persistence, no replication. It is fast and simple, and still a fine choice when all you need is a big, plain cache in front of a database.

Rule of thumb: choose **Valkey** (or Redis) when you want data structures, persistence, or replication, which covers most real applications. Choose **Memcached** only when you want the simplest possible key/value cache and nothing more.

## Serverless or a node-based cluster?

Whichever engine you pick, ElastiCache asks *how* you want to run it. The two options bill and scale very differently:

| Aspect | Serverless | Node-based cluster |
|---|---|---|
| **Capacity** | Scales automatically and instantly | You choose node type and count |
| **Billing** | Data stored (GB-hours) + requests (ECPUs), ~$6/month floor | Per node, per hour |
| **Best for** | Spiky or unpredictable traffic; getting started fast; not managing capacity | Steady, predictable load; fine-grained control; cost tuning with reserved nodes |
| **TLS** | Always on | Optional (you choose at creation) |
| **Cheapest small footprint** | No (that ~$6 floor) | Yes (a `cache.t4g.micro` is pennies a day) |

For production with bursty traffic and nobody to babysit capacity, **Serverless** is the easy win: it scales up and down on its own and you never size a node. For a steady workload, a specific instance type, or a cost-controlled demo like this one, a **node-based cluster** wins. I chose node-based purely because a single tiny node is the cheapest way to learn.

## Step 1: Create the security group first

A Valkey cache lives inside a VPC and sits behind a **security group**, which is just a firewall. I created that first so the cache and the client machine could share it.

In **EC2 → Security Groups → Create security group**, I made `valkey-demo-sg` in the default VPC and added one inbound rule: **Custom TCP, port 6379, source = the same security group**. That self-referencing rule is a clean pattern: anything wearing this security group is allowed to talk to anything else wearing it, no IP addresses to manage.

![Security group inbound rule allowing TCP port 6379 from the same security group](https://karandeepsingh.ca/images/posts/valkey-security-group-6379.png)

## Step 2: Launch an EC2 client in the same VPC

Here is the first thing people miss: **ElastiCache is only reachable from inside its VPC.** You cannot connect from your laptop. You need a small machine in the same network to run your client from.

I launched a `t3a.micro` EC2 instance named `valkey-demo-client` (Ubuntu, though Amazon Linux works just as well), enabled a public IP, and, importantly, attached **both** the SSH security group *and* `valkey-demo-sg`. That second group is what will later let the box reach the cache.

![EC2 instance security tab showing both valkey-demo-sg and the launch-wizard security group attached](https://karandeepsingh.ca/images/posts/valkey-ec2-security-groups.png)

**tip:**
If you use Ubuntu, EC2 Instance Connect (the browser SSH button) may fail because the Ubuntu image does not ship its helper. **Session Manager** is the better route: no key pair, no open SSH port, it just needs the instance to have an IAM role with SSM access. That is how I connected.

## Step 3: Create the Valkey cache

Back in **ElastiCache → Valkey caches → Create**, I chose **Design your own cache → node-based cluster**. One warning: the wizard defaults the **Configuration** preset to a large **Production** node (an `r7g.xlarge`, hundreds of dollars a month). I switched the creation method to full control and set:

- Node type: `cache.t4g.micro`
- Replicas: 0
- Name: `valkey-demo`

![The ElastiCache create-cache wizard with the Valkey engine and a node-based cluster selected, showing the Demo cache.t4g.micro configuration option](https://karandeepsingh.ca/images/posts/valkey-create-wizard.png)

It sat in **Creating** for about seven minutes, then flipped to **Available** with a configuration endpoint like `clustercfg.valkey-demo.xxxxxx.use1.cache.amazonaws.com:6379`.

![ElastiCache cluster details page showing status Available, node type cache.t4g.micro, and the configuration endpoint](https://karandeepsingh.ca/images/posts/valkey-cluster-details.png)

Two settings landed differently than I expected, and both are locked at creation time, so plan for them:

- **Cluster mode was Enabled.** That is fine; you connect via the *configuration endpoint* and good client libraries handle the topology automatically.
- **Encryption in transit was Required.** This means every client must speak **TLS**. It is one extra flag, and it matches how you would really run this, so I left it.

## Step 4: The connection that hung, and why

With the cache Available, I connected to the EC2 box over Session Manager and set up Python. Ubuntu will not let you `pip install` system-wide, so I used a virtual environment, which is just a private sandbox for the packages:

```bash
sudo apt-get update
sudo apt-get install -y python3-venv python3-pip
mkdir valkey-demo && cd valkey-demo
python3 -m venv venv
source venv/bin/activate          # your prompt now starts with (venv)
pip install valkey                # the official Valkey Python client
```

Then I wrote the smallest possible program: connect and `PING`. Because the cache has cluster mode on and TLS required, I used the *cluster* client with `ssl=True`:

```python
# app.py
from valkey.cluster import ValkeyCluster

client = ValkeyCluster(
    host="clustercfg.valkey-demo.xxxxxx.use1.cache.amazonaws.com",
    port=6379,
    ssl=True,
)

print("cache says:", client.ping())
```

Running `python3 app.py` hung for a while, then failed:

```text
valkey.exceptions.ValkeyClusterException: Valkey Cluster cannot be connected.
Please provide at least one reachable node: Timeout connecting to server
```

A timeout, not a rejection, is the classic signature of a **network block**, not a code bug. I checked the cache's security group and found the problem immediately: the create wizard had put the cache on the VPC **`default`** security group, not on `valkey-demo-sg`. My EC2 client wore `valkey-demo-sg`, so it was never allowed in on port 6379.

![ElastiCache connectivity tab showing the cache attached to the default security group instead of valkey-demo-sg](https://karandeepsingh.ca/images/posts/valkey-sg-default-bug.png)

The fix took thirty seconds: **Modify** the cache, add `valkey-demo-sg`, and remove `default`.

![ElastiCache security groups after the fix, showing valkey-demo-sg added alongside the default group](https://karandeepsingh.ca/images/posts/valkey-sg-fixed.png)

Because that group already allowed 6379 from itself, and the client was a member, the very next run connected:

```text
cache says: True
```

**warning:**
This is the number-one gotcha with ElastiCache. The wizard silently defaults the security group. **Always confirm the cache's security group matches the one your client is in.** A timeout with no error message almost always means a blocked port, not broken code.

## Step 5: Test with valkey-cli, the tool you already have

I did not need to write a program to poke at the cache. Valkey ships **`valkey-cli`**, its own command-line client. On Ubuntu I installed it with `sudo apt-get install -y valkey-tools` and connected with the two flags this cache requires:

```bash
valkey-cli -h clustercfg.valkey-demo.xxxxxx.use1.cache.amazonaws.com -p 6379 --tls -c
```

The `--tls` flag is mandatory here (encryption in transit is Required), and `-c` turns on cluster mode so the CLI follows the cache's "that key lives on another shard" redirects. From there it is plain Valkey:

```text
> SET region us-east-1
OK
> GET region
"us-east-1"
> SET version v2.0.1 EX 300
OK
> KEYS *
1) "version"
2) "region"
> DEL region
(integer) 1
```

Two things bit me here, and both are good to know:

- **Keys are case-sensitive.** `GET REGION` returned `(nil)`, while `GET region` returned the value. They are two different keys.
- **TTL really auto-expires.** A key I set with `EX 10` vanished from `KEYS *` on its own after ten seconds. To check the time left from inside the CLI, use `TTL keyname` (it returns seconds remaining, `-1` for no expiry, or `-2` if the key is already gone). Note that `sleep` is a shell command, not a Valkey one, so the cache rejects it.

If you want a fast refresher on the shell side of this, my [Ubuntu Terminal cheatsheet](https://karandeepsingh.ca/cheatsheets/terminal/ubuntu-terminal/) and [Amazon Linux 2 Terminal cheatsheet](https://karandeepsingh.ca/cheatsheets/terminal/al2-terminal/) cover the `apt`/`yum` and systemd commands I leaned on.

## Step 6: When to reach for a script instead

The CLI is perfect for poking around. A script earns its place only when you need something repeatable that the CLI is clumsy at. For me that was a **health check**: connect, write a canary value, read it straight back, and confirm they match, the kind of probe a monitor or cron job runs on a schedule.

I built it up one piece at a time in Python. The core is small:

```python
import time
from valkey.cluster import ValkeyCluster

ENDPOINT = "clustercfg.valkey-demo.xxxxxx.use1.cache.amazonaws.com"

client = ValkeyCluster(host=ENDPOINT, port=6379, ssl=True, decode_responses=True)

# write a unique canary value, then read it back and compare
token = str(time.time())
client.set("healthcheck:canary", token, ex=30)
got = client.get("healthcheck:canary")

if got == token:
    print("OK: cache is healthy (write + read matched)")
else:
    print(f"FAIL: expected {token}, got {got}")
```

A few deliberate choices in that small block:

- The canary uses a **unique timestamp** each run, so a stale leftover value can never fool the check into a false "healthy."
- `ex=30` gives the canary a 30-second TTL, so it **cleans up after itself** and never clutters the cache.
- `ssl=True` is required because of encryption in transit, and `ValkeyCluster` (not the plain client) is used because the cache runs in cluster mode.
- `decode_responses=True` returns plain strings instead of raw bytes, so `got` compares cleanly.

To make it a genuine monitoring probe rather than a demo, you would add two small things: a proper **exit code** (`sys.exit(0)` on pass, `sys.exit(1)` on fail) so cron or a monitor can act on it, and a **connect timeout** so it fails in a few seconds instead of hanging when the cache is down. The Python patterns behind this live in my [Python Basics & CLI cheatsheet](https://karandeepsingh.ca/cheatsheets/python/python-basics-cli/), and if you script AWS more broadly, the [Python Boto3 cheatsheet](https://karandeepsingh.ca/cheatsheets/python/python-boto3/) is the companion piece.

**tip:**
A Python virtual environment only applies to the shell that activated it. A cron job or systemd service must call the venv's Python directly, for example `/home/ubuntu/valkey-demo/venv/bin/python healthcheck.py`, rather than relying on `source venv/bin/activate`.

## Common pitfalls, and how to avoid them

- **Connecting from your laptop.** ElastiCache is VPC-only. Run your client on an EC2 instance (or another resource) inside the same VPC.
- **The default security group.** The create wizard may attach the cache to `default`. Confirm it matches your client's group, or the connection times out with no useful error.
- **Forgetting TLS.** If encryption in transit is Required, every client needs it: `--tls` for the CLI, `ssl=True` in Python. Without it you hang.
- **Cluster mode surprises.** Use the configuration endpoint and a cluster-aware client. Use `SCAN` instead of `KEYS` on large datasets to avoid stalling the server.
- **Leaving it running.** The node bills per hour whether you use it or not. Delete it when you are done.

## Step 7: Clean up so you are not billed

The cheap demo stays cheap only if you tear it down. In **ElastiCache**, delete the `valkey-demo` cache (skip the final backup). Then delete the subnet group, terminate the `valkey-demo-client` EC2 instance, and remove the demo security groups. That returns your hourly charges to zero.

## Wrapping up

Running **Valkey on AWS ElastiCache** is mostly about the network around it: a security group the client shares, an EC2 box inside the VPC, and TLS on the wire. Once those line up, Valkey behaves exactly like the Redis you already know. Test interactively with `valkey-cli`, and reserve scripts for the repeatable jobs, like the health check, where code genuinely beats typing. Build it small, watch the timeouts, and delete it when you are done.

## References and Further Reading

- Valkey Project. [Valkey Documentation](https://valkey.io/docs/).
- Amazon Web Services. [Amazon ElastiCache for Valkey](https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/WhatIs.html).
- Valkey. [valkey-py Python client](https://github.com/valkey-io/valkey-py).
- Amazon Web Services. [ElastiCache subnet groups and VPC access](https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/SubnetGroups.html).
- Amazon Web Services. [Encryption in transit for ElastiCache](https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/in-transit-encryption.html).

**Question:** Have you moved any workloads from Redis to Valkey yet, and what pushed the decision?

## Quiz

1. Why did the first connection to the Valkey cache hang and time out?
   a) The Python code had a syntax error
   b) The cache was created on the default security group, which did not allow the EC2 client in on port 6379
   c) Valkey was not installed on the cache
   d) The endpoint was spelled wrong
   Answer: b. The cache landed on the VPC default security group instead of valkey-demo-sg, so traffic from the EC2 client on port 6379 was blocked and the connection silently timed out.


