Redis vs Valkey: Which Should You Use for Your Next Project?
Redis vs Valkey in 2026: licensing, client compatibility, the RDB format wall, measured performance and a migration path that actually works.
Valkey is a drop-in replacement for Redis — until you try to move your data. This comparison separates the parts that really are drop-in (protocol, clients, config) from the parts that are not (persistence files), with measured numbers instead of vendor claims. Verified 2026-09-28.
The one-line answer
- Valkey — BSD-3 licensed, Linux Foundation governed, wire-compatible with Redis OSS 7.2 and older. Choose it if licensing, vendor neutrality or AWS ElastiCache pricing matter to you.
- Redis — tri-licensed (RSALv2 / SSPLv1 / AGPLv3), faster release cadence, ships search and JSON in core since 8.0. Choose it if you want the feature velocity and AGPLv3 is acceptable.
Both speak RESP2/RESP3, so your application code does not care. The decision is licensing, managed pricing, and how much it costs you to move data.
What actually happened
In March 2024 Redis changed its license away from BSD. The Linux Foundation forked the last BSD commit as Valkey (7.2.4). Redis later added AGPLv3 as a third option starting with Redis 8, so it is OSI-approved open source again — but under a copyleft license, not a permissive one.
Then came the part that surprises people: Redis 7.4 bumped the RDB persistence format from version 11 to version 12, and later releases kept going. That change, not the license, is what actually blocks migrations today.
License and governance
| Valkey | Redis | |
|---|---|---|
| License | BSD-3-Clause | RSALv2, SSPLv1 or AGPLv3 (choose one) |
| OSI-approved | Yes | Yes (AGPLv3 only) |
| Copyleft | No | AGPLv3 is copyleft |
| Governance | Linux Foundation, vendor-neutral | Redis Ltd. |
| Contributors | AWS, Google, Oracle, Ericsson, others | Redis Ltd. plus community |
| Current stable (2026-09-28) | 9.1.2 (2026-09-01) | 8.10.2 (2026-09-17) |
The practical question for a solo developer or small business: will a future license change affect you? Under AGPLv3 you must publish modifications if you offer the modified software as a network service. Running it unmodified behind your app is fine. Under BSD-3 you have no such obligation.
Rule of thumb: if you are embedding a cache in a product you distribute, BSD-3 removes an entire category of legal review.
Client libraries: genuinely drop-in
I tested ioredis 6.0.0 and node-redis 6.2.1 against both Redis 8.10.2 and Valkey 9.1.2 with identical code, including pipelines, MULTI/EXEC and RESP3 negotiation.
| Client | Redis 8.10.2 | Valkey 9.1.2 |
|---|---|---|
| ioredis 6.0.0 | OK | OK |
| node-redis 6.2.1 (RESP3) | OK | OK |
| pipeline / multi | OK | OK |
Switching the client from one to the other is a connection-string change, nothing more.
One gotcha: Valkey reports redis_version:7.2.4 in INFO for backward compatibility. Code that branches on redis_version will conclude it is talking to a 2024 Redis.
docker exec my-valkey valkey-cli INFO server | grep -E 'server_name|valkey_version|redis_version'
# server_name:valkey
# redis_version:7.2.4 <- lie, kept for compatibility
# valkey_version:9.1.2 <- the truth
Detect Valkey with server_name and valkey_version. Never with redis_version.
The RDB wall
This is the section that matters, because it is where “drop-in replacement” stops being true.
Replication compatibility (measured)
A replica loads its primary’s snapshot, so replication is a direct test of format compatibility. Primary on the left, replica on the right:
| Primary | Replica | Result |
|---|---|---|
| Redis 7.2.16 | Valkey 9.1.2 | master_link_status:up — all keys transferred |
| Redis 7.4.11 | Valkey 9.1.2 | failed — Can't handle RDB format version 12 |
| Redis 8.10.2 | Valkey 9.1.2 | failed — Can't handle RDB format version 15 |
| Redis 8.10.2 | Valkey 8.1.10 | failed — Can't handle RDB format version 15 |
| Valkey 9.1.2 | Redis 8.10.2 | up — all keys transferred |
| Valkey 9.1.2 | Valkey 8.1.10 | up |
| Valkey 9.1.2 | Valkey 9.1.2 | up |
Two things to take from this:
- Redis 7.2 and older migrate to Valkey cleanly. Redis 7.4 and newer do not migrate at all, by file or by replication.
- Valkey can go back to Redis. The migration is one-directional in practice: Valkey negotiates a compatible format with an older replica; Redis does not.
DUMP/RESTORE does not save you
The obvious workaround — copy keys one by one with DUMP/RESTORE — fails for the same reason. The payload carries the RDB version and has no magic string to override. Tested with five key types (string, hash, list, sorted set, key with TTL):
| source \ destination | redis 7.2 | redis 7.4 | redis 8.10 | valkey 8.1 | valkey 9.1 |
|---|---|---|---|---|---|
| redis 7.2 | OK 5/5 | OK 5/5 | OK 5/5 | OK 5/5 | OK 5/5 |
| redis 7.4 | FAIL | OK 5/5 | OK 5/5 | FAIL | FAIL |
| redis 8.10 | FAIL | FAIL | OK 5/5 | FAIL | FAIL |
| valkey 8.1 | OK 5/5 | OK 5/5 | OK 5/5 | OK 5/5 | OK 5/5 |
| valkey 9.1 | FAIL | FAIL | FAIL | FAIL | OK 5/5 |
Note the diagonal: everything reads its own format. Note also that valkey 9.1 → valkey 8.1 fails, even though replication between those two versions succeeds. DUMP has no version negotiation; replication does.
Failure message: ERR DUMP payload version or checksum are wrong.
Why it works this way
From Valkey’s own source (src/rdb.h):
/* RDB 11 is the last open-source Redis RDB version, used by Valkey 7.x and 8.x.
*
* RDB 12-79 are reserved for Redis non-compatible RDB formats
*
* We start using high rdb version numbers since Valkey 9.0... */
#define RDB_VERSION 81
#define RDB_FOREIGN_VERSION_MIN 12
#define RDB_FOREIGN_VERSION_MAX 79
Valkey deliberately reserves versions 12–79 for formats it refuses to guess at, and numbers its own from 80 upward to avoid collisions. This is a design decision, not a bug, and it is not going to be “fixed” in a patch release.
What each build actually writes, confirmed from the file header:
| Build | RDB magic written |
|---|---|
| Redis 7.2.16 | REDIS0011 |
| Redis 7.4.11 | REDIS0012 |
| Redis 8.10.2 | REDIS0015 |
| Valkey 8.1.10 | REDIS0011 |
| Valkey 9.0.6 / 9.1.2 | VALKEY080 |
How to migrate
Check which case you are in first:
docker exec my-redis redis-cli INFO server | grep redis_version
Case 1 — Redis 7.2 or older: copy the snapshot
Both servers understand RDB 11, so this is a plain upgrade.
# 1. Quiesce writes, then snapshot the source
docker exec my-redis redis-cli CONFIG SET save ""
docker exec my-redis redis-cli SHUTDOWN NOSAVE # or SAVE then stop
docker cp my-redis:/data/dump.rdb ./dump.rdb
# 2. Start Valkey with that file in place
docker run -d --name my-valkey -v "$PWD:/data" valkey/valkey:9.1.2-alpine \
valkey-server --dir /data --save 60 1 --appendonly yes
# 3. Verify the key count matches what the source had
docker exec my-valkey valkey-cli DBSIZE
docker exec my-valkey valkey-cli INFO keyspace
Zero-downtime alternative: run Valkey as a replica of Redis 7.2, wait for master_link_status:up, point the app at Valkey, then REPLICAOF NO ONE.
docker exec my-valkey valkey-cli REPLICAOF <redis-ip> 6379
docker exec my-valkey valkey-cli INFO replication | grep master_link_status
# ... repoint the application ...
docker exec my-valkey valkey-cli REPLICAOF NO ONE
Case 2 — Redis 7.4 or newer: no direct path
Both the file copy and replication routes are closed, and DUMP/RESTORE is closed too. Your options:
- Application-level migration. Read through the app or a
SCANloop using type-specific commands (GET/SET,HGETALL/HSET,LRANGE/RPUSH,ZRANGEwith scores), and write to Valkey. Slow, but it works regardless of versions and is the only route with no third-party dependency. - A dedicated migration tool. Community converters exist for exactly this case. I did not verify a specific tool end to end, so treat any of them as unverified until you have tested the round trip on a copy of your data.
- Dual-write and cut over. Write to both for a period, then flip reads. The safest option if the cache is reconstructible and you can tolerate a cold period — and often the correct answer for a pure cache, where you can simply start empty.
If your data is a cache and not a store: do not migrate it. Start Valkey empty, let it warm up, and move on. This turns the whole problem into a one-line config change.
Case 3 — Valkey back to Redis
Replication works (see the matrix above), but the file-copy route does not: a VALKEY080 snapshot is refused by every older server, including Valkey 8.x, not just Redis.
| Snapshot | Loads into Redis 7.2–8.10 | Loads into Valkey 8.0/8.1 | Loads into Valkey 9.0/9.1 |
|---|---|---|---|
REDIS0011 (Redis 7.2, Valkey 8.x) |
yes | yes | yes |
REDIS0012 (Redis 7.4) |
Redis 7.2 no, Redis 7.4+ yes | no | no |
REDIS0015 (Redis 8.10) |
Redis 8.10 yes only | no | no |
VALKEY080 (Valkey 9.0/9.1) |
no — Wrong signature trying to load DB from file |
no — Can't handle RDB format version 80 |
yes |
So the migration routes are not equally wide:
- Redis 7.2 → anything: file copy, replication, or
DUMP/RESTORE. All three work. - Redis 7.4+ → Valkey: no route. Neither file, nor replication, nor
DUMP/RESTORE. - Valkey 9 → Valkey 8.1 (downgrade): replication works, file copy does not. Replicate first, then
REPLICAOF NO ONE; never hand it the RDB. - Valkey 9 → Redis 8.10: replication works, file copy does not.
The reason the copy fails where replication succeeds is that replication negotiates a compatible snapshot for the replica, while a file on disk has no one to negotiate with — the loader simply reads the version number and refuses.
One more thing worth knowing: Valkey 9.0 and 9.1 both write VALKEY080 and read each other’s snapshots fine, so patch upgrades inside 9.x are a plain file swap. The wall is at major-version boundaries, not at every release.
Performance: measured, with big caveats
Benchmarked in containers limited to 2 vCPU and 2 GB, 50 clients, no persistence, io-threads 2, on one host. I ran the suite three times; the ranges below span every run. Requests per second, p50 in ms for the P=1 rows:
| Test | Redis 8.10.2 | Valkey 9.1.2 | Redis 7.4.11 |
|---|---|---|---|
| SET, P=1 | 47–52k (0.47–0.52) | 43–49k (0.41–0.44) | 25–43k (0.90) |
| GET, P=1 | 48–62k (0.39–0.50) | 44k (0.41–0.42) | 41–44k (0.46–0.52) |
| LPUSH, P=1 | 63k (0.39) | 43–47k (0.39–0.62) | 50–54k (0.39–0.79) |
| SET, P=16 | 476–585k | 360–746k | 324–333k |
| GET, P=16 | 641–917k | 581–781k | 448–730k |
| LPUSH, P=16 | 311–444k | 241–315k | 254–312k |
| INCR, P=16 | — | 649k (one run) | — |
Do not treat the middle columns as a ranking. The spread within a single server is larger than the spread between servers: Valkey 9.1.2 SET P=16 measured 360k, 493k and then 746k across three runs — a 2.1x range on identical configuration and identical hardware. That is a shared-host container benchmark measuring the host as much as the server.
What survived every run:
- Redis 8.10.2 beats Valkey 9.1.2 on
LPUSH P=16— 311–444k against 241–315k. The only per-operation gap that held consistently in one direction. - Redis 7.4.11 is the slowest of the three on
SET— 324–333k at P=16, below both 8.x/9.x lines in every run. - Everything else overlaps. Redis 8.10.2 and Valkey 9.1.2 traded places on
SET P=16andGET P=16depending on the run.
I originally wrote that Redis 8.10 was “at least level with Valkey on these operations” and that Redis 7.4 was “clearly slower on writes”. A second run did not support either claim — Valkey won SET P=16 outright in the third pass, and Redis 7.4 matched or beat Redis 8.10 on LPUSH. The corrected position: there is no defensible performance argument for choosing between these two, and if you need a real answer for your workload you must benchmark on a dedicated instance with your own key sizes and access patterns.
# Benchmark your own instance properly — pinned, dedicated, realistic payloads
valkey-benchmark -h 127.0.0.1 -p 6379 -a "$REDIS_PASSWORD" \
-n 1000000 -c 50 -P 16 -d 512 -t set,get,lpush,incr -q
Managed pricing: the concrete saving
Where Valkey is unambiguously cheaper is a managed cache. AWS ElastiCache prices Valkey below Redis OSS on both models:
| Valkey | Redis OSS | |
|---|---|---|
| Node-based | 20% cheaper | baseline |
| Serverless data | $0.084 / GB-hour | $0.125 / GB-hour |
| Serverless compute | $0.0023 / million ECPU | $0.0034 / million ECPU |
| Serverless minimum data | 100 MB | 1 GB |
Serverless storage and compute are both roughly 33% cheaper, and the minimum metered floor is ten times smaller. Backups are $0.085 per GiB-month for either engine. Redis OSS also carries extended-support surcharges (+80% in years 1–2, +160% in year 3) once a version goes EOL, which Valkey does not.
For an existing reserved Redis OSS node, AWS allows an engine swap that keeps the reservation at 20% more value.
Feature differences worth knowing
Valkey 9.x added capabilities Redis OSS 7.2 never had:
- Hash field expiration —
HEXPIRE,HTTL,HPERSIST,HGETEX,HGETDEL. Per-field TTLs inside a hash, which previously required a second key and a cleanup job. MSETEX— multi-key set with expiry in one round trip, atomically.SCANin cluster mode, numbered databases in cluster mode, atomic slot migration.
If you are currently emulating per-field TTLs with a side key, that alone justifies Valkey 9.
The reverse: Redis 8.x bundles search, JSON, time series and probabilistic types in core (FT.SEARCH, JSON.SET). Check command availability before committing to either — I confirmed Valkey 9.1.2 has the hash-field commands above and does not have HOTKEYS, ACL ROLES, XACKDEL, JSON.SET, FT.SEARCH or VSIM; those are 9.2-rc1 or Redis-specific.
# Quick capability probe — run this against your own target before migrating
for c in HEXPIRE HGETEX MSETEX JSON.SET FT.SEARCH; do
printf '%-10s %s\n' "$c" "$(docker exec my-valkey valkey-cli $c 2>&1 | head -1)"
done
ERR wrong number of arguments means the command exists. ERR unknown command means it does not.
Our recommendation
Our recommendation for a new self-hosted project in 2026
Valkey 9.1 on a small VPS. BSD-3 removes license review, clients and config files are compatible with everything you already know, and the hash-field expiration commands replace hand-rolled cleanup jobs. Pick the pinned tag, not latest:
# docker-compose.yml
services:
cache:
image: valkey/valkey:9.1.2-alpine
command: >
valkey-server
--appendonly yes
--appendfsync everysec
--maxmemory 512mb
--maxmemory-policy allkeys-lru
--requirepass ${REDIS_PASSWORD}
volumes:
- valkey-data:/data
restart: unless-stopped
volumes:
valkey-data:
Our recommendation when you are already on Redis 8 and it works
Stay on Redis 8. There is no performance argument for moving, AGPLv3 is a genuine open-source license, and — as the matrix above shows — your data cannot be migrated to Valkey directly anyway. Moving now costs you a rebuild of the cache at best, and a custom migration script at worst, for no measurable gain.
Our recommendation if you are on a managed cache
Valkey, for pricing. On ElastiCache that is 20% off nodes and roughly 33% off serverless, with a 100 MB minimum instead of 1 GB.
Cost
Self-hosted, the software is free either way. The bill is the box, and both fit on the same one:
| Line item | Valkey | Redis |
|---|---|---|
| Valkey 9.1.2 / Redis 8.10.2 | $0 | $0 |
| VPS (2 vCPU / 4 GB, 2 GB given to the cache) | ~€5.99–6.49/mo | same |
| Managed cache (ElastiCache, node-based, smallest) | ~20% below Redis OSS | baseline |
| Managed serverless, 1 GB stored | ~$61/mo at $0.084/GB-hr | ~$91/mo at $0.125/GB-hr |
Assumptions: 1 GB of resident data, ~1 vCPU-equivalent of steady compute, EU region, on-demand, no reserved commitment. Serverless figures are storage-dominated; compute adds ECPU charges on top. Verify current prices before budgeting — both vendors change them without notice.
Common problems
Can't handle RDB format version 12/15— you are migrating from Redis 7.4 or newer. There is no flag for this. See Case 2.Can't handle RDB format version 80— a Valkey 9 snapshot handed to Valkey 8.x, or to any Redis. Replicate instead of copying the file.Wrong signature trying to load DB from file— aVALKEY080file loaded by an older Redis, which rejects the magic string before it even reads the version.ERR DUMP payload version or checksum are wrong— same wall, hit throughDUMP/RESTORE. Not a checksum problem despite the wording.- Monitoring reports the wrong Redis version — Valkey’s
INFOsaysredis_version:7.2.4. Readserver_nameandvalkey_versioninstead. - A library refuses to start against Valkey — some clients run a version gate on
redis_version. Both major Node.js clients were fine in testing; a pinned legacy client may not be. - Pulling
latestand getting a format bump — pin the tag. A major-version jump can change the RDB version your backups are written in, which is the same trap one release later. - Assuming the migration is reversible — it is not symmetric. Valkey reads Redis 7.2 and older; Redis reads neither Valkey’s format nor Redis 7.4+’s.
Related guides
- Hetzner vs DigitalOcean: Which VPS Should You Use for a Small SaaS?
- Docker Compose vs Kubernetes for Small Projects
- How Much Does It Cost to Run a SaaS with 1,000 Users?
- PostgreSQL vs MySQL for SaaS Applications
Conclusion
Valkey and Redis are the same protocol, the same client libraries and the same config format. They are not the same persistence format, and that asymmetry — one-directional, deliberate, and documented only in a source comment — is the single thing most likely to break your migration.
For a new project, Valkey’s BSD-3 license and 20–33% managed-cache discount make it the default. For an existing Redis 8 deployment, do nothing: the performance difference is inside the noise, and the data cannot come with you.