mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
8a872529fe
* docs: make /worlds the canonical home for World docs The world pages (Local/Postgres/Vercel) and Building a World were duplicated inside the v4 and v5 docs trees while /worlds/[id] rendered the v4 copy — hiding v5-only content like multi-region and leaving two diverging sources of truth. - Move world docs to an unversioned docs/content/worlds/ collection (based on the v5 copies, with inline 4.x callouts for factory naming and 5.x-only env vars), rendered at /worlds/* - Add /worlds/building-a-world; flatten the docs Deploying section to a single intro page and drop its Rocket icon - Point every link, frontmatter ref, and worlds-manifest docs field at /worlds/*; add redirects for the removed v5 and building-a-world URLs - Keep world docs on agent-facing surfaces: search, llms.txt, sitemap.md/.xml, and .md exports now serve the worlds collection - Extend the docs link linter to validate worlds pages (with heading anchors) and their outgoing links Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> * docs: version the world docs like the docs trees (v4/v5 switcher) Instead of a single unversioned copy, world docs now follow the same versioning strategy as the docs pages: content/worlds/v4 is served at /worlds/* (current) and content/worlds/v5 at /v5/worlds/*, restoring the original per-version content. Each world detail page (and Building a World) renders the docs version switcher — the worlds listing page has no natural home for it, so it lives on the world pages themselves. - Render-time href rewriting on v5 pages now covers /worlds/... links (shared rewriteHrefForVersion helper, also used by the v5 docs and cookbook routes), and the markdown-export rewrite does the same - v5 world pages are noindexed with a canonical to /worlds/<id>; community worlds stay unversioned (/v5/worlds/<id> redirects) - /v5/docs/deploying/world/* redirects now land on /v5/worlds/*; /v5/worlds and /v5/worlds/compare redirect to the unversioned pages - Link linter models the versioned worlds URL spaces (v5 pages resolve /worlds hrefs against the v5 collection); sitemap.md and the .md export routes cover /v5/worlds/* Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> * docs: fix v4 multi-region anchor and tighten version-prefix matching Address PR review: - The v4 Deploying page linked /worlds/vercel#multi-region, but the Multi-region section only exists on the v5 world page; use the explicit cross-version /v5/worlds/vercel#multi-region link (this was the Docs Links CI failure) - rewriteHrefForVersion now uses the boundary-checked hasPathPrefix (shared leaf module lib/geistdocs/path-prefix.ts, also used by source.ts) instead of bare startsWith - buildVersionUrl's shared-route fast path is segment-based rather than substring includes() Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> --------- Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
257 lines
7.7 KiB
Plaintext
257 lines
7.7 KiB
Plaintext
---
|
|
title: Postgres World
|
|
description: Production-ready, self-hosted world using PostgreSQL for storage and graphile-worker for job processing.
|
|
type: integration
|
|
summary: Deploy workflows to your own infrastructure using PostgreSQL and graphile-worker.
|
|
prerequisites:
|
|
- /docs/deploying
|
|
related:
|
|
- /worlds/local
|
|
- /worlds/vercel
|
|
---
|
|
|
|
The Postgres World is a production-ready backend for self-hosted deployments. It uses PostgreSQL for durable storage and [graphile-worker](https://github.com/graphile/worker) 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:
|
|
|
|
```package-install
|
|
@workflow/world-postgres
|
|
```
|
|
|
|
Configure the required environment variables to use the world and point it to your PostgreSQL database:
|
|
|
|
```bash title=".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:
|
|
|
|
<Tabs items={["npm", "pnpm", "Yarn", "Bun"]}>
|
|
|
|
<Tab value="npm">
|
|
|
|
```bash
|
|
npx --package=@workflow/world-postgres bootstrap
|
|
```
|
|
|
|
</Tab>
|
|
|
|
<Tab value="pnpm">
|
|
|
|
```bash
|
|
pnpm dlx --package @workflow/world-postgres bootstrap
|
|
```
|
|
|
|
</Tab>
|
|
|
|
<Tab value="Yarn">
|
|
|
|
```bash
|
|
yarn dlx --package @workflow/world-postgres bootstrap
|
|
```
|
|
|
|
</Tab>
|
|
|
|
<Tab value="Bun">
|
|
|
|
```bash
|
|
bunx --package @workflow/world-postgres bootstrap
|
|
```
|
|
|
|
</Tab>
|
|
|
|
</Tabs>
|
|
|
|
<Callout type="info">
|
|
The migration is idempotent and can safely be run as a post-deployment lifecycle script.
|
|
</Callout>
|
|
|
|
## Starting the World
|
|
|
|
To subscribe to the graphile-worker queue, your workflow app needs to start the world on server start. Here are examples for a few frameworks:
|
|
|
|
<Tabs items={["Next.js", "SvelteKit", "Nitro"]}>
|
|
|
|
<Tab value="Next.js">
|
|
|
|
Create an `instrumentation.ts` file in your project root:
|
|
|
|
```ts title="instrumentation.ts" lineNumbers
|
|
export async function register() {
|
|
if (process.env.NEXT_RUNTIME !== "edge") {
|
|
const { getWorld } = await import("workflow/runtime");
|
|
const world = await getWorld();
|
|
await world.start?.();
|
|
}
|
|
}
|
|
```
|
|
|
|
<Callout type="info">
|
|
Learn more about [Next.js Instrumentation](https://nextjs.org/docs/app/guides/instrumentation).
|
|
</Callout>
|
|
|
|
</Tab>
|
|
|
|
<Tab value="SvelteKit">
|
|
|
|
Create a `src/hooks.server.ts` file:
|
|
|
|
```ts title="src/hooks.server.ts" lineNumbers
|
|
import type { ServerInit } from "@sveltejs/kit";
|
|
|
|
export const init: ServerInit = async () => {
|
|
const { getWorld } = await import("workflow/runtime");
|
|
const world = await getWorld();
|
|
await world.start?.();
|
|
};
|
|
```
|
|
|
|
<Callout type="info">
|
|
Learn more about [SvelteKit Hooks](https://svelte.dev/docs/kit/hooks).
|
|
</Callout>
|
|
|
|
</Tab>
|
|
|
|
<Tab value="Nitro">
|
|
|
|
Create a plugin to start the world on server initialization:
|
|
|
|
```ts title="plugins/start-pg-world.ts" lineNumbers
|
|
import { defineNitroPlugin } from "nitro/~internal/runtime/plugin";
|
|
|
|
export default defineNitroPlugin(async () => {
|
|
const { getWorld } = await import("workflow/runtime");
|
|
const world = await getWorld();
|
|
await world.start?.();
|
|
});
|
|
```
|
|
|
|
Register the plugin in your config:
|
|
|
|
```ts title="nitro.config.ts"
|
|
import { defineNitroConfig } from "nitropack";
|
|
|
|
export default defineNitroConfig({
|
|
modules: ["workflow/nitro"],
|
|
plugins: ["plugins/start-pg-world.ts"],
|
|
});
|
|
```
|
|
|
|
<Callout type="info">
|
|
Learn more about [Nitro Plugins](https://v3.nitro.build/docs/plugins).
|
|
</Callout>
|
|
|
|
</Tab>
|
|
|
|
</Tabs>
|
|
|
|
<Callout type="info">
|
|
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](/worlds/vercel) instead.
|
|
</Callout>
|
|
|
|
## Observability
|
|
|
|
Use the `workflow` CLI to inspect workflows stored in PostgreSQL:
|
|
|
|
```bash
|
|
# 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](/docs/observability) documentation.
|
|
|
|
## Testing & Compatibility
|
|
|
|
<WorldTestingPerformance worldId="postgres" />
|
|
|
|
## 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 graphile-worker queue job names. Useful when sharing a database between multiple applications.
|
|
|
|
### `WORKFLOW_POSTGRES_WORKER_CONCURRENCY`
|
|
|
|
Number of concurrent workers polling for jobs. Default: `50`.
|
|
|
|
This value also bounds how many parent→child workflow polls can be in flight simultaneously. Every `await childRun.returnValue` inside a workflow holds a worker slot until the child run terminates — if you expect recursive or highly-fanned-out parent/child workflows, raise this ceiling above the peak number of concurrent polls. With the default of 50, the included `fibonacciWorkflow` e2e test (fib(6), ~24 concurrent polls at peak) passes; deeper recursion or larger fanouts need a correspondingly larger setting.
|
|
|
|
### `WORKFLOW_POSTGRES_MAX_POOL_SIZE`
|
|
|
|
Maximum size of the internal `pg.Pool` used when `createWorld()` constructs the pool. Default: `10`
|
|
|
|
For higher worker concurrency, Graphile Worker recommends setting `maxPoolSize` to `10` or `queueConcurrency + 2`, whichever is larger.
|
|
|
|
### Programmatic configuration
|
|
|
|
{/*@skip-typecheck: incomplete code sample*/}
|
|
|
|
```typescript title="workflow.config.ts" lineNumbers
|
|
import { createWorld } from "@workflow/world-postgres";
|
|
|
|
const world = createWorld({
|
|
connectionString: "postgres://user:password@host:5432/database",
|
|
jobPrefix: "myapp_",
|
|
queueConcurrency: 50,
|
|
maxPoolSize: 52, // overrides WORKFLOW_POSTGRES_MAX_POOL_SIZE
|
|
});
|
|
```
|
|
|
|
## 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** - [graphile-worker](https://github.com/graphile/worker) 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](https://github.com/vercel/workflow/tree/main/packages/world-postgres).
|
|
|
|
## 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
|
|
|
|
<Callout type="info">
|
|
The Postgres World is not compatible with Vercel deployments. On Vercel, workflows automatically use the [Vercel World](/worlds/vercel) with zero configuration.
|
|
</Callout>
|
|
|
|
## 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](/worlds/vercel) for Vercel deployments
|
|
|
|
For local development, use the [Local World](/worlds/local) which requires no external services.
|