Migrating from Vercel KV to Layerbase
Vercel KV is Redis. Upstash runs it under the hood, and since Vercel moved its storage products to the Marketplace, KV is provisioned through the Upstash integration rather than as a native Vercel product. The useful consequence for a migration is that there is nothing exotic to move: it is a keyspace, it speaks the Redis wire protocol, and you already hold the one thing you need to copy it, the KV_URL. This post is the move, start to finish. If you are still weighing whether to leave, Vercel KV alternatives: own your Redis makes that case; this post assumes you have decided and walks the migration.
Just want it done? Start at layerbase.com/migrate/vercel-kv. You sign in, the wizard opens with Vercel KV already selected, and it reads your store once with a non-blocking scan and copies every key, type, and TTL into managed Redis (or Valkey): read-once, nothing written back to your KV store. The rest of this post is that same migration explained step by step, plus the manual path.
Contents
- What moves and what does not
- Find your KV_URL
- The migration, step by step
- Redis or Valkey, and the queue question
- Swap the @vercel/kv client
- The manual path
What moves and what does not
Vercel KV holds a Redis keyspace, and the whole keyspace moves.
Moves automatically:
- Every key, with its type preserved: strings, hashes, lists, sets, sorted sets, and streams.
- Every TTL. Expiring keys keep expiring on the same schedule after the move, so sessions, rate-limit windows, and cache entries do not suddenly become immortal.
Does not move:
- The Vercel dashboard wiring. The KV store is an integration on your Vercel project; migrating the data does not touch your deployment. You swap an environment variable when you are ready to cut over (see the client section).
- Nothing else, because there is nothing else. Unlike a database migration with schemas and auth, a KV move is just the keyspace. That is the whole appeal.
Find your KV_URL
This is the only credential you need, and it is already in your project.
In the Vercel dashboard, open your project, go to Storage, click your KV store, and open the .env.local tab. Copy the KV_URL value. It is a rediss:// string (TLS Redis), something like:
rediss://default:<token>@<name>.upstash.io:6379That single string is enough to read the whole store. There is no separate API key and no account email to dig up, because Vercel KV exposes a standard Redis connection string directly.
The migration, step by step
The fastest path is the built-in migration flow. It handles the scan and the copy for you.
Step 1: Start the migration
In the Layerbase dashboard, click New database and choose Migrating from another platform, then pick Vercel KV. (You can also open the Migrate tab on an existing blank database.)
Paste the KV_URL you copied above. Unlike the API-key sources, there is no "list your databases" step here: the connection string points at exactly one store, so the wizard reads it directly.
Step 2: Let it copy
Layerbase provisions a Redis (or Valkey, your choice) instance and reads your store with a non-blocking SCAN, never a blocking KEYS *, so your live app keeps serving reads and writes while the copy runs. Every key is recreated with its type and remaining TTL. For a typical cache or session store this is quick; a very large keyspace takes proportionally longer.
Step 3: Grab the connection string
Open the new database in the dashboard and copy its connection string. It is a standard rediss:// URL:
rediss://layerbase:<password>@your-host.cloud.layerbase.dev:portAny standard Redis client takes it as-is.
Redis or Valkey, and the queue question
The wizard lets you land on either Redis or Valkey. They are wire-compatible, so your commands and data structures are identical on both, and the copy is the same either way. Two things help you choose:
- If you just want a Redis you own at flat pricing, either works. Valkey is the BSD-licensed Redis fork; pick it if you would rather not depend on the Redis license, or Redis if you want the exact upstream.
- If your KV store backs a message queue, pick Valkey. Layerbase's managed queue add-on, vqueue, runs on Valkey. A plain key/value copy brings your data across to either target, but if you want the queue product on the other side, Valkey is where it lives.
Whichever you pick, the pricing is flat per instance rather than per request, which is the point of moving off a metered Upstash-backed store: the bill is the same at a thousand ops a month or a billion.
Swap the @vercel/kv client
If your code already talks to KV through a standard Redis client, this is one environment variable: point REDIS_URL (or whatever you call it) at the new connection string and deploy.
The change that matters is for code using the @vercel/kv client, which is a REST/HTTP wrapper built for edge runtimes.
Before (Vercel KV client):
import { kv } from '@vercel/kv'
await kv.set('session:42', value, { ex: 3600 })
const v = await kv.get('session:42')After (standard Redis client over TCP):
import Redis from 'ioredis'
const redis = new Redis(process.env.REDIS_URL!) // rediss://...cloud.layerbase.dev:port
await redis.set('session:42', JSON.stringify(value), 'EX', 3600)
const v = JSON.parse((await redis.get('session:42')) ?? 'null')Two notes. @vercel/kv auto-serialized objects to JSON; a raw Redis client stores strings, so serialize on the way in and parse on the way out (or keep a thin wrapper that does it). And @vercel/kv exists because some edge runtimes cannot open raw TCP sockets: if you call KV from an edge function, keep that path behind a small Node API route that talks to the new instance over TCP, and use ioredis there.
The manual path
If you would rather drive the copy yourself, your KV_URL is a normal rediss:// endpoint, so redis-cli works. Use --scan (not KEYS *) so you do not block the store, and copy each key with DUMP/RESTORE to preserve type and TTL:
SRC='rediss://default:<token>@<name>.upstash.io:6379' # your KV_URL
DST='rediss://layerbase:<password>@your-host.cloud.layerbase.dev:port'
redis-cli -u "$SRC" --scan | while read -r key; do
ttl=$(redis-cli -u "$SRC" --no-raw PTTL "$key")
dump=$(redis-cli -u "$SRC" --no-raw DUMP "$key")
redis-cli -u "$DST" RESTORE "$key" "${ttl/-1/0}" "$dump"
doneThe ${ttl/-1/0} turns a "no expiry" PTTL of -1 into the 0 that RESTORE wants for a persistent key. This loop is fine for small-to-medium stores; for a large keyspace prefer the wizard, which batches and resumes.
Rehearse locally first
SpinDB runs Valkey locally with no Docker, so you can copy into a local instance and verify before you touch production:
npm i -g spindb
spindb create kv-test -e valkey --start
# point DST at "$(spindb url kv-test)" in the loop above, then check a few keys:
redis-cli -u "$(spindb url kv-test)" PTTL session:42Confirm your TTL-sensitive logic (sessions, rate limits, cache expiries) survived, then run the real copy into Layerbase Cloud.
Leaving Vercel KV is a keyspace copy and a one-line client swap, because it was Redis the whole time. Paste the KV_URL you already have, copy every key and TTL through the wizard or a --scan loop, and land on a Redis or Valkey instance you own at flat pricing. For the decision behind the move, see Vercel KV alternatives; for the same move from the Upstash side (Vercel KV is Upstash underneath), see Migrating from Upstash.