Postgres-backed background job queue for Python — Oban for Pythonistas. Install: pip install pgroost; import: `import roost`.
Project description
Roost
Postgres-backed background job queue for Python — Oban for Pythonistas.
🚧 Alpha — under active development. APIs may change before
1.0. Pin exactly.
Why Roost?
- One less piece of infra. No Redis, no RabbitMQ. Your existing Postgres is the queue.
- Transactional enqueue.
INSERT INTO roost.jobs ...commits in your transaction. Jobs cannot orphan or vanish. - Battle-tested concurrency.
SELECT ... FOR UPDATE SKIP LOCKEDfor safe parallel workers. - Real-time wakeups.
LISTEN/NOTIFY— no polling overhead, sub-second pickup. - Sync and async, first-class. Twin facades over a single SQL surface — Django/Flask + FastAPI/Starlette without a glue layer.
- Crash-tolerant. Heartbeats + orphan reaper recover jobs from SIGKILL'd workers automatically.
- Polished dashboard.
roost-webmounts in three lines, live updates via SSE, no Node.js toolchain.
Real numbers (from a laptop)
Bulk enqueue: 15,289 jobs/sec single async connection
Sustained drain: 822 jobs/sec 16 workers, local PG, noop handler
Dispatch overhead: p50 3 ms p99 51 ms — pure Roost overhead
per job (excludes handler runtime)
Run bench/throughput.py against your own Postgres to see what you get.
Quickstart
pip install pgroost # install
export ROOST_DSN=postgresql://user:pass@localhost/app
roost init --apply # CLI command stays `roost`
Distribution vs import name: the PyPI distribution is
pgroost(the bareroostname is taken on PyPI). You stillimport roostand call theroostCLI — only the install line differs.
# tasks.py
from roost import job
@job("send_welcome_email", queue="emails", max_attempts=5, timeout_seconds=30)
async def send_welcome_email(user_id: int) -> None:
...
# inside a FastAPI / Starlette / Django handler — same conn, same txn
async with pool.acquire() as conn, conn.transaction():
user_id = await conn.fetchval("INSERT INTO users ... RETURNING id")
await roost.enqueue(send_welcome_email, args={"user_id": user_id}, conn=conn)
# both rows commit together — or roll back together. That's the whole point.
roost run --module tasks --queues emails,default --concurrency 8
roost doctor # health check
roost status # counts per state per queue
Read the docs and the recipes for more.
Feature matrix
| Feature | Status |
|---|---|
| Transactional enqueue | shipped |
| Async + sync facades | shipped |
Bulk enqueue (enqueue_many) |
shipped |
| Worker, retries (3 strategies) | shipped |
| Snoozing | shipped |
| Per-task timeouts | shipped |
| Cron with IANA timezones | shipped |
| Unique jobs (partial idx) | shipped |
Job chaining (depends_on) |
shipped |
| Per-task rate limit | shipped |
| Per-task max concurrency | shipped |
| Pydantic-typed args | shipped |
| Cancel propagation via NOTIFY | shipped |
Result storage + wait_for |
shipped |
| Worker heartbeats + orphan reaper | shipped |
| Auto-archive + result TTL | shipped |
| OpenTelemetry hooks (extra) | shipped |
| Prometheus metrics (extra) | shipped |
Event hooks (Hooks(before, after)) |
shipped |
| FastAPI / Django / Flask contrib | shipped |
roost.testing helpers |
shipped |
Schema migrations + roost migrate |
shipped |
| Typer CLI (init/run/doctor/...) | shipped |
| Drop-in dashboard | roost-web |
Compatibility
- Python: 3.10, 3.11, 3.12, 3.13.
- PostgreSQL: 13, 14, 15, 16. Tested every commit on the full matrix.
- Drivers:
asyncpg(async),psycopg[binary](sync). - Hosts: any ASGI app — FastAPI, Starlette, Litestar, Quart. Django and Flask via the contrib helpers.
Test suite
- 163 tests, 86% coverage, real Postgres via
testcontainers(no mocks of the DB layer). - Run locally:
uv run pytest -q. With coverage:uv run --with pytest-cov pytest --cov=src/roost --cov-report=term. - Override the test image:
ROOST_TEST_PG_IMAGE=postgres:15-alpine uv run pytest -q.
| Module | Coverage |
|---|---|
roost.sync_api |
100% |
roost._core.retry |
100% |
roost.exceptions |
100% |
roost.contrib.flask |
100% |
roost.testing |
98% |
roost.contrib.django |
96% |
roost._core.wait |
95% |
roost.hooks |
92% |
roost._core.migrations |
91% |
roost._core.repo |
88% |
roost.observability |
85% |
roost._core.doctor |
84% |
roost.async_api |
83% |
roost.cli, worker.py |
82% |
roost.decorators |
81% |
roost._core.cron |
80% |
Compared to other queues
See the comparison page for a side-by-side feature matrix vs Celery, RQ, dramatiq, arq, procrastinate, and pgqueuer.
Quick read: pick Roost if you already run Postgres and want transactional enqueue + a polished dashboard. Pick Celery / RQ if you don't run Postgres and have Redis already. Pick procrastinate / pgqueuer if you want the same Postgres-only foundation with a smaller feature surface.
Project structure
| Concern | File |
|---|---|
| Schema + numbered migrations | src/roost/_core/migrations.py |
| All DB I/O (single source of truth) | src/roost/_core/repo.py |
| Worker loop, signals, heartbeats | src/roost/worker.py |
| Backoff strategies | src/roost/_core/retry.py |
| Cron scheduler | src/roost/_core/cron.py |
| Public async API | src/roost/async_api.py |
| Public sync API | src/roost/sync_api.py |
@job / @cron decorators |
src/roost/decorators.py |
| Hooks, observability | src/roost/hooks.py, src/roost/observability.py |
| Health-check primitives | src/roost/_core/doctor.py |
| Test helpers | src/roost/testing.py |
roost.contrib.{fastapi,django,flask} |
src/roost/contrib/ |
| CLI | src/roost/cli.py |
Examples
The examples/ directory has six runnable patterns:
docker/— one-command stack (Postgres + worker + dashboard).fastapi_app/— transactional enqueue inside a FastAPI request,wait_forfor sync results.scheduled_reports/— daily cron with timezone, weekly UTC rollup.fanout_join/— N parallel children + a single aggregator gated ondepends_on.etl_pipeline/— chained extract → transform → load with rate limits and concurrency caps.plain_python.py— smallest possible enqueue + worker.
Each has its own README; start there.
Contributing
Read CONTRIBUTING.md. Bugs + feature requests via GitHub issues. Security disclosures: see SECURITY.md. All participants are expected to follow the Code of Conduct.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pgroost-0.1.1.tar.gz.
File metadata
- Download URL: pgroost-0.1.1.tar.gz
- Upload date:
- Size: 48.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7761a6216ccc9e649975969c2ce9b494ceaaab4f99b88e4b469773fafa895bc9
|
|
| MD5 |
4cc74353cbcb75b55c8d69cab87d1195
|
|
| BLAKE2b-256 |
b7015b4f1a5a62f37a61e131247d97afca2f06d2486899504e5c03a2946f16ef
|
Provenance
The following attestation bundles were made for pgroost-0.1.1.tar.gz:
Publisher:
release.yml on ashhadahsan/roost
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pgroost-0.1.1.tar.gz -
Subject digest:
7761a6216ccc9e649975969c2ce9b494ceaaab4f99b88e4b469773fafa895bc9 - Sigstore transparency entry: 1474089789
- Sigstore integration time:
-
Permalink:
ashhadahsan/roost@0cbd4733a0757bea4ab3b3118e272b0626dc656a -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/ashhadahsan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0cbd4733a0757bea4ab3b3118e272b0626dc656a -
Trigger Event:
push
-
Statement type:
File details
Details for the file pgroost-0.1.1-py3-none-any.whl.
File metadata
- Download URL: pgroost-0.1.1-py3-none-any.whl
- Upload date:
- Size: 60.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3170c2147f357d543b666a602cc779cde03e5c809e75e7a9759fea2a47f8f7d
|
|
| MD5 |
2033374e8950d885df6c89c68ece682b
|
|
| BLAKE2b-256 |
68f9108f193d83a53cc216991f02f162824860b7206f4f2bbb9f3804bac279a4
|
Provenance
The following attestation bundles were made for pgroost-0.1.1-py3-none-any.whl:
Publisher:
release.yml on ashhadahsan/roost
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pgroost-0.1.1-py3-none-any.whl -
Subject digest:
e3170c2147f357d543b666a602cc779cde03e5c809e75e7a9759fea2a47f8f7d - Sigstore transparency entry: 1474089829
- Sigstore integration time:
-
Permalink:
ashhadahsan/roost@0cbd4733a0757bea4ab3b3118e272b0626dc656a -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/ashhadahsan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0cbd4733a0757bea4ab3b3118e272b0626dc656a -
Trigger Event:
push
-
Statement type: