Skip to content
Open source · Apache-2.0

Job queue + workflow control plane

Queue the work.
Keep your code.

A PostgreSQL-backed queue that persists, claims, retries, and observes background work. Your workers stay in your environment and execute the business logic.

Spooled Queue state persist · claim · retry
Your worker Business logic execute · side effects
  • PostgreSQL durability
  • Retry + DLQ
  • SSE + WebSocket
spooled ▸ illustrative workspace Demo
Enqueued
1,204
Running
18
Completed
998,320
Dead-letter
3
Queue activity illustrative
Recent jobs demo data
job_a7f2 stripe.invoice.paid running
job_a7f1 email.send done
job_a7f0 image.resize retry 2/5
job_a7ef webhook.deliver done
Quickstart

Up and running in minutes

Enqueue over the API, run a worker in your environment, then subscribe to state changes. Official SDKs cover Node.js, Python, Go, and PHP.

Enqueue a job

POST a queue name and JSON payload. Pass an idempotency key and duplicate submissions collapse into one job. Bulk enqueue accepts up to 100 jobs per request.

Jobs & queues docs →

Run a worker

Workers run on your infrastructure. They claim jobs, run your code, and report complete or fail. Failures retry with exponential backoff — up to 100 attempts — then land in the dead-letter queue.

Worker docs →

Stream the results

Subscribe over Server-Sent Events or WebSocket instead of polling. The dashboard shows throughput, latency, failures, and DLQ replays from the same streams.

Real-time API docs →
Real-time

See your queues live

The hosted dashboard subscribes to the very streams you saw fan out above — so what you watch here is exactly what your own clients receive, with no polling.

In the dashboard

Job timeline Follow a job through retries to its outcome.
Dead-letter replay Inspect failures and replay once fixed.
Queue stats Throughput, pending counts, and latency at a glance.
Worker health Active workers, leases, and heartbeats in real time.
Open the dashboard →

Same streams, your clients

  • Server-Sent Events One-way stream for dashboards and live views. Reconnects on its own.
  • WebSocket Two-way channel when you need to send filters and commands back.
See the subscribe code ↑
Live demo

Watch jobs flow through the queue

Spooled stores every job in Postgres. Your workers claim and process them in parallel, failures auto-retry, and state changes stream to dashboards and clients. This simulation shows the lifecycle without pretending to be production telemetry.

Pipeline preview

Demo
Incoming 2 queued
job_128 pending
stripe.invoice.paid
job_129 pending
user.signup
Processing 2 active
job_126 processing
github.push
job_127 processing
order.created
Completed 2 done
job_124 completed
email.send
job_125 completed
webhook.deliver

Illustrative states. Pro API allowance: 100 requests/sec (burst 200); completed and failed records are retained for 30 days.

Official SDKs: Node.js · Python · Go · PHP

Other marks show compatible HTTP sources and infrastructure, not native integrations.

  • Stripe
  • GitHub
  • Shopify
  • PostgreSQL
  • Redis
  • Node.js
  • Python
  • Go
  • PHP
Reliability

The safety net around every job

Failed jobs retry with exponential backoff. Exhausted jobs move to the dead-letter queue with payload and error history preserved. This shortened simulation shows inspect, fix, and replay; production jobs can be configured for up to 100 attempts.

job a1f9c… · queue payment-processing

Ready — press play to run the retry flow.

  1. Initial attempt

    t = 0s

    Worker claims the job and runs it

    Failed · HTTP 500
  2. 1

    Retry #1

    t + 1m

    First backoff delay — 1 minute

    Failed · Timeout
  3. 2

    Retry #2

    t + 3m

    Delay doubles — 2 minutes

    Failed · Connection refused
  4. 3

    Retry #3

    t + 7m

    Delay doubles again — 4 minutes

    Failed · HTTP 503
  5. Dead-letter queue

    exhausted

    Payload and error history preserved for inspect & replay

    Parked · inspect & replay

