PgQue is a zero-bloat message queue built entirely in SQL and PL/pgSQL on top of Postgres. It revives PgQ — the queue engine originally designed at Skype in 2006 to handle messaging for hundreds of millions of users — and repackages it as a single SQL file that installs on any Postgres 14+, including every major managed provider. No C extension, no sidecar daemon, noDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/NikolayS/PgQue/llms.txt
Use this file to discover all available pages before exploring further.
shared_preload_libraries entry, no provider approval required.
Zero bloat by design
TRUNCATE-based table rotation instead of per-row DELETE means no dead tuples on the hot path — ever. No VACUUM tuning, no autovacuum starvation.
No performance decay
Snapshot-based batching keeps throughput stable under sustained load. PgQue does not get slower after months in production.
Native fan-out
Each consumer holds an independent cursor on a shared event log. Every subscriber sees every event — no data duplication, no copy-per-subscriber inserts.
Pure SQL install
One file:
\i sql/pgque.sql. Works on RDS, Aurora, Cloud SQL, AlloyDB, Supabase, Neon, Crunchy Bridge, and any self-hosted Postgres 14+.Why SKIP LOCKED queues develop problems under load
Most Postgres queues useSKIP LOCKED with UPDATE and DELETE to claim and remove jobs. This works well in small-scale demos and then turns into dead tuples, VACUUM pressure, index bloat, and performance drift under sustained load. When autovacuum cannot keep up — because a long-running transaction is pinning the xmin horizon, OLAP queries are running alongside, or throughput simply outpaces cleanup — dead tuples accumulate and the queue slows down. This failure mode is well-documented:
- Brandur/Heroku (2015) — 60,000-row backlog within one hour.
- PlanetScale (2026) — death spiral at 800 jobs/sec with OLAP on the side.
- River issue #59 — autovacuum starvation.
The anti-extension design
PgQue calls itself an “anti-extension.” The standard install is a single SQL + PL/pgSQL file — no.control file, no C code, no compiled binary. Install it with \i sql/pgque.sql inside psql, or with psql --single-transaction -f sql/pgque.sql from the shell. It works anywhere you can run SQL — RDS, Aurora, Cloud SQL, AlloyDB, Supabase, Neon, and any other managed Postgres 14+.
An optional pg_tle path exists for teams that want
ALTER EXTENSION pgque UPDATE and DROP EXTENSION pgque CASCADE semantics. This is opt-in; see installation for details. pg_tle is itself a C extension that requires shared_preload_libraries — the dependency the default install avoids.Comparison
| Feature | PgQue | PgQ | PGMQ | River | Que | pg-boss |
|---|---|---|---|---|---|---|
| Snapshot-based batching (no row locks) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Zero bloat under sustained load | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| No external daemon or worker binary | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Pure SQL install, managed Postgres ready | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| Language-agnostic SQL API | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| Multiple independent consumers (fan-out) | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |
| Built-in retry with backoff | ✅ | ✅ | ⚠️ | ✅ | ✅ | ✅ |
| Built-in dead letter queue | ✅ | ❌ | ⚠️ | ⚠️ | ❌ | ✅ |
- PgQ is the Skype-era queue engine (~2007) PgQue is derived from. Same snapshot/rotation architecture, but requires C extensions and an external daemon (
pgqd) — unavailable on managed Postgres. PgQue removes both constraints. - No external daemon: PgQue uses pg_cron (or your own scheduler) for ticking; PGMQ uses visibility timeouts. River, Que, and pg-boss require a Go / Ruby / Node.js worker binary.
- Que uses advisory locks — no dead tuples from claiming, but completed jobs are still DELETEd. Ruby-only.
- PGMQ retry is visibility-timeout re-delivery (
read_cttracking) with no configurable backoff or max attempts. - pg-boss fan-out is copy-per-queue
publish()/subscribe(), not a shared event log with independent cursors. - Category: River, Que, pg-boss (and Oban, graphile-worker, solid_queue, good_job) are job queue frameworks. PgQue is an event/message queue optimized for high-throughput streaming with fan-out.
When to use PgQue vs a job queue
PgQue’s model is closer to Kafka (a shared event log with per-consumer cursors) than to ActiveMQ or RabbitMQ (task queues where each job goes to exactly one worker).- Choose PgQue when you want event-driven fan-out, zero bloat without tuning, and a language-agnostic SQL API, and you do not need per-job priorities or a worker framework.
- Choose a job queue (River, pg-boss, Oban, Que, graphile-worker) when you need per-job lifecycle tracking, sub-3 ms latency, priority queues, cron scheduling, unique jobs, or deep language-specific ecosystem integration.
Latency trade-off
PgQue trades per-message dispatch latency for zero bloat and stable throughput. Thesend and receive functions themselves execute in the microsecond range. End-to-end delivery — the gap between send committing and a consumer seeing the event — depends on the tick period. The default is 100 ms (10 ticks/sec), giving roughly 50 ms median end-to-end latency. The tick rate is tunable from 1 ms to 1,000 ms via pgque.set_tick_period_ms(ms).
If your top priority is single-digit-millisecond dispatch, PgQue is the wrong tool. If your priority is stability under load without bloat, that is where PgQue fits.
Project status
PgQue is early-stage as a product and API layer. The underlying PgQ engine has run at Skype scale for over a decade. What is new here is the packaging, Postgres 14+ modernization, managed-Postgres compatibility, and the modernsend/receive/ack/nack API built on top of that proven core. The default install stays small; additional APIs live under sql/experimental/ until they are ready to promote.
Install PgQue
Requirements, install command, ticker setup, role grants, and uninstall.
Quickstart
Hands-on walkthrough: create a queue, send a message, receive and ack a batch.
