Crux
GuidesDurable Execution

Next and Vercel Runtime

Run the Crux Runtime Engine on serverless deployments with Postgres state and QStash wake delivery.

Use the serverless runtime when work must continue after a request ends and the deployment may be cold between wakes.

Install

pnpm add @use-crux/core @use-crux/postgres @use-crux/upstash pg @upstash/qstash

Configure runtime

import { config } from '@use-crux/core'
import { serverless } from '@use-crux/core/runtime'
import { postgres } from '@use-crux/postgres/runtime'
import { qstash } from '@use-crux/upstash/runtime'

export default config({
  runtime: serverless({
    store: postgres({ url: process.env.DATABASE_URL }),
    wake: qstash(),
    publicUrl: process.env.CRUX_PUBLIC_URL,
    // Optional on Vercel: production and preview namespaces are inferred.
    namespace: process.env.CRUX_RUNTIME_NAMESPACE,
    retention: {
      events: '24h',
      terminalWork: '7d',
      terminalSnapshots: '30d',
    },
  }),
})

publicUrl can be omitted when Crux can infer a stable URL from Vercel environment variables. Production setup fails with PUBLIC_URL_UNRESOLVED when no stable URL exists.

namespace is also optional on Vercel. VERCEL_ENV=production resolves to production; previews resolve to preview-<sanitized-VERCEL_GIT_COMMIT_REF>, such as preview-feature-foo. An explicit namespace or CRUX_RUNTIME_NAMESPACE still wins when you need a custom partition.

Generate the route

Run generation before framework build:

crux runtime generate

Generated Next files target the public handler API:

import { createRuntimeHandler } from '@use-crux/core/runtime'
import { reviewFlow } from '@/flows/review'
import { embedDocument } from '@/tasks/embed-document'

export const { GET, POST } = createRuntimeHandler({
  targets: [reviewFlow, embedDocument],
})

POST verifies the wake request before durable writes. GET returns health metadata for setup and preflight without exposing secrets.

Verify setup

crux setup --check
crux runtime status

Use crux setup --apply only when you want Crux to create missing Crux-owned Postgres resources. It is additive only.

Production notes

  • Keep DATABASE_URL, QStash credentials, and CRUX_PUBLIC_URL in deployment secrets.
  • Vercel production and preview deployments receive distinct inferred namespaces. Set CRUX_RUNTIME_NAMESPACE or namespace when you need a custom partition; non-Vercel production deployments must set one or serverless() throws NAMESPACE_AMBIGUOUS.
  • Keep retention.idempotencyKeys at least as long as QStash's max wake delay, or set it to false.
  • Regenerate artifacts in CI and fail builds on ARTIFACTS_STALE.
  • Inspect stuck work with crux runtime inspect <workId>; retry blocked/dead-letter work with crux runtime retry <workId> after fixing the cause.

On this page