Open-source project
timgit/pg-boss avatar
timgit/pg-boss

pg-boss: a Postgres-backed job queue for Node.js services

Queueing jobs in Postgres from Node.js like a boss

4,077 stars281 forksTypeScriptMIT

At a glance

What is it?
pg-boss turns PostgreSQL into a background job queue using SKIP LOCKED instead of shipping a separate broker. Worth understanding for services that already run Postgres and dislike running one more stateful system.
Who is it for?
pg-boss is a good fit when the service already depends on Postgres and the queue is a means to an end: transactional sends, retries, cron, a handful of workers. It is a poor fit when throughput per queue dwarfs what a single database can index, or when you need fan-out across regions.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • timgit/pg-boss4,077 ★ · +52
Star history
Repository2026-10-082026-10-22
timgit/pg-boss4k4.1k

52 Stars in the last 14 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

SKIP LOCKED instead of a broker of its own

pg-boss does not run a message broker. It builds its queues out of PostgreSQL tables and leans on `SKIP LOCKED`, the row-level locking clause the README credits for the whole design. A worker claims a job inside a transaction and skips rows another worker is already holding, so several pollers can drain one queue without blocking each other. From that single choice the README derives exactly-once delivery and atomic commits, which is a stronger guarantee than most broker-backed queues offer by default.

The practical payoff is operational. There is no second stateful service to install, patch, monitor or restore, and the queue tables sit in the same backup and failover story as everything else. The cost is that queue traffic now competes for connections and IO on the same database as your application queries, which is exactly what the README means when it says the project will appeal most to teams already comfortable with relational semantics and already paying for Postgres.

Creating a queue and sending the first job

The README example is the whole getting-started path: construct a `PgBoss` with a connection string, start it, create a named queue, send a payload, and register a worker.