Backoff schedule

1m try 1
2m try 2
4m try 3
8m try 4
16m try 5
32m try 6
60m try 7+
Backoff formula
delay_minutes = min(2 ** retry_count, 60)

Retries are scheduled in minutes (1m, 2m, 4m …), doubling each time and capped at 60m, for up to 100 attempts. Exhausted jobs land in the dead-letter queue with their payload and error history intact — inspect and replay them from the dashboard or the API.

Recovery procedure

Inspect, fix, replay

DLQ API reference →

Exhausted jobs keep their payload and error history. Recovery is explicit rather than silently dropping or endlessly cycling bad work.

  1. 01 / INSPECTRead payload + error historyIdentify whether failure came from data, code, or a dependency.
  2. 02 / FIXCorrect the underlying causeDeploy the worker fix or restore the unavailable service.
  3. 03 / REPLAYRetry one or many jobsReplay through the API or dashboard with traceability intact.
Choose orchestration capability
Orchestration

Order, priority, and timing

Spooled decides what runs, in what order, and when — dependency graphs, priority levels, and cron schedules, all on one Postgres-backed queue.

Declare which jobs depend on which, and Spooled runs the graph for you. Roots go first, independent branches run in parallel, and a failed parent cancels everything downstream.

Workflow run

Live
completed
create-account
queue: users
completed
send-welcome-email
queue: emails
completed
setup-defaults
queue: users
waiting running completed
3 / 3 done

The workflow behind it

Illustrative motion. Workflows are a paid feature — Pro runs up to 25 concurrent workflows; the Free tier does not include them.

Give a job a priority and workers claim the highest first — high (10) before normal (0) before low (-10). Same enqueue call, one extra field.

Waiting to be claimed

6 jobs
  • VIP order High · +10
  • Pager alert High · +10
  • Send receipt Normal · 0
  • Send invite Normal · 0
  • Nightly cleanup Low · -10
  • Rebuild report Low · -10
worker · claiming

Processed in order

0 done
  1. VIP order #1
  2. Pager alert #2
  3. Send receipt #3
  4. Send invite #4
  5. Nightly cleanup #5
  6. Rebuild report #6

Priority only orders the claim — every job still runs. On the Free plan up to 10 jobs run at once, so higher priority simply gets there first.

The one extra field

Describe when with a 6-field cron expression and Spooled enqueues the job for you, second-precise and timezone-aware. Pick a pattern and watch the next runs fill in.

Pick a schedule

Cron expression Morning report
0 sec
0 min
9 hour
* day
* month
* wday

Six fields: second, minute, hour, day-of-month, month, day-of-week.

Next runs

Computed
  1. 1
    Tomorrow, 9:00 AM
    in 6 hours
  2. 2
    Fri, 9:00 AM
    in 1 day
  3. 3
    Sat, 9:00 AM
    in 2 days
  4. 4
    Sun, 9:00 AM
    in 3 days

Preview computed in your browser. Free includes 1 schedule, Pro allows 50 with 30-day job history.

The call that creates the schedule

Throughput

Keep workers fed without polling

gRPC bidirectional streaming keeps a worker and Spooled talking over one open HTTP/2 connection, so a single worker can process thousands of jobs per second — no per-job round trip, no polling.

In practice

See it in real code

Three patterns you can copy today. Each pairs the enqueue side with the worker side — the same durable queue sits between them. Pick a scenario, then expand any block to read the full snippet.

Live proof · SpriteForge ↗

One sprite is one workflow.

Frame generation fans out as parallel jobs. The assemble job waits on every frame, then the result streams back to the client.

  1. prompt
    workflow
  2. frames 1..N
    parallel
  3. dependency
    join
  4. assemble
    result

Reliable Stripe checkout

Ack the Stripe webhook in milliseconds, then let a durable job fulfil the order — retried automatically if fulfilment fails.

