Skip to content

vqueue: Scheduled HTTP Webhooks on Your Own Valkey

5 min readValkeyQueuesLayerbase

Short version: vqueue is an HTTP webhook queue that runs on the Valkey database you already have on Layerbase Cloud, and it implements the API surface the open-source @upstash/qstash client expects. Point that client at your own database, keep your publisher and receiver code, and stop paying a second vendor for the queue. It is free regardless of how many messages you publish, because the Valkey underneath it is the thing you are already paying for.

We just shipped vqueue, a free HTTP webhook queue layered on top of every Layerbase Cloud Valkey instance. If your app already uses @upstash/qstash to schedule webhooks, you can point it at your own Layerbase database and stop paying a second vendor for the same queue.

The Layerbase CLI (formerly SpinDB) (what is the Layerbase CLI?) is the local CLI Layerbase ships for spinning up Valkey on your laptop in 30 seconds. vqueue is the cloud-side product surface, not the CLI, but the same Valkey runs in both places.

The problem

Real apps need more than a database. Almost every web app or serverless project that hits production also wants:

  1. A cache (Valkey or Redis)
  2. A background job worker (BullMQ on top of Redis, or a managed queue)
  3. Scheduled HTTP delivery so a Vercel or Cloudflare function can say "fire this webhook in 30 seconds" without holding a long-running connection

Today most teams pay one vendor for Valkey, a second for managed queues. The schemas, billing, and dashboards never line up. The cache and the queue are on different infrastructure even though they both want Redis-protocol storage underneath.

What vqueue is

vqueue is a small Hono route on Layerbase Cloud that implements the HTTP API surface the open-source @upstash/qstash client expects, backed by the Valkey instance you already provisioned. Endpoints:

  • POST /v2/batch for an array of message publishes (this is what @upstash/qstash batchJSON() calls under the hood)
  • POST /v2/publish/<destination-url> for a single message

Each delivery carries an Upstash-Signature JWT signed with HS256, identical to the format that the @upstash/qstash Receiver.verify() call accepts. Drop the env vars from your Layerbase Valkey detail page into your app and the existing client code routes through your database with zero changes.

bash
VQUEUE_URL=https://your-db-name-queue.cloud.layerbase.dev
VQUEUE_TOKEN=<your database password>
VQUEUE_CURRENT_SIGNING_KEY=<derived; shown in the dashboard>

Try it locally with the Layerbase CLI first

You can build the receiver side and signature verification on your laptop before touching the cloud at all. Spin up a local Valkey instance:

bash
npm i -g layerbase
lbase create my-queue --engine valkey --start --connect

Connection string:

bash
lbase url my-queue
# redis://default:<password>@127.0.0.1:6379/0

Build the receiver locally using the same @upstash/qstash client library. When you're ready to go live, provision a Valkey on Layerbase Cloud and swap the env vars over.

Publisher example

ts
import { Client } from '@upstash/qstash'

const queue = new Client({
  baseUrl: process.env.VQUEUE_URL,
  token: process.env.VQUEUE_TOKEN,
})

await queue.batchJSON([
  {
    url: 'https://your-app.example.com/api/jobs/send-email',
    body: { to: 'you@example.com' },
  },
])

Receiver example

ts
import { Receiver } from '@upstash/qstash'

const receiver = new Receiver({
  currentSigningKey: process.env.VQUEUE_CURRENT_SIGNING_KEY!,
  nextSigningKey: process.env.VQUEUE_NEXT_SIGNING_KEY ?? '',
})

export async function POST(req: Request) {
  const rawBody = await req.text()
  const signature = req.headers.get('upstash-signature') ?? ''
  const isValid = await receiver.verify({
    signature,
    body: rawBody,
    url: 'https://your-app.example.com/api/jobs/send-email',
  })
  if (!isValid) return new Response('bad signature', { status: 403 })
  // ...do the work
}

That's it. Same code shape you'd write against any @upstash/qstash-compatible endpoint; just a different URL and a different signing key.

What's in v1

  • POST /v2/batch (the canonical publish path used by batchJSON())
  • POST /v2/publish/<destination> for single-message parity
  • Outbound HS256-JWT signature compatible with Receiver.verify()
  • Upstash-Forward-* header passthrough
  • Backed by the Valkey instance you already pay for; free regardless of how many messages you publish

What's coming

  • Scheduled delivery (Upstash-Delay)
  • Dead-letter queue inspection
  • Workflow compatibility layer on top of vqueue
  • Dashboard view of in-flight and recently-delivered messages

For now jobs fire immediately and best-effort with logged failures. That covers the most common use shape (fire-and-forget webhook delivery, async background work, event fan-out), which is roughly 80% of why teams reach for a managed queue.

Useful Layerbase CLI commands

bash
# Spin up a fresh Valkey for development
lbase create dev-queue --engine valkey --start --connect

# Get the connection string
lbase url dev-queue

# Stop it when you're done; the data is preserved
lbase stop dev-queue

# Start it again later
lbase start dev-queue

The Layerbase CLI supports 21 database engines with the same install-and-run workflow, so the same project that uses vqueue against a Valkey can also pull a local Postgres, ClickHouse, or libSQL the same way.

FAQ

Do I have to rewrite my code to use vqueue?

No. vqueue implements the HTTP endpoints @upstash/qstash calls, and every delivery carries an Upstash-Signature JWT signed with HS256 in the format Receiver.verify() accepts. Swap three environment variables and your existing publisher and receiver keep working.

What does vqueue cost?

Nothing beyond the Valkey instance it runs on. There is no per-message charge, no separate signup, and no second bill, because the storage underneath is the database you already provisioned.

Which endpoints does v1 implement?

POST /v2/batch, which is what batchJSON() calls under the hood, and POST /v2/publish/<destination> for single-message parity. Plus the outbound HS256 JWT signature and Upstash-Forward-* header passthrough.

Can I schedule a delivery for later?

Not yet. Jobs fire immediately and best-effort today, with failures logged. Scheduled delivery via Upstash-Delay, dead-letter queue inspection, a workflow compatibility layer, and a dashboard view of in-flight messages are what is next.

Can I develop against it locally?

Yes, and it is the cheapest way to build the receiver side. Start a local Valkey with the Layerbase CLI, write the receiver and the signature verification against the same @upstash/qstash library, then swap the environment variables when you go live.

Where do the environment variables come from?

Every Valkey database on Layerbase Cloud shows a vqueue block on its detail page with VQUEUE_URL, VQUEUE_TOKEN, and VQUEUE_CURRENT_SIGNING_KEY ready to copy. The block only appears on engines where it means something.

Where to find the env vars

Every Layerbase Cloud Valkey database now shows a vqueue block on its detail page with VQUEUE_URL, VQUEUE_TOKEN, and VQUEUE_CURRENT_SIGNING_KEY ready to copy. The block is hidden on non-eligible engines so you only see it where it makes sense.

Free with any Valkey instance, no separate signup, no second bill. If you're already running @upstash/qstash against another provider, swap the env vars and ship.