js
async function readme() {
  const { PgBoss } = require('pg-boss');
  const boss = new PgBoss('postgres://user:pass@host/database');

  boss.on('error', console.error)

  await boss.start()

  const queue = 'readme-queue'

  await boss.createQueue(queue)

  const id = await boss.send(queue, { arg1: 'read me' })

The worker is registered separately and receives jobs as an array, so a batch size is a property of the handler rather than something hidden in the client:

js
  await boss.work(queue, async ([ job ]) => {
    console.log(`received job ${job.id} with data ${JSON.stringify(job.data)}`)
  })

The runtime floor is stated plainly: Node 22.12 or higher, or Bun, against PostgreSQL 13 or higher. That is a real constraint for teams still on Node 18 or 20, and it is recent enough that an older application may need a plan before it can adopt pg-boss at all.

Scheduling, retries and queue storage policies

The feature list is longer than a bare queue would suggest, and the interesting entries are the ones about controlling rate. Cron and RRULE scheduling cover recurring work, job deferral covers one-off delays, and queue storage policies are what let a queue debounce, rate limit or cap concurrency. Those three are the features that normally push a team toward a purpose-built broker, and having them as table-driven configuration rather than broker config is the argument pg-boss makes.

Retries use exponential backoff, and failed jobs can land in a dead letter queue with redrive. Priority is a queue-level setting, so a queue of cleanup jobs and a queue of customer-facing notifications keep their own ordering and throughput characteristics without sharing one global limit.

The releases show this part still moving. Version 12.33.6, published on 2026-09-22, carries schema version 42 and two fixes that are worth reading as design notes: one for an application that registers its own global `pg-types` parser and breaks `timestamptz` handling, and one for a cron pass whose database clock read exactly on a minute boundary and sent that occurrence thirty seconds late. Both are small, which is a good sign for a project whose core is a set of carefully tuned SQL statements.

Joining an existing transaction and adapting to your ORM

Sending a job inside the transaction that changes your data is the feature pg-boss argues hardest for, and it is the one Redis-backed queues cannot match without distributed coordination. If the order insert rolls back, the enqueue rolls back with it, and there is no second system left holding an event for work that never happened.

The README lists adapters for Drizzle, Knex, Kysely and Prisma so the job insert can reuse the connection your ORM already has rather than opening a second pool. `package.json` confirms those four are exercised in the project's own test setup, alongside PGlite for in-process runs and `postgres` as an alternative driver.

Schema migrations do not need application code. The package ships a `pg-boss` binary for managing them, which the README positions for CI pipelines and manual setup scripts. A companion dashboard package exists for inspecting queues, jobs and schedules, and an HTTP proxy package covers platforms where a long-lived database connection is awkward. Both are separate npm packages rather than bundled modules, so the core install stays small.

Running against Redis-style alternatives

BullMQ and Bee-Queue are the usual comparison, and the difference is architectural rather than feature-by-feature. Redis stores queue state in memory with a persistence story bolted on, so workers get very low read latency and the queue survives database problems. A Postgres queue pays for every claim through the same buffer pool and indexes as your application, and inherits the database's durability and backup behaviour without extra configuration.

pg-boss also advertises multi-master operation, which is what makes it workable in a Kubernetes ReplicaSet where several pods poll the same database, and a pub/sub API for fan-out queue relationships, where one job fans out to many consumers. LISTEN/NOTIFY delivery is listed for lower latency than pure polling, and SQL support for non-Node runtimes means a Python or Go service can enqueue into the same queues. The README further points at Postgres-compatible backends such as CockroachDB, YugabyteDB and Citus, plus embedded PGlite, and the repository carries a separate `docker-compose` file for each of them.

If your workload is heavy fan-out, millions of tiny jobs per minute, or queues that must survive the database being down, that is where a dedicated broker still wins.

Limits worth knowing before you adopt it

The first limit is connections. `docker-compose.yaml` raises `max_connections` to 400 for the project's own test database, which tells you how many pollers the design expects. Each worker holds a connection, and an application sharing the pool with pg-boss can register global type parsers, which is precisely what the 12.33.6 release fixed. Connection budgeting is the thing to get right on day one rather than discover during an incident.

The second is that the README is a feature list, not an operations manual. Capacity guidance, vacuum behaviour under heavy churn, and the interaction between queue storage policies and Postgres autovacuum are all things the project documents on pgboss.io rather than in the repository front page. If your queue is on the same database as a latency-sensitive application, that documentation is the deciding input.

The third is bus factor. The README states that pg-boss is MIT licensed, and that it is maintained by one person, with sponsorship funding maintenance, security response and support. Nineteen open issues against a mature version line is a healthy number, but a single maintainer is a fact about long-term support rather than a defect. Adopting it means accepting that the bus factor is one, and that the SQL surface underneath is worth understanding well enough to maintain yourself.

Editorial conclusion

pg-boss is a good fit when the service already depends on Postgres and the queue is a means to an end: transactional sends, retries, cron, a handful of workers. It is a poor fit when throughput per queue dwarfs what a single database can index, or when you need fan-out across regions. Concretely, read the queue storage policies page, create a queue, and check the SKIP LOCKED behaviour on your own Postgres version, because that is the one assumption everything else rests on. The project is MIT licensed, its last push was on 2026-09-22, and the README states plainly that one maintainer handles it.

Frequently asked questions

What is pg-boss?

pg-boss is a job queue library for Node.js that stores its queues in PostgreSQL tables and uses the SKIP LOCKED clause so workers can claim jobs without blocking each other. It provides background processing with exactly-once delivery and no separate broker to run, on Node 22.12 or higher with PostgreSQL 13 or newer.

Is pg-boss good?

It is well suited to teams that already run Postgres and want background jobs in the same database, with transactional enqueue, cron and RRULE scheduling, retries and dead letter queues. It is less suitable when you need very high throughput, cross-region fan-out, or queue availability independent of your database. The README notes it is maintained by one person.

Can PostgreSQL be used as a job queue?

Yes, and that is exactly what pg-boss does, using SKIP LOCKED for concurrent claims. The trade is that queue work shares connections, IO and vacuum pressure with your application queries, so connection counts and storage policies matter. Dedicated brokers still win when throughput or isolation from database outages is the priority.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. timgit/pg-boss on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/timgit-pg-boss.svg)](https://hysenlabs.com/projects/timgit-pg-boss)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/timgit-pg-boss.svg)](https://hysenlabs.com/projects/timgit-pg-boss)