Durable background jobs on the Postgres you already have
Queues, retries, crons and durable step functions for TypeScript. Jobs live in their own schema of the database you already run — next to your data, not on someone else's servers.
A demo job, drawn by the same component as the console: attempt 1 times out, attempt 2 hits a rate limit, attempt 3 replays its first step instead of re-running it, and finishes. Select a bar to open its step below.
Background jobs today cost more than the work itself
- Billed per step
- Hosted orchestrators meter every step function invocation — the bill moves with how you wrote the code, not with what it does.
- State far from your data
- Job state and logs live on someone else's servers, often outside your region, so every query pays cross-region latency.
- Redeploys and env sync
- Hosted runners redeploy a copy of your code and expect every environment variable kept in sync by hand.
How it works
Your Postgres
Peacock migrates a dedicated
peacockschema into the database you already run — automatically, idempotently, on first connect. Nothing new to provision.Two ways to run
In-process workers today: point Peacock at
DATABASE_URLand it runs alongside your app. Calling the serverless deploy you already have is planned.Optional cloudPlanned
An optional connection for alerts once retries are exhausted, and serverless wake-ups — never required to run Peacock.
import { Peacock, serve } from "peacock";
// Reads DATABASE_URL, creates its schema on first run.
export const peacock = new Peacock();
const welcome = peacock.createFunction(
{ id: "send-welcome", event: "user/created", retries: 5 },
async ({ event, step }) => {
const { email } = event.data;
await step.run("email", () => sendEmail(email));
await step.sleep("wait", "3d");
await step.run("follow-up", () => sendFollowUp(email));
},
);
export const { GET, POST } = serve(peacock, [welcome]);Every attempt, every step
The job from the top of the page, step by step and line by line. Pick an attempt to follow it on its own.
Every task at a glance
Each registered task with its last 24 hours — runs, failures, p95 and the next cron tick — sorted worst first.
What's real today, and what's planned
Works today
- Queues on Postgres —
FOR UPDATE SKIP LOCKEDclaims, a worker pool, heartbeats, stale-job recovery - Retries with backoff, dead letter (including jobs with no handler), deadlines on stuck jobs
- Durable
step.runwith memoization,step.sleep/step.sleepUntil, memoizedstep.sendEvent, replay traces - Cron with a distributed lock, so exactly one instance ticks
- Concurrency limits per task, plus keyed concurrency per payload value
- Payload validated against a zod schema before it runs
- Per-job and per-attempt logs, persisted steps, task and queue statistics
- The execution timeline above: every attempt, backoff waits, dead letters, and steps replayed from memoization
Planned
- The plug-and-play SDK —
serve(), zero-configinit - Same code, two deploys — a push-HTTP route for your serverless function
- Alerts that understand retries — one alert after retries are exhausted, with context
- Serverless wake-ups from Peacock Cloud
- A multi-app view across every Peacock-connected service
Run your first job on the Postgres you already trust
Early access is open. No credit card, no new infrastructure to provision.