Skip to content

Vercel KV alternatives: own your Redis in 2026

4 min readVercel KVRedisValkeyDatabases

Vercel KV was Vercel's first-party key/value store: Redis, with Upstash running underneath and a @vercel/kv client that worked nicely from the edge. In late 2024 Vercel moved its storage products to the Vercel Marketplace, so KV is now provisioned through the Upstash integration rather than as a native Vercel product. If you're sorting out where your KV data should live, it's a good moment to ask a different question: do you want to keep renting Redis through a per-request meter, or own an instance at a flat price?

If the answer is "own it," Valkey on Layerbase Cloud is a managed, Redis-compatible instance with flat per-instance pricing. Because Vercel KV is Redis, your data is portable with the KV_URL you already have, and the only code change is swapping the @vercel/kv client for a standard Redis driver.

Contents

What Vercel KV Actually Is

KV is Redis. Vercel exposed it two ways: a KV_URL (a standard rediss:// connection string) and a REST API (KV_REST_API_URL + KV_REST_API_TOKEN) that the @vercel/kv package uses for edge runtimes. That's the same shape as Upstash, because it is Upstash.

So moving off it is the same as moving any Redis:

  • Your keys, types, and TTLs copy over with the rediss:// URL.
  • The @vercel/kv client (REST) becomes a standard Redis client (ioredis) on a Node runtime, or stays HTTP-shaped if you genuinely need edge access.
  • Flat per-instance pricing replaces the per-request meter.

Set Up Valkey Locally with SpinDB

Stand up Valkey locally so you can copy into it and verify first. SpinDB runs it with one CLI, no Docker. (What is SpinDB?)

bash
npm i -g spindb    # npm
pnpm add -g spindb # pnpm

spindb create vercel-kv-migration -e valkey --start
spindb url vercel-kv-migration
text
redis://127.0.0.1:6380

The redis:// scheme is correct; Valkey speaks the same protocol as Redis.

Copy Your Data

The managed wizard (paste your KV_URL)

On Layerbase Cloud, choose Migrating from another platform, pick the connection-string option, and paste your KV_URL (the rediss://... value from your Vercel project's storage settings, or .env.local). It reads the keyspace once with a non-blocking SCAN and copies every key, type, and TTL into a fresh managed Valkey. No REST tokens, no manual export.

By hand with a scan + dump

The KV_URL is a normal rediss:// string, so redis-cli works directly. Use --scan (never KEYS *) and DUMP/RESTORE to preserve types and TTLs:

bash
SRC="$KV_URL"                       # rediss://default:...@...upstash.io:6379
DST='redis://127.0.0.1:6380'        # or your Layerbase Valkey rediss:// URL

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"
done

For large keyspaces, the managed wizard batches and resumes; the loop is fine for typical cache/session data.

Swap the Client

The one code change is the client. @vercel/kv is the Upstash REST client with a Vercel wrapper.

Before (@vercel/kv):

ts
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):

ts
import Redis from 'ioredis'

const redis = new Redis(process.env.REDIS_URL!) // your Valkey rediss:// URL
await redis.set('session:42', JSON.stringify(value), 'EX', 3600)
const v = JSON.parse((await redis.get('session:42')) ?? 'null')

Two small differences: ioredis stores strings, so serialize objects yourself (@vercel/kv auto-JSON-encoded), and the expiry option is 'EX', seconds rather than { ex: seconds }. If you deploy to a Node runtime, this is the simpler path. If you need HTTP from an edge function, keep that path behind a small Node API route that talks to Valkey over TCP.

What to Test

  • Run your suite against the local Valkey before cutting over.
  • Check JSON handling. Since @vercel/kv auto-encoded values, audit every get/set to add explicit JSON.stringify/JSON.parse (or use a small wrapper) so you don't store [object Object].
  • Confirm TTLs carried across on sessions and rate-limit keys (PTTL a few).
  • Use one client per instance, not one per request, especially in serverless.

The Managed Path: Layerbase Cloud

Worth doing the copy once by hand to see the shape. When you want managed Valkey you own, Layerbase Cloud provisions it with TLS, backups, and flat per-instance pricing, and the Migrating from another platform flow copies your data from the KV_URL you already have. Off the per-request meter, onto an instance that's yours.

Wrapping Up

Vercel KV is Redis, so leaving it is a data copy plus a one-client swap: paste your KV_URL into the wizard (or run a --scan + DUMP/RESTORE loop), then replace @vercel/kv with ioredis and handle JSON encoding yourself. The payoff is a flat-priced Redis you control.

Manage your local Valkey instance with SpinDB:

bash
spindb stop vercel-kv-migration    # Stop the server
spindb start vercel-kv-migration   # Start it again
spindb url vercel-kv-migration     # Print the connection URL
spindb list                        # See all your instances

SpinDB handles 20+ database engines, so Valkey can sit next to your Postgres or Meilisearch while you verify the move. Layerbase Desktop wraps it in a GUI on macOS. Coming from Upstash directly? See Migrating from Upstash to Layerbase.

Something not working?