graphile-worker: a PostgreSQL-backed job queue for Node.js applications
High performance Node.js/PostgreSQL job queue (also suitable for getting jobs generated by PostgreSQL triggers/functions out into a different work queue)
At a glance
- What is it?
- graphile-worker stores jobs in a PostgreSQL table, runs them from a Node.js worker, and exposes its queue as SQL. It fits teams already on Postgres and PostGraphile or PostgREST, and it is the wrong tool if you need a broker with its own persistence and delivery guarantees.
- Who is it for?
- Adopt graphile-worker if your application already runs on PostgreSQL and you want jobs to live in the same database as your data, with no extra broker to operate; the npm package graphile-worker and the dist/cli.js entrypoint are the two ways in. Do not adopt it if you need a queue that survives your database being unavailable, or if you are not running Postgres at all.
- 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 18 days 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 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What graphile-worker solves, and who it is for
The README frames the problem plainly: run jobs such as sending emails, performing calculations or generating PDFs "in the background" so that your HTTP response or application code is not held up. That is a familiar need, but the implementation choice is what separates this project from most Node.js queues. Jobs live in PostgreSQL, and the README states the queue can be used with any PostgreSQL-backed application. The stated pairing is with PostGraphile or PostgREST, which makes sense: those tools already treat Postgres as the application's centre, so adding a second piece of infrastructure just to hold pending work would be an odd split.
The audience follows from that. If your application has a Postgres connection string and you would rather not operate Redis, RabbitMQ or a hosted queue alongside it, the trade is attractive. The repository description adds a second use case that is easy to miss: getting jobs generated by PostgreSQL triggers or functions out into a different work queue. That means the queue table can be written to by database code, not only by Node.js, and a worker can then pick those rows up. For a team whose business logic sits in PL/pgSQL, that is a genuinely different starting point from a library where only application code can enqueue.
How the queue works: a table, a worker, and SQL you can read
The mechanism visible in the repository is straightforward. There is a sql/ directory at the top level, a src/ directory holding the TypeScript implementation, and a dist/cli.js entrypoint produced by the build. The package is published as graphile-worker with main and exports both pointing at dist/index.js, so the same code can be driven as a library or as a command line process. The Dockerfile confirms the shape of a deployment: it builds with node:18-alpine, runs yarn run prepack, copies sql/ and dist/ into a clean stage, installs production dependencies, and sets ENTRYPOINT to ./dist/cli.js. A container built from that file starts a worker, not a web server.
The consequence is that the queue is inspectable with ordinary SQL. You can query pending jobs, count failures, or delete a bad row without a separate administration UI, because the queue is rows in your database. The cost is that every enqueue and every job fetch is a database round trip, and the worker competes with your application for connections. That is a real constraint at high throughput, and it is the main reason to think carefully before choosing this design over an external broker.
The examples/ directory is worth reading before you commit, because it shows the boundary of the design rather than the happy path. It contains examples/worker-bullmq-exporter/, examples/worker-cloud-tasks-exporter/ and examples/worker-faktory-exporter/, each of which moves work from this queue into a different system. If you are evaluating graphile-worker, those directories tell you the maintainers expect some users to treat Postgres as the intake point and something else as the execution layer.
Installing graphile-worker and running a first job
The package is published on npm as graphile-worker, as the badge in the README indicates. Installation is a normal npm install; the README does not include a separate install section, so the package name and the Dockerfile are the reliable sources for how the software is obtained and started. The Dockerfile shows the CLI being invoked as ./dist/cli.js, which is what the npm bin entry resolves to after the build.
npm install graphile-workerRunning a worker requires a PostgreSQL database and connection details. The docker-compose.yml in the repository shows the environment variables the project itself uses for its development database: PGUSER, PGPASSWORD, PGHOST and PGPORT, with POSTGRES_DB set to graphile_worker_test. Those are the standard libpq variables, so a worker started with them set will connect without further configuration.
PGUSER=postgres PGPASSWORD=workerdev PGHOST=localhost PGPORT=5432 \
npx graphile-workerThe repository also ships a graphile.config.mts file at the top level, which is the configuration file format the current release expects. The README does not document the full set of keys, so treat that file as the reference for what a real configuration looks like rather than guessing at option names. For a first real use, the honest advice is to start with the CLI against a scratch database, confirm that the worker creates its schema in sql/, and only then wire job handlers in from application code.
The limitation that decides most evaluations
Everything about this project inherits PostgreSQL's availability characteristics. If the database is unreachable, jobs cannot be enqueued and cannot be run; there is no separate broker buffering work while the database is down. For applications where the database is already a hard dependency for every request, that changes nothing. For applications that use a queue precisely to decouple from a struggling database, it removes the decoupling.
A second consideration is connection pressure. Workers hold connections and poll for work, and the docker-compose.yml shows the project testing against postgres:12-alpine. The README does not state a minimum supported PostgreSQL version, so that is something to verify against the release notes for v0.18.0 rather than assume from the test image. The repository also carries a perfTest/ directory, which indicates performance work is part of the project, but the README does not publish throughput numbers and none should be assumed.
Finally, the project is crowd-funded. The README asks individuals and businesses that use it to support maintenance via sponsorship and links to a sponsorship page. That is not a defect, but it is relevant context when you are deciding how much operational weight to put on a dependency: the funding model is explicit and public, and you can read the sponsor list in SPONSORS.md.
How it compares to BullMQ and to Postgres-native alternatives
The most direct comparison in the repository's own material is BullMQ, and the project does not treat it as a rival to be dismissed. examples/worker-bullmq-exporter/ exists to move jobs from this queue into BullMQ. The difference in approach is where the queue lives. BullMQ keeps jobs in Redis, so enqueueing is a fast in-memory operation and the queue survives independently of your relational database. graphile-worker keeps jobs in PostgreSQL, so enqueueing is a transactional write that can be committed in the same transaction as your business data. If you need "create the order and queue the receipt email atomically", that property is the whole argument. If you need the queue to keep accepting work while Postgres is being failed over, BullMQ's model is the one that helps.
The other comparison worth naming is writing your own jobs table and a polling loop. That is what graphile-worker is, done carefully, with a CLI, a configuration file, a documented schema in sql/, and a test suite under __tests__. The examples/readme/ directory suggests the README examples are exercised as part of the project, which is a sign the documented API is kept honest. Whether that saves you enough work over a bespoke table depends on how much you value the surrounding tooling.
Licence, maintenance and upgrade cost
The licence is MIT, as stated in the package metadata and the LICENSE.md file, and the README carries an MIT license badge. MIT is permissive: you can use the library in commercial and closed-source applications. This is a description of the licence text, not legal advice; if your organisation has specific obligations around attribution, read LICENSE.md yourself.
On maintenance, the repository is not archived and the last push was on 2026-09-13, with v0.18.0 released on 2026-09-08. The project uses changesets: the .changeset/ directory and the changeset-version and changeset-publish scripts in package.json show that releases are cut through a versioning workflow rather than by hand. That means CHANGELOG.md is the place to look before upgrading, and it should be read for every minor bump, because a job queue touches schema.
The upgrade cost that matters most is the database schema. The sql/ directory is copied into the published Docker image alongside dist/, and the Dockerfile copies sql/ into the clean stage explicitly. Any change to that schema has to be applied to your database when you upgrade the worker, and a running worker from an older version may be reading the same tables. The README does not document a rollback procedure, so plan upgrades as forward-only and test them against a copy of your production database before touching the real one.
Editorial conclusion
Adopt graphile-worker if your application already runs on PostgreSQL and you want jobs to live in the same database as your data, with no extra broker to operate; the npm package graphile-worker and the dist/cli.js entrypoint are the two ways in. Do not adopt it if you need a queue that survives your database being unavailable, or if you are not running Postgres at all. Before committing, verify the minimum PostgreSQL version the current release supports, inspect the sql/ directory to see the schema the worker creates in your database, and confirm how you will run migrations for that schema during upgrades.
Frequently asked questions
What is graphile-worker and what is it for?
It is a job queue for PostgreSQL that runs on Node.js, used to run jobs such as sending emails, performing calculations or generating PDFs in the background so application code is not held up. The README states it can be used with any PostgreSQL-backed application.
How do I install graphile-worker?
It is published on npm as graphile-worker, so it installs with npm install graphile-worker. The repository also provides a Dockerfile whose ENTRYPOINT is ./dist/cli.js, which starts a worker rather than a web server.
Does graphile-worker need Redis or another broker?
No. Jobs are stored in PostgreSQL, and the README describes it as a job queue for PostgreSQL running on Node.js. The repository does include exporters such as examples/worker-bullmq-exporter/ for moving jobs into other systems, but those are optional examples, not requirements.
What licence does graphile-worker use?
MIT, according to the package metadata, LICENSE.md and the licence badge in the README. The README also asks users to support maintenance through sponsorship, and lists sponsors in SPONSORS.md.
Official sources
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.
[](https://hysenlabs.com/projects/graphile-worker)