Tidebase vs BullMQ
The short answer: BullMQ retries jobs; Tidebase resumes them. A BullMQ retry re-runs the whole job function from the top — anything it already did, it does again. A Tidebase re-invocation replays completed steps from Postgres and continues from the first incomplete one. If your jobs are short and idempotent, BullMQ is enough; if a job is twenty minutes of LLM calls and external writes, the difference is the product.
Tidebase is an open-source checkpoint layer for AI agents: wrap your steps, and failed runs resume from the last safe point — in your own Postgres, without moving execution into a new runtime.
The structural difference
BullMQ is a queue. Redis-backed, fast, mature: producers add jobs, workers process them, failures retry with backoff. The job function is opaque to BullMQ — it knows that the job failed, never where. State inside a job attempt lives and dies with the process.
Tidebase is a checkpoint layer with a queue attached. The unit of durability is the step, not the job. run.step('fetch-sources', ...) writes its result to Postgres; when the run is re-invoked — by Tidebase’s own queue, a recovery webhook, or your retry button — finished steps return their stored results and the twenty minutes of completed LLM work is never re-bought. External writes carry an explicit replay contract: a failed step with undeclared side effects parks for manual review instead of blindly re-firing.
Side by side
| Tidebase | BullMQ | |
|---|---|---|
| Backing store | Postgres | Redis |
| Failure granularity | Step — resume from last checkpoint | Job — retry re-runs everything |
| Double-fire protection | Input-hashed checkpoints, idempotency keys, manual_review classification | Your job code’s responsibility |
| Run record for product UIs | Live state, events, step results, SSE | Job status/progress fields |
| Human approval gates | Built in, durable, exactly-once | Not a queue concern |
| Per-run LLM cost ledger | Built in | Not a queue concern |
| Throughput as a queue | Modest (Postgres, agent-workflow-shaped) | Excellent (Redis, battle-tested at scale) |
| Delayed/repeatable jobs | Delays, cron schedules, dedupe | Mature support |
| Maturity | Self-hosted alpha, opt-in auth | Production standard for Node queues |
Choose BullMQ if
- Jobs are short, cheap to re-run, or naturally idempotent — thumbnails, emails, cache warming. A checkpoint layer adds nothing there.
- You need raw queue throughput and the Redis operational model you already have.
Choose Tidebase if
- A retried job re-doing its finished work is expensive (LLM tokens, rate-limited APIs) or dangerous (payments, sends, deploys).
- You want the run visible — progress, pending approvals, what each step returned, what it cost — as Postgres rows your product can query.
- You’d rather not hand-roll the
statuscolumn, the “did step 7 fire?” forensics, and the idempotency bookkeeping around a queue.
They also compose: plenty of stacks keep BullMQ for dispatch and wrap the job body in tide.run(...) for checkpoints — the queue decides when, Tidebase remembers what. The honest tradeoff either way: Tidebase never executes your code, so something — BullMQ, Tidebase’s own queues, or your infra — must invoke it.
Repo: https://github.com/BlueprintLabIO/tidebase · See also: Tidebase vs Inngest