Skip to content

faststream-outbox is a FastStream broker integration for the transactional outbox pattern, with a Postgres table as the message queue. A producer writes a domain entity and an outbox row in the same SQLAlchemy transaction; a subscriber polls the table with FOR UPDATE SKIP LOCKED, runs the handler, and deletes the row on success.

There are two ways to use it:

  • Standalone: the table is the queue. Subscribers process rows in place, with no separate message bus.
  • Relay: a subscriber forwards each row to Kafka, RabbitMQ, NATS or Redis with a stacked publisher decorator. See Relay to a foreign broker.

Use it when

  • You already have Postgres and don't want to add a message bus just to get at-least-once delivery alongside your domain writes.
  • You want the row insert to commit atomically with the rest of your SQLAlchemy transaction (no two-phase commit, no Sagas).
  • You're building on FastStream or FastAPI and want the same subscriber / dependency-injection ergonomics for an outbox.
  • You need a durable, transactionally-published feed of events that a separate worker relays to Kafka / RabbitMQ / NATS with the at-least-once contract preserved end-to-end.

Reach for something else when

  • You're already running Kafka / Rabbit / NATS and don't need transactional atomicity with a DB write: use that broker directly.
  • You need sub-second scheduled-delivery precision: see Timers § latency floor.
  • You're on a non-Postgres database: this package is Postgres-only at v0. CDC / Debezium may be a better fit (see Comparison).
  • You're modelling ad-hoc background jobs, not events tied to a DB transaction: see Comparison § vs Celery.

Start where you're going

If you want to… Start at
Install and write the first publisher / subscriber Installation, then Tutorial: Your first outbox app
See it work end-to-end on a FastAPI app FastAPI integration
Relay outbox rows to Kafka / RabbitMQ / NATS / Redis Relay to Kafka / RabbitMQ / NATS
Understand the architecture before adopting How it works
Compare against CDC / Kafka transactions / a hand-rolled outbox Comparison
Deploy to production safely Production checklist

The categorized index below (also the left-hand nav) lists every page with a one-line summary.

Documentation

Getting started

Concepts

  • How it works: two-loop subscriber, lease-token invariant, at-least-once semantics, opt-in DLQ on terminal failure.
  • Performance: round-trips and table churn, and the levers that address each.
  • Comparison: vs writing your own, vs CDC, vs Kafka transactions, vs LISTEN/NOTIFY, vs Celery, vs FastStream foreign-broker direct.
  • Instrumentation seams: the recorder seam vs native middleware, and why both exist. Read this first if you're deciding what to wire.

Guides

Reference

  • Subscriber: options, ack policies, retry strategies, connection budget, slow-handler queue segregation.
  • Publisher: publish, publish_batch, OutboxPublisher, chained publishing via OutboxResponse.
  • Router: OutboxRouter, OutboxRoute, walking every subscriber via broker.subscribers.
  • Dead-letter queue: opt-in audit table, atomicity via a single CTE, dlq_written metric, retention patterns.
  • Observability: the recorder-seam API, the full event/tag catalog, and the operator PromQL playbook.

Operations

  • Production checklist: connection budget, lease TTL sizing, and deploy-safety items before going live.
  • Troubleshooting: common symptoms (idle latency, lease_lost spikes, connection exhaustion) and fixes.
  • Alembic migrations: autogenerate the outbox table and its partial indexes.