Postgres

@workflow/world-postgres

PostgreSQL-based world for multi-host deployments

The Postgres World is a production-ready backend for self-hosted deployments. It uses PostgreSQL for durable storage and pg-boss for reliable job processing.

Use the Postgres World when you need to deploy workflows on your own infrastructure outside of Vercel - such as a Docker container, Kubernetes cluster, or any cloud that supports long-running servers.

Installation

Install the Postgres World package in your workflow project:

npm install @workflow/world-postgres

Configure the required environment variables to use the world and point it to your PostgreSQL database:

.env
WORKFLOW_TARGET_WORLD="@workflow/world-postgres"
WORKFLOW_POSTGRES_URL="postgres://user:password@host:5432/database"

Run the migration script to create the necessary tables in your database. Ensure WORKFLOW_POSTGRES_URL is set when running this command:

npx workflow-postgres-setup

The migration is idempotent and can safely be run as a post-deployment lifecycle script.

Starting the World

To subscribe to the pg-boss queue, your workflow app needs to start the world on server start. Here are examples for a few frameworks:

Create an instrumentation.ts file in your project root:

instrumentation.ts
export async function register() {
  if (process.env.NEXT_RUNTIME !== "edge") {
    const { getWorld } = await import("workflow/runtime");
    await getWorld().start?.();
  }
}

Learn more about Next.js Instrumentation.

Create a src/hooks.server.ts file:

src/hooks.server.ts
import type { ServerInit } from "@sveltejs/kit";

export const init: ServerInit = async () => {
  const { getWorld } = await import("workflow/runtime");
  await getWorld().start?.();
};

Learn more about SvelteKit Hooks.

Create a plugin to start the world on server initialization:

plugins/start-pg-world.ts
import { defineNitroPlugin } from "nitro/~internal/runtime/plugin";

export default defineNitroPlugin(async () => {
  const { getWorld } = await import("workflow/runtime");
  await getWorld().start?.();
});

Register the plugin in your config:

nitro.config.ts
import { defineNitroConfig } from "nitropack";

export default defineNitroConfig({
  modules: ["workflow/nitro"],
  plugins: ["plugins/start-pg-world.ts"],
});

Learn more about Nitro Plugins.

The Postgres World requires a long-lived worker process that polls the database for jobs. This does not work on serverless environments. For Vercel deployments, use the Vercel World instead.

Observability

Use the workflow CLI to inspect workflows stored in PostgreSQL:

# Set your database URL
export WORKFLOW_POSTGRES_URL="postgres://user:password@host:5432/database"

# List workflow runs
npx workflow inspect runs --backend @workflow/world-postgres

# Launch the web UI
npx workflow web --backend @workflow/world-postgres

If WORKFLOW_POSTGRES_URL is not set, the CLI defaults to postgres://world:world@localhost:5432/world.

Learn more in the Observability documentation.

Testing & Performance

E2E Tests

Passing100% passing

Spec compliance is tested against Next.js (Turbopack) built in production mode and started with `next start`. View CI run →

78
Passed
0
Failed
0
Skipped
78
Total
View comprehensive E2E test results against all frameworks/configurations
460
Passed
0
Failed
8
Skipped
468
Total

Benchmarks

Click on a benchmark to view performance history over the last 30 commits.

Benchmark
Time
Samples
Promise.all with 10 concurrent steps
2.43s10
Promise.all with 25 concurrent steps
2.83s10
Promise.race with 10 concurrent steps
1.61s14
Promise.race with 25 concurrent steps
2.93s10
workflow with 1 step
2.15s10
workflow with 10 sequential steps
20.42s5
workflow with no steps
284ms10

Stream Benchmarks

Benchmark
Time
TTFB
Slurp
Samples
workflow with stream2.35s2.69s10

Last updated: 1/14/2026, 10:31:00 PM · Commit: abdca8f

Configuration

All configuration options can be set via environment variables or programmatically via createWorld().

WORKFLOW_POSTGRES_URL (required)

PostgreSQL connection string. Falls back to DATABASE_URL if not set.

Default: postgres://world:world@localhost:5432/world

WORKFLOW_POSTGRES_JOB_PREFIX

Prefix for pg-boss queue job names. Useful when sharing a database between multiple applications.

WORKFLOW_POSTGRES_WORKER_CONCURRENCY

Number of concurrent workers polling for jobs. Default: 10

Programmatic configuration

workflow.config.ts
import { createWorld } from "@workflow/world-postgres";

const world = createWorld({
  connectionString: "postgres://user:password@host:5432/database",
  jobPrefix: "myapp_",
  queueConcurrency: 20,
});

How It Works

The Postgres World uses PostgreSQL as a durable backend:

  • Storage - Workflow runs, events, steps, and hooks are stored in PostgreSQL tables
  • Job Queue - pg-boss handles reliable job processing with retries
  • Streaming - PostgreSQL NOTIFY/LISTEN enables real-time event distribution

This architecture ensures workflows survive application restarts with all state reliably persisted. For implementation details, see the source code.

Deployment

Deploy your application to any cloud that supports long-running servers:

  • Docker containers
  • Kubernetes clusters
  • Virtual machines
  • Platform-as-a-Service providers (Railway, Render, Fly.io, etc.)

Ensure your deployment has:

  1. Network access to your PostgreSQL database
  2. Environment variables configured correctly
  3. The start() function called on server initialization

The Postgres World is not compatible with Vercel deployments. On Vercel, workflows automatically use the Vercel World with zero configuration.

Limitations

  • Requires long-running process - Must call start() on server initialization; not compatible with serverless platforms
  • PostgreSQL infrastructure - Requires a PostgreSQL database (self-hosted or managed)
  • Not compatible with Vercel - Use the Vercel World for Vercel deployments

For local development, use the Local World which requires no external services.