# Absurd: a durable execution engine that lives entirely in Postgres

> Absurd is a Python-led durable workflow system whose whole runtime is a single SQL file applied to your own database. It suits teams that already run Postgres and want checkpointed tasks without a separate orchestrator.

**earendil-works/absurd** — An experiment in durability

- Repository: https://github.com/earendil-works/absurd
- Website: https://earendil-works.github.io/absurd
- Stars: 2,446 · Forks: 115
- Language: Python
- License: Apache-2.0
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/earendil-works-absurd

## What Absurd solves, and who it is actually for

Long-running work that crosses process boundaries is the problem here. A payment that must be captured, an inventory reservation that must be released, a notification that should fire only after a warehouse confirms a shipment: each of these can fail halfway, and each failure leaves you deciding what already happened. The usual answer is ad-hoc retry logic plus database checkpoints scattered through application code. Absurd's answer is to make the checkpoint the unit of programming.

The README frames the project as "the simplest durable execution workflow system you can think of" and states it is "entirely based on Postgres and nothing else." That constraint is the whole pitch. Durable execution is described there as the combination of a queue system and a state store that remembers the most recently seen state. Absurd puts both inside the database you already operate.

The audience follows from that: teams that already run Postgres and would rather apply one more schema than operate another service. The README lists LLM-based agents, payments, email scheduling and order processing as the kinds of work that span minutes, days or years. If your tasks finish in milliseconds and never need to survive a restart, this is more machinery than the problem deserves.

## Tasks, steps and checkpoints: the mechanism in the database

The high-level model is small. A task dispatches onto a queue, and a worker picks it up from there. Tasks are subdivided into steps, executed in sequence by the worker. When a task fails or is suspended, it executes again as a run. The result of each step is written to the database as a checkpoint, and on the next run checkpoints are loaded from Postgres so completed work is not repeated.

The consequence is stated plainly in the TypeScript example: code that runs outside of steps will potentially be executed multiple times. Only step bodies are protected. That is the single most important thing to internalise before writing any handler, because it changes where you put side effects. If you generate an idempotency key, the README's example derives it from ctx.taskID rather than from a random value, so a replay produces the same key.

Suspension is the other half. Tasks can sleep or suspend for events, and events are cached with first emit wins, which the README calls race-free. A task can therefore wait indefinitely for something like a shipment event and resume when it arrives, without a polling loop in your code.

Architecturally, the interesting decision is where the complexity sits. The README says Absurd's goal is to move the complexity of SDKs into the underlying stored functions, leaving each SDK to wrap the low-level operations in whatever is idiomatic for its language. That is why the schema file matters more than any client library here: the semantics live in SQL.

## Installing the schema and running a first task

Absurd needs one .sql file, absurd.sql, applied to a database of your choice. The README suggests plugging it into your favourite migration system, and notes that migrations are released under sql/migrations when the file changes. The absurdctl tool wraps the schema operations.

These commands initialise a database, check which schema version it reports, migrate it, and create a queue named default. The README shows them run through uvx for one-off use, and notes you can instead install persistently with uv tool install absurdctl or download the binary from GitHub Releases.

```bash
uvx absurdctl init -d database-name
uvx absurdctl schema-version -d database-name
uvx absurdctl migrate -d database-name
uvx absurdctl create-queue -d database-name default
```

With the schema in place, install the SDK for your language. The README gives npm install absurd-sdk for TypeScript and JavaScript, uv add absurd-sdk for Python, and go get github.com/earendil-works/absurd/sdks/go/absurd@latest for Go, which it labels an experimental bootstrap.

```bash
npm install absurd-sdk
uv add absurd-sdk
go get github.com/earendil-works/absurd/sdks/go/absurd@latest
```

The README's own example registers a task named order-fulfillment with two checkpointed steps and then suspends on an event. The shape is what matters: work inside ctx.step is retained once it succeeds, and ctx.awaitEvent blocks until the event is emitted, at which point the run resumes.

```typescript
import { Absurd } from 'absurd-sdk';

const app = new Absurd();

app.registerTask({ name: 'order-fulfillment' }, async (params, ctx) => {
  const payment = await ctx.step('process-payment', async () => {
    return { paymentId: `pay-${params.orderId}`, amount: params.amount };
  });

  const shipment = await ctx.awaitEvent(`shipment.packed:${params.orderId}`);

  return { orderId: params.orderId, payment, trackingNumber: shipment.trackingNumber };
});
```

For inspecting what is running, the repository ships habitat, a Go application that serves a web UI showing the current state of running and executed tasks. The README does not document habitat's configuration or port, so check the habitat directory before relying on it in an environment with fixed port allocations.

## Pull-only delivery and the coordinator you have to write yourself

Absurd is pull-based. Workers pull tasks from Postgres as they have capacity. The README states it does not support push at all, and explains why: push would require a coordinator process calling HTTP endpoints. The stated trade-off is that push systems need greater care around system load constraints.

