Skip to content

faststream-outbox is a FastStream broker integration for the transactional outbox pattern — a Postgres table is 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. The table is the queue — no separate message bus, no relay process, no Kafka.

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 rather than 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 InstallationTutorial: 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.
  • Comparison — vs writing your own, vs CDC, vs Kafka transactions, vs LISTEN/NOTIFY, vs Celery, vs FastStream foreign-broker direct.
  • Instrumentation seamsconcept: 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.
  • Publisherpublish, publish_batch, OutboxPublisher, chained publishing via OutboxResponse.
  • RouterOutboxRouter, OutboxRoute, walking every subscriber via broker.subscribers.
  • Dead-letter queue — opt-in audit table, atomicity via a single CTE, dlq_written metric, retention patterns.
  • Observabilityreference: 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.