vqueue: Scheduled HTTP Webhooks on Your Own Valkey
We just shipped vqueue, a free HTTP webhook queue layered on top of every Layerbase Cloud Valkey or Redis 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.
What is SpinDB? 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:
- A cache (Valkey or Redis)
- A background job worker (BullMQ on top of Redis, or a managed queue)
- 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 emulates the QStash HTTP API surface, backed by the Valkey or Redis instance you already provisioned. Endpoints:
POST /v2/batchfor an array of message publishes (this is what@upstash/qstashbatchJSON()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.
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 SpinDB 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:
npm i -g spindb
spindb create my-queue --engine valkey --start --connectConnection string:
spindb url my-queue
# redis://default:<password>@127.0.0.1:6379/0Build 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
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
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 bybatchJSON())POST /v2/publish/<destination>for single-message parity- Outbound HS256-JWT signature compatible with
Receiver.verify() Upstash-Forward-*header passthrough- Backed by the Valkey or Redis 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 SpinDB commands
# Spin up a fresh Valkey for development
spindb create dev-queue --engine valkey --start --connect
# Get the connection string
spindb url dev-queue
# Stop it when you're done; the data is preserved
spindb stop dev-queue
# Start it again later
spindb start dev-queueSpinDB 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.
Where to find the env vars
Every Layerbase Cloud Valkey or Redis 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 or Redis instance, no separate signup, no second bill. If you're already running @upstash/qstash against another provider, swap the env vars and ship.