mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
e1e64e3de3
* docs: apply Vercel technical writing standards Audit the complete documentation corpus, package READMEs, skills, and source TSDoc/comments against the vercel-technical-writing skill and style-rules.md. Normalize sentence-case headings without changing published anchors, remove prose em dashes and filler wording, improve active voice and self-contained phrasing, standardize product/brand capitalization, American English, list punctuation, units, and code fence languages, and preserve exact runtime strings/table placeholders. All executable code is unchanged. Modified skills have their metadata versions bumped. * docs: extend writing audit to repository Markdown Apply the same technical-writing rules to design documents, compiler specifications, workbench guides, package changelogs, and the remaining tracked Markdown outside the deployed docs corpus. Preserve historical meaning, commands, output literals, table placeholders, and heading anchors. * docs: exclude generated package changelogs from audit
265 lines
8.1 KiB
Plaintext
265 lines
8.1 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 to deploy workflows on your own infrastructure outside 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:
|
|
|
|
<Callout type="warn">
|
|
This step is specific to worlds that run a background worker, such as Postgres
|
|
World. Worlds whose queue delivers work over HTTP, including the Vercel World,
|
|
have no worker to subscribe, so `start()` does nothing there while the import
|
|
still adds overhead. Pulling in `workflow/runtime` from a server-startup hook puts
|
|
the whole runtime into the cold-start path before the first request is served.
|
|
</Callout>
|
|
|
|
<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
|
|
|
|
You can set all configuration options through environment variables or programmatically through `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 (PaaS) providers, such as Railway, Render, and Fly.io
|
|
|
|
Ensure your deployment has:
|
|
|
|
- Network access to your PostgreSQL database
|
|
- Environment variables configured correctly
|
|
- 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.
|