1

Receive the event

Verify the signature, enqueue a job, return 200 fast.

2

Fulfil in a worker

Claim → do the work → complete or fail, on your own servers.

How it fits

Division of labor

Spooled never runs your code. It stores jobs, schedules retries, and streams status; your workers do the work, wherever they already run.

Spooled handles

  • Durable job storage in Postgres
  • Retries with exponential backoff
  • Deduplication via idempotency keys
  • Dead-letter queues and replay
  • Schedules, workflows, priorities
  • Live status over SSE / WebSocket

You provide

  • Workers — your code, your servers
  • Email/SMS providers you already use
  • Storage (S3, R2, GCS…)
  • External APIs your jobs call
  • The business logic itself

Your workers stay on your infrastructure and keep your secrets — Spooled only sees the job payloads you send it.

Architecture

Rust core, Postgres durability, open end to end

The API and workers are Rust. PostgreSQL is the source of truth — every job, retry, and dead-letter entry is a row you could query, with every query scoped to your organization. Redis carries only real-time pub/sub for live dashboards and streams; losing it never loses a job.

The core is Apache-2.0: backend, queue engine, and all four SDKs. Self-host it against your own Postgres with no platform limits, or use the hosted cloud and skip the operating. Same API either way.

100
jobs per bulk enqueue
6-field
cron, second precision
SSE + WS
real-time job streams
REST + gRPC
APIs, HTTP/2 streaming

How work flows through Spooled

Data flow: GitHub, Stripe, and HTTP sources call the Spooled API over REST and gRPC; the API persists jobs to durable Postgres with organization-scoped queries and publishes to Redis pub/sub; Prometheus scrapes metrics for observability. GitHub Stripe HTTP Sources Spooled API REST · gRPC Postgres durable · org-scoped Redis pub / sub Datastores Prometheus metrics Observability
claimed completed
FAQ

Questions engineers ask

What is Spooled Cloud?
Spooled is an open-source job queue backed by PostgreSQL. You enqueue jobs over REST or gRPC; your workers claim and run them; Spooled stores every job durably, retries failures with exponential backoff, and moves exhausted jobs to a dead-letter queue. The core is Apache-2.0 and self-hostable, with a hosted cloud option.
Is Spooled a job queue without Redis?
Yes. Jobs are stored in PostgreSQL, so durability does not depend on Redis. Redis is used only for real-time pub/sub (live dashboards and WebSocket/SSE streams), not as the source of truth for job state.
How do retries and the dead-letter queue work?
Failed jobs retry automatically with exponential backoff. You configure max attempts (up to 100) and delays. Jobs that exhaust retries land in the dead-letter queue, where you can inspect payloads, bulk retry, or purge with filters.
What delivery semantics should I design for?
Workers claim jobs with leases. If a worker disappears before reporting completion, the lease expires and the job can be claimed again. Design side effects to be idempotent; enqueue idempotency keys prevent duplicate submissions, but they are not an exactly-once execution guarantee.
Where does my worker code run?
On your infrastructure. Workers are plain processes that claim jobs from Spooled, run your code, and report success or failure. Spooled never executes your code — it stores jobs, schedules retries, and streams status.
How are job payloads stored?
Job payloads are stored as plaintext JSONB in PostgreSQL. Use your database and infrastructure controls to protect access, and put sensitive or large objects in your own storage with a reference in the job payload.
Is Spooled open source? Can I self-host it?
The core — the Rust backend, queue engine, and all SDKs — is Apache-2.0 on GitHub. You can self-host it with your own PostgreSQL with no platform limits, or use the hosted cloud with a free tier of 1,000 jobs per day.
Get started

Put one real workload on a durable queue.

Try hosted Spooled with 1,000 jobs a day, or run the Apache-2.0 core against your own PostgreSQL.

  • No credit card required
  • Free tier forever
  • Open source — Apache-2.0