That is a defensible position and also a real boundary. If your architecture expects the workflow engine to call a webhook when a task becomes runnable, Absurd will not do it. The README's suggested workaround is to write a simple service that consumes messages and makes HTTP requests. That service is yours to operate, monitor and scale, which quietly reintroduces the component Absurd was meant to remove.

The second limitation is more subtle and lives in the programming model rather than the deployment. Because only step results are checkpointed, any code between steps runs again on replay. A handler that mutates external state before its first step, or between two steps, will do so more than once. The README is explicit about this, but it is easy to skim past when the example is short. Treat the boundary of each step as the boundary of your idempotency.

SDK maturity is uneven. TypeScript and Python are listed as existing SDKs, while the Go one is described as an experimental bootstrap. If your services are written in Go, you are adopting the least settled client in the repository.

## How Absurd differs from Temporal, DBOS and the rest

The repository links to a comparison page covering PGMQ, Cadence, Temporal, Inngest and DBOS, so the project is willing to be measured against them. The difference that can be established from the README alone is operational: Absurd adds no service beyond Postgres.

Temporal and Cadence are the reference points most engineers will reach for. They run dedicated server components and separate persistence, which buys language support, visibility tooling and a mature execution model, at the cost of operating that cluster. Absurd inverts the trade: no cluster, but the semantics are stored functions in your database, and your database's availability is now your workflow engine's availability.

DBOS is the closer comparison, since it also leans on a relational database for durable state. The distinction Absurd draws is packaging: one .sql file and a small CLI rather than a framework with its own runtime assumptions. PGMQ is a Postgres-based queue rather than a durable execution engine, so it covers dispatch without the step checkpointing and event suspension that Absurd adds on top.

The honest summary is that Absurd competes on operational surface area, not on features. If you need a workflow engine with a hosted UI, multi-region workers and a decade of production war stories, the larger systems are the safer pick. If you need checkpointed steps and you already have Postgres with a migration pipeline, the calculus changes.

## Maintenance, upgrades and the Apache-2.0 licence

The repository is not archived, and the last push was on 2026-08-10, roughly seven weeks before this writing. Releases have been reasonably paced: 0.5.0 on 2026-08-04, 0.4.0 on 2026-05-27 and 0.3.0 on 2026-04-02. That is a project still moving, and the version numbers say it has not reached 1.0.

Upgrade cost is where the design pays off or bites, depending on how you applied the schema. Because the engine is a SQL file, upgrading means applying a migration from sql/migrations rather than redeploying a service. absurdctl exposes migrate and schema-version for this, and the README says migrations are released when absurd.sql changes. The practical risk is drift: if you applied absurd.sql by hand and your migration tool does not track it, schema-version is your only way to know what you are running.

The licence is Apache-2.0, which is permissive and includes an express grant of patent rights from contributors, with the usual notice and attribution conditions on redistribution. That is a summary of the licence's character, not legal advice; read the LICENSE file in the repository before you ship it inside a product.

One more cost that is easy to miss: the Makefile shows the project's own test surface spans core tests, TypeScript, Python and Go SDKs, plus a Zensical-pinned documentation build. If you fork or patch the stored functions, you are maintaining against all four.

## Conclusion

Absurd fits teams that already run Postgres and want checkpointed, retryable tasks without standing up a coordinator service, and who are comfortable applying absurd.sql through their own migration system. It is a poor fit if you need push delivery to HTTP endpoints, since the README states push is not supported at all, or if you require a mature multi-language SDK story rather than an experimental bootstrap. Before adopting, verify the schema version your database reports after running uvx absurdctl schema-version -d database-name, and confirm in sql/migrations that an upgrade path exists from the version you applied.

## FAQ

### Does Absurd need any service besides Postgres to run?

No. The README states it is entirely based on Postgres and nothing else, and that it needs a single absurd.sql file applied to a database of your choice. The optional habitat web UI is a separate Go application, not a required component.

### Which languages have an Absurd SDK?

The README lists TypeScript (and JavaScript), Python, and Go, with the Go SDK marked as an experimental bootstrap. Install commands given are npm install absurd-sdk, uv add absurd-sdk, and go get github.com/earendil-works/absurd/sdks/go/absurd@latest.

### Will code outside a step run more than once in Absurd?

Yes. The README's TypeScript example states that code running outside of steps will potentially be executed multiple times, because only step results are checkpointed and reloaded from Postgres on a new run.

### Can Absurd push tasks to my HTTP endpoint?

No. The README says Absurd is pull-based and does not support push at all, since push would require a coordinator calling HTTP endpoints. It suggests writing a small service that consumes messages and makes the requests yourself.

## Sources

- [earendil-works/absurd on GitHub](https://github.com/earendil-works/absurd)
- [License: Apache-2.0](https://github.com/earendil-works/absurd/blob/main/LICENSE)
- [Project website](https://earendil-works.github.io/absurd)
- [README](https://github.com/earendil-works/absurd/blob/main/README.md)
- [Releases](https://github.com/earendil-works/absurd/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/earendil-works-absurd
