Agenda: a MongoDB-backed job scheduler for Node.js
Lightweight job scheduling for Node.js
At a glance
- What is it?
- Agenda 6 is an ESM-only TypeScript rewrite of the long-running Node.js scheduler, now with a pluggable backend interface and optional real-time notifications. It fits teams that already run MongoDB and want cron-style recurring jobs without adding Redis.
- Who is it for?
- Adopt Agenda when you already operate MongoDB or PostgreSQL and want cron-style recurring jobs, human-readable intervals and a small API surface inside a Node.js process. Skip it if you need rate limiting, dead letter queues or job dependencies, which the README's own comparison table does not claim.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 71 days ago.
- What is it written in?
- Mainly HTML, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap Agenda fills between setInterval and a full queue
Node.js has no built-in scheduler that survives a restart. A setInterval callback dies with the process, and a cron entry on the host has no access to your application objects or your database connection. Agenda sits in that gap: it stores job definitions and their schedules in a database, so a job registered by one process can be picked up by another, and a schedule persists across deploys.
The audience is narrow and specific. You are running Node.js, you already have MongoDB (version 4 or newer according to the README), and you want recurring work such as deleting stale records or sending a digest on a cron expression. The README's own example is exactly that: a job named 'delete old users' registered with agenda.every('3 minutes', 'delete old users'). If your workload is a high-throughput message stream rather than a set of named jobs, the project's comparison table points elsewhere, since Agenda lists itself as optimized for jobs, not messages.
One detail matters for anyone evaluating it today. Version 6 is ESM-only and requires Node.js 18 or newer, so a CommonJS codebase cannot import it without a migration. The repository keeps a docs/migration-guide-v6.md file specifically for that jump, which tells you the maintainers expected the break to be disruptive.
How the backend interface and job lifecycle actually work
The central design change in version 6 is that storage is no longer hardcoded to MongoDB. An AgendaBackend interface handles both persistence and notifications, and the constructor takes a backend instance rather than a connection string. The README shows a MongoBackend constructed with an address, and notes that you can pass an existing MongoDB Db instance instead, or override the default collection name.
That split matters because the notification channel is what makes processing near-instant. Without one, the scheduler polls the database on an interval. With one, the README lists Redis, PostgreSQL LISTEN/NOTIFY, MongoDB Change Streams and custom pub/sub as options, so a worker can be woken when a job is inserted rather than discovering it on the next tick. The trade-off is that you now choose and operate two things, a database and a notification path, and the notification path is described as optional, which means the polling fallback is what you get by default.
Around that core, the API is promise-based. Agenda defines jobs by name, starts with agenda.start(), and registers repeats with agenda.every(). The README also documents touch() with an optional progress parameter from 0 to 100, getRunningStats() for monitoring, fork mode for sandboxed execution, and optional persistent job logging of lifecycle events to the database. Persistent logging is opt-in, which is the right default for a library that advertises minimal overhead, but it also means a fresh install has no historical record of job runs unless you turn it on.
Installing Agenda and scheduling a first recurring job
The core package and the storage backend are installed separately. The README lists npm install agenda for the scheduler, then a second command for whichever backend you use. For MongoDB that is @agendajs/mongo-backend, and the README also names @agendajs/postgres-backend and @agendajs/redis-backend as official alternatives.
npm install agenda
npm install @agendajs/mongo-backendAfter both are present, the README's example builds an Agenda instance around a MongoBackend pointed at a local MongoDB. The commented lines in the same example show the two variations the API accepts: a custom collection name, or an existing Db instance you already opened elsewhere.
import { Agenda } from 'agenda';
import { MongoBackend } from '@agendajs/mongo-backend';
const mongoConnectionString = 'mongodb://127.0.0.1/agenda';
const agenda = new Agenda({
backend: new MongoBackend({ address: mongoConnectionString })
});The job itself is defined by name with an async function, then started and scheduled inside an async IIFE so that await is available at the top level of the script. The README gives both a human-readable interval and the equivalent cron expression for the same schedule, which is a useful way to confirm the two forms agree.
agenda.define('delete old users', async job => {
await User.remove({ lastLogIn: { $lt: twoDaysAgo } });
});
(async function () {
await agenda.start();
await agenda.every('3 minutes', 'delete old users');
})();Run that file with Node 18 or newer. On success the process stays alive and the job fires every three minutes; the README notes that indexes are created automatically by default, so the first run is also when the collection and its indexes appear. The repository's examples/ directory contains runnable variants for each backend (basic-mongodb.ts, basic-postgres.ts, basic-redis.ts) plus focused files for concurrency, debouncing, priorities, unique jobs, backoff and retry, event handling and graceful shutdown, which are worth reading before you write your own worker.
Where Agenda is the wrong choice
The README's feature comparison table is unusually honest, and it is the fastest way to rule Agenda out. Rate limiter, dead letter queues and job dependencies are all marked as absent for Agenda while present for BullMQ and pg-boss. If your design depends on a failed job landing in a dead letter queue for later inspection, or on one job triggering only after a set of predecessors completes, Agenda does not provide that and you would be building it yourself on top of the event API.
Atomic operations are marked with a tilde rather than a checkmark, which signals partial support rather than a guarantee. Combined with a database-backed queue, that is a reason to write job handlers that are safe to run more than once. The README does not document an exactly-once mode, so idempotency is your responsibility, not the library's.
The comparison table also lists Bull and Bee as in maintenance or stale, so the honest framing is not that Agenda beats every alternative, it is that Agenda occupies a specific position: database-backed rather than Redis-backed, with a small codebase and a REST API and web UI shipped as separate packages in the same monorepo. If you want the Redis ecosystem and its rate limiting, Agenda is the wrong tool and the README says so indirectly through that table.
Agenda compared with BullMQ and pg-boss
The difference is where the queue lives and what that buys you. BullMQ and Bull are Redis-backed. pg-boss is PostgreSQL-backed. Agenda defaults to MongoDB but, since version 6, reaches PostgreSQL and Redis through the same backend interface, so the storage argument is no longer a clean separation. What remains different is the shape of the API and the intended workload.
Agenda's model is named jobs with schedules. You define a job once, register a repeat, and the scheduler decides when to enqueue it. The README emphasizes human-readable intervals alongside cron, which BullMQ and pg-boss do not list in the comparison table. It also lists support for long-running jobs, which message-oriented queues tend to discourage because a long-held message blocks throughput. If your work is a nightly report that takes twenty minutes, that row is the one that matters.
pg-boss is the closest structural alternative for teams already on PostgreSQL, and the comparison table shows it matching Agenda on priorities, concurrency, delayed jobs, repeatable jobs, auto-retry with backoff, persistence and real-time notifications. It diverges on global events, pause and resume, sandboxed workers, a UI and a REST API, none of which pg-boss lists. BullMQ goes the other way, adding rate limiting, dead letter queues and job dependencies that Agenda lacks. Pick by which column of that table you cannot live without.
Maintenance status, licence and the upgrade cost of version 6
The repository is not archived, and the last push was on 2026-07-21, so it is current rather than abandoned. The same date carries the three releases listed: [email protected], [email protected] and @agendajs/[email protected], all published within minutes of each other. That pattern indicates the monorepo publishes its packages together, which is good for version alignment and awkward if you want to upgrade only one of them.
The monorepo layout is visible in the top-level entries: packages/ holds the published modules, docs/ holds current documentation, and docs-legacy/ is kept alongside it. The project also maintains a changesets directory and a pnpm workspace, so releases are versioned through @changesets/cli rather than hand-edited. For an operator, the practical consequence is that a minor bump may touch the scheduler, the UI and a backend at once, and the migration guide file exists precisely because the v5 to v6 jump was breaking: ESM-only, a new backend constructor, and the MongoDB 6 driver.
The licence field is reported as NOASSERTION, which means the repository metadata does not map to a recognised SPDX identifier. A LICENSE.md file is present at the top level, so the terms are written down, but the automated classification could not determine them. Read that file before you ship, particularly if you are embedding Agenda in a product; this is a description of the metadata, not legal advice.
Editorial conclusion
Adopt Agenda when you already operate MongoDB or PostgreSQL and want cron-style recurring jobs, human-readable intervals and a small API surface inside a Node.js process. Skip it if you need rate limiting, dead letter queues or job dependencies, which the README's own comparison table does not claim. Before committing, verify that your Node runtime is 18 or newer, that you have installed the matching @agendajs backend package, and that your job functions tolerate the at-least-once execution model implied by a database-backed queue.
Frequently asked questions
How do I install Agenda for a Node.js project?
Install the core package with npm install agenda, then install the backend for your database, for example npm install @agendajs/mongo-backend for MongoDB. Version 6 requires Node.js 18 or newer and is ESM-only.
Does Agenda require MongoDB?
MongoDB is the default and the README's primary example, and it requires MongoDB v4 or newer. Since version 6 the storage layer is a pluggable backend interface, and the README also names official PostgreSQL and Redis backend packages.
Which databases can Agenda use as a backend?
The README lists MongoDB, PostgreSQL and Redis in its feature comparison table, with separate npm packages @agendajs/mongo-backend, @agendajs/postgres-backend and @agendajs/redis-backend. You can also implement a custom backend against the AgendaBackend interface.
What changed in Agenda version 6?
The README lists an ESM-only codebase on Node.js 18 or newer, a pluggable backend interface, optional real-time notification channels, the MongoDB 6 driver, optional persistent job logging, and a move to a monorepo containing agenda, agendash and agenda-rest. A migration guide for v5 users is kept in the docs directory.
Does Agenda support cron expressions?
Yes. The README's example schedules the same job twice, once as agenda.every('3 minutes', 'delete old users') and once as agenda.every('*/3 * * * *', 'delete old users'), showing that human-readable intervals and cron syntax are interchangeable.
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/agenda-agenda)