Vercel KV is gone: where your data went and how to move it
If you are still importing @vercel/kv, the product behind that import no longer exists, and npm has been telling you so every install for a while now.
Vercel's own Redis docs are unambiguous: "Vercel KV is no longer available. If you had an existing Vercel KV store, we automatically moved it to Upstash Redis in December 2024. For new projects, install a Redis integration from the Marketplace." The changelog entry that started it is dated 22 October 2024, when Upstash joined the Vercel Marketplace with KV, Vector, and QStash; that integration "replaces Vercel KV," and the page now carries a plain note that "The Vercel KV product has been sunset."
Nothing broke, which is exactly why so many projects never noticed. The migration was zero-downtime and the price did not change. What did change is that you are now a direct Upstash customer with a metered Redis, running a client that nobody maintains, and most teams find this out during an incident rather than on purpose.
This post is the reckoning: what you are actually running, what the deprecated client costs you, and how to move the keyspace onto a Valkey instance with a flat price if that is what you want.
Contents
- Check what you are running
- The client is the real problem
- Two client models, and which one you need
- Moving the keyspace
- Swapping the code
- Verify before you cut over
Check what you are running
Start in the Vercel dashboard, on your project's Storage tab. If the store is listed as an Upstash integration rather than a native "KV" product, the December 2024 move already happened to you, which for practically everyone it did.
Then look at your environment variables, and look rather than assume. The legacy Vercel KV set was four variables: KV_URL, KV_REST_API_URL, KV_REST_API_TOKEN, and KV_REST_API_READ_ONLY_TOKEN. The Upstash SDK reads a different pair, UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN, which is what Redis.fromEnv() picks up. Depending on when your project was provisioned you may have either set, or both. Neither Vercel nor Upstash publishes a definitive list of what the integration injects today, so read your project's actual environment list and work from that rather than from any blog post, including this one.
The variable you want for a migration is a rediss:// connection string. Upstash gives every database a TLS endpoint alongside the REST one, and TLS is not optional there: "TLS is enabled by default for all Upstash Redis databases. It's not possible to disable it." That string is enough to read the entire keyspace.
The client is the real problem
The data situation is fine. The client situation is not.
@vercel/kv on npm is at version 3.0.0, published 27 September 2024, and it carries a deprecation notice on the registry itself:
Vercel KV is deprecated. If you had an existing KV store, it should have moved
to Upstash Redis which you will see under Vercel Integrations. For new projects,
install a Redis integration from Vercel Marketplace.Two years without a release on a package that sits in your request path is the actual forcing function here, not the rebrand. Whatever you migrate to, @vercel/kv has to come out of your dependencies, and once you are editing those call sites anyway, the question of who hosts the data underneath is suddenly cheap to answer.
Two client models, and which one you need
Redis clients come in two shapes, and picking the wrong one is the most common way a migration like this goes sideways.
HTTP/REST. @upstash/redis is, in its own npm description, "an HTTP/REST based Redis client built on top of Upstash REST API." Every command is a stateless HTTPS request with a Authorization: Bearer <token> header. There is no socket to keep alive and no pool to exhaust, which is the entire point: it exists to "access your Upstash Redis database over HTTP, from serverless and edge runtimes where TCP connections are restricted." If your code runs in an edge runtime that cannot open a raw TCP socket, this is not a preference, it is a requirement.
RESP over TCP. ioredis and the redis package hold a persistent TLS socket, authenticate with the password in the URI, and pipeline over that one connection. This is how every Redis client outside the edge has always worked, it is faster per command once the connection is up, and it works against any Redis-compatible server rather than one vendor's HTTP gateway.
The useful thing to know is that these are not exclusive on the source side. Upstash serves both protocols against the same database: their REST docs note that "if you have legacy code that relies on Redis clients, the Redis protocol allows you to utilize Upstash without requiring any modifications." So you can copy your data out with a completely ordinary Redis client today, no REST gymnastics required.
On the destination side, a managed Valkey instance speaks RESP over TLS, so a standard client connects to it unchanged. If part of your app genuinely runs on an edge runtime with no TCP, keep that path behind a small Node route that talks to the instance over TCP, and call the route from the edge. Which engines can be queried over HTTPS instead, and what sleeping and waking costs you, is laid out on the serverless databases page.
Moving the keyspace
A key/value migration is refreshingly boring compared to a relational one. There is no schema, no sequence to reset, no auth to recreate. There is a keyspace, and the keyspace moves.
The managed path does it for you: in the Layerbase create flow, choose Migrating from another platform, pick Vercel KV, and paste the rediss:// string. It provisions the instance and reads your store with a non-blocking SCAN, never a blocking KEYS *, so your live app keeps serving while the copy runs. Every key is recreated with its type and its remaining TTL, and nothing is written back to the source.
If you would rather drive it yourself, the endpoint is an ordinary TLS Redis endpoint, so redis-cli works. Upstash documents the connection form as redis-cli --tls -u rediss://.... A quick census before you start:
SRC='rediss://default:<token>@<name>.upstash.io:6379'
redis-cli --tls -u "$SRC" DBSIZE
redis-cli --tls -u "$SRC" --scan | head -20--scan is the flag that matters. It iterates with SCAN under the hood instead of KEYS *, so it does not block the server while it walks a large keyspace. DUMP, RESTORE, SCAN, and PTTL are all supported on Upstash, so a serialize-and-restore copy preserving every type and TTL works against it. The step-by-step version of that loop, plus the local rehearsal, is written up in migrating from Vercel KV to Layerbase; no point in repeating it here.
One caveat worth knowing before you hand-roll it: RESTORE refuses a payload whose RDB version is newer than the destination understands, and fails with a checksum error rather than something readable. If you hit that, the fix is to copy by type (GET/SET, HGETALL/HSET, and so on) with the TTL reapplied, or to let the wizard handle it, which is one reason the wizard exists.
Swapping the code
@vercel/kv serialized objects to JSON for you. A standard Redis client stores strings, so the serialization becomes yours again. That is the whole diff for most codebases.
Before:
import { kv } from '@vercel/kv'
await kv.set('session:42', session, { ex: 3600 })
const session = await kv.get<Session>('session:42')After:
import Redis from 'ioredis'
const redis = new Redis(process.env.REDIS_URL!) // rediss://...
await redis.set('session:42', JSON.stringify(session), 'EX', 3600)
const raw = await redis.get('session:42')
const session: Session | null = raw ? JSON.parse(raw) : nullIf you would rather not sprinkle JSON.parse through the codebase, write the four-line wrapper that does it and keep your existing call signatures. Most teams that migrate this find the wrapper is smaller than the diff would have been.
The environment change is one variable: point REDIS_URL at the new connection string and deploy. Keep the old store readable for a day or two in case you need to reconcile, then delete it so it stops costing you.
Verify before you cut over
Check the things a keyspace copy can silently get wrong, which are almost always the expiring keys:
DST='rediss://layerbase:<password>@your-host.cloud.layerbase.dev:port'
redis-cli --tls -u "$DST" DBSIZE
redis-cli --tls -u "$DST" PTTL session:42
redis-cli --tls -u "$DST" TYPE leaderboardDBSIZE on both ends should agree, allowing for keys that expired mid-copy. PTTL should return a countdown in milliseconds, not -1. A -1 where you expected a TTL means the key came across as permanent, and sessions that never expire and rate-limit windows that never reset are the two bugs this migration actually produces. TYPE confirms a sorted set did not arrive as a string.
Then decide which engine you are landing on. Redis and Valkey are wire-compatible, so the copy and your commands are identical either way; Valkey is the BSD-licensed fork the open source community maintained after the 2024 license change, and it is the one to pick if depending on the Redis license is part of why you are moving in the first place.
Vercel sunset KV, moved your data into a metered Upstash database without asking, and left a deprecated client in your package.json. That is not a crisis, but it is a decision someone else made for you, and the cost of making your own is a SCAN copy and a one-line client swap.
Create a managed Valkey instance. Flat per-instance pricing with no command meter, the Free plan needs no card, and your existing Redis client connects to it without a code change beyond the URL.
Keep reading
- Migrating from Vercel KV to LayerbaseVercel KV was sunset and its stores moved to Upstash Redis, so the move is a data copy and a one-line client swap. Paste the rediss:// string behind your project Storage tab and copy every key, type, and TTL into flat-priced managed Redis or Valkey.
- Vercel KV alternatives: own your Redis in 2026Vercel KV was first-party Redis built on Upstash, and it was sunset into Upstash Redis on the Vercel Marketplace. If you want a Redis you own at flat pricing, here is how to move to managed Valkey with the rediss:// string you already have.
- Redis free tiers compared: what each one runs out ofEvery free Redis tier is generous until it hits its one real ceiling. Upstash counts commands, Redis Cloud caps you at 30 MB and 30 connections, Render throws your data away on restart, and Layerbase puts idle databases to sleep. Here is what each one runs out of first.
- Migrating from Upstash to LayerbaseUpstash bills per request, which is great at zero traffic and surprising at scale. Here is how to move to flat-priced managed Valkey on Layerbase: copy every key with one API key, and swap the REST client for a standard Redis driver.