---
title: "Upstash"
description: "Use Upstash Redis for the MainSDK key-value store, cache and rate limits - setup, main.yaml, secrets, local testing and swapping."
section: "integrations"
canonical: "https://mainsdk.dev/docs/integrations/upstash"
updated: "2026-10-28"
---

# Upstash

> Use Upstash Redis for the MainSDK key-value store, cache and rate limits - setup, main.yaml, secrets, local testing and swapping.

Canonical: https://mainsdk.dev/docs/integrations/upstash · Updated: 2026-10-28

Upstash is serverless Redis reached over HTTP, and a driver behind two interfaces: [`kv`](https://mainsdk.dev/docs/interfaces/kv), for small values and cached results, and [`limits`](https://mainsdk.dev/docs/interfaces/limits), for rate limits, locks and quotas. Because it needs no long-lived connection, it fits serverless and edge functions where a classic Redis connection pool does not. Pick Upstash when you deploy on a serverless platform and want one store for your cache and your counters.

> **Note:** `kv` and `limits` have no spec yet. The calls and options on this page are provisional, taken from the catalog and the product requirements, and may change when the spec lands.

## Interfaces it serves

### Key-value store and cache (`kv`)

- Status: Building for 0.3 · ships as a beta API
- Driver: `upstash` (Upstash Redis). planned; reach it today with the redis driver and a rediss:// URL
- Docs: https://mainsdk.dev/docs/interfaces/kv.md

```yaml
kv:
  driver: upstash
```

Swap to another provider: `local`, `redis`, `vercel-runtime-cache`, `cloudflare-kv`.

## Before you start

You need an Upstash account.

1. **Create a Redis database.** Choose a primary region close to where your app runs. Upstash can also replicate reads to more regions; use that only for data that may be copied to every region it lists.
2. **Copy the REST URL and token.** Upstash shows them in the database's details. The token grants read and write access, so treat it as a secret.
3. **Decide on one database or two.** You can share one database between `kv` and `limits`, or keep counters apart from cached data so a cache flush never resets a rate limit.

Store the URL and token in your secrets manager, never in code or a committed env file.

## Configure

For the cache and key-value store:

```yaml title="main.yaml"
kv:
  driver: upstash
  # token: secret:UPSTASH_REDIS_REST_TOKEN   # suggested name; key names are [TBD] until the kv spec
```

For rate limits, locks and quotas:

```yaml title="main.yaml"
limits:
  driver: upstash   # was builtin
```

How the `limits` driver gets its URL and token, and whether it can share the `kv` connection, are [TBD]. There is no default TTL for `kv`: you pass `ttl` on every write.

## Use it

Cache a slow lookup:

```ts title="lib/prices.ts"
import { cache } from "mainsdk";

export async function pricesFor(market: string) {
  return cache.wrap(`prices:${market}`, () => loadPrices(market), {
    ttl: "5m",
    tags: ["prices"],
  });
}
```

Rate-limit an expensive endpoint:

```ts title="app/api/export/route.ts"
import { auth, limits } from "mainsdk";

export async function POST(req: Request) {
  const session = await auth.require(req);
  const ok = await limits.rate(`export:${session.user.id}`, { per: "1m", max: 5 });
  if (!ok) return new Response("Too many requests", { status: 429 });
  return Response.json({ started: true });
}
```

`limits.lock(name, fn)` runs `fn` on one worker at a time, and `limits.quota(org, metric)` reports where an organisation stands against its plan.

## Local development and testing

- **No account:** tests don't need Upstash. `limits` defaults to the `builtin` driver, so you can set Upstash in `main.env.prod.yaml` only. The local driver for `kv` is not named yet [TBD].
- **A real database:** a separate Upstash database for development keeps test counters and cache out of production.
- **Tests:** `createTestApp()` from `@mainsdk/testing` runs `kv` and `limits` on in-memory fakes. Use `clock.advance()` to pass a TTL or reset a rate window, and `chaos.on("kv", { outage: true })` or `chaos.on("limits", { outage: true })` to simulate an Upstash outage.

```ts title="lib/export-limit.test.ts"
import { it } from "node:test";
import assert from "node:assert/strict";
import { createTestApp, clock } from "@mainsdk/testing";
import { limits } from "mainsdk";

it("refuses the sixth export in a minute and allows it after", async () => {
  createTestApp({ main: "./main.yaml" });
  const rule = { per: "1m", max: 5 };
  for (let i = 0; i < 5; i++) assert.equal(await limits.rate("export:u1", rule), true);
  assert.equal(await limits.rate("export:u1", rule), false);
  clock.advance("1m");
  assert.equal(await limits.rate("export:u1", rule), true);
});
```

## What MainSDK adds on top

- `kv` keys scoped per tenant, a TTL on every entry, and no personal data in shared caches without a label.
- Sign-in, sign-up, password reset and AI calls rate limited by default, whichever `limits` driver you pick.
- Abuse and bot protection as part of `limits`.
- The same calls on every driver, so you can move counters between the built-in driver and Upstash with one line.

See [Key-value store and cache](https://mainsdk.dev/docs/interfaces/kv) and [Rate limits and quotas](https://mainsdk.dev/docs/interfaces/limits).

## Limits and notes

- `upstash` is a planned driver for `kv` and `limits`, both phase 1, and neither interface has a spec yet.
- Pick database regions that match your data rules. Cached values that hold personal data follow the same residency rules as the records they came from (US, EU or India); see [Data and regions](https://mainsdk.dev/docs/concepts/data-and-regions).
- Limit by user or organisation ID rather than by an email address, so personal data stays out of counter keys.
- Use [`db`](https://mainsdk.dev/docs/interfaces/db), not `kv`, for data you must keep.

## Swap to or from it

```yaml title="main.yaml"
kv:
  driver: upstash   # was redis
limits:
  driver: upstash   # was builtin
```

Cached entries refill as they expire, and rate-limit counters start fresh on the new driver. No migration is needed.

## FAQ

**Upstash or plain Redis?** Upstash suits serverless functions because it is reached over HTTP. Use [Redis](https://mainsdk.dev/docs/integrations/redis) when you already run a server in your own network.

**Do I need Upstash for rate limits?** No. `limits` works on its `builtin` driver. Move to Upstash when you want counters shared across many instances in a store you control.

**Can one Upstash database serve both `kv` and `limits`?** Whether the two drivers share one connection is [TBD]; two databases always work.

## Next

- [Key-value store and cache](https://mainsdk.dev/docs/interfaces/kv) and [Rate limits and quotas](https://mainsdk.dev/docs/interfaces/limits)
- [Redis](https://mainsdk.dev/docs/integrations/redis), [Arcjet](https://mainsdk.dev/docs/integrations/arcjet) and [Vercel](https://mainsdk.dev/docs/integrations/vercel)
- [Swap a driver](https://mainsdk.dev/docs/guides/swap-a-driver)
