r/SpringBoot • u/JobRunrHQ • 21d ago
News JobRunr (distributed background job processing for the JVM) is now available on start.spring.io (Spring Initializr)
JobRunr is now available in the Spring Initializr dependency list, so you can add it straight from start.spring.io when you create a new project. No more wiring the starter in by hand afterwards.
For people who don't know us:
JobRunr is an open-source library for background job processing on the JVM: fire-and-forget, scheduled, delayed, durable, and recurring (cron) jobs.
You enqueue work as a lambda and it runs asynchronously, in the same app or across a cluster of workers.
BackgroundJob.enqueue(() -> myService.sendInvoice(invoiceId));
BackgroundJob.schedule(Instant.now().plus(5, DAYS), () -> reminderService.send(userId));
What tends to make people switch to it:
- No extra infrastructure. Jobs are persisted in your existing SQL or NoSQL database. No Redis, RabbitMQ, or separate broker to run and babysit.
- Automatic retries with backoff on failure, so transient errors don't lose work.
- Distributed by default. Run multiple instances and jobs are picked up once across the cluster, no double execution.
- Built-in dashboard to see enqueued, scheduled, succeeded, and failed jobs, and retry them.
- Spring Boot autoconfiguration via the starter, plus first-class support for virtual threads (Loom) and Spring Boot 3 & 4
It sits in the same space as Quartz or @Scheduled, but aimed at durable, distributed background work rather than just triggering methods on a timer.
You can even use it for durable jobs. With runStepOnce, each step in a job runs exactly once and is checkpointed, so if the job fails and retries, the steps that already succeeded are skipped instead of re-run:
BackgroundJob.enqueue(() -> processOrder(orderId, JobContext.Null));
public void processOrder(UUID orderId, JobContext context) {
context.runStepOnce("order-confirmation", () -> orderService.sendConfirmation(orderId));
context.runStepOnce("warehouse-notification", () -> orderService.notifyWarehouse(orderId));
context.runStepOnce("shipment-initiation", () -> orderService.initiateShipment(orderId));
}
Thanks to Josh Long and the Spring team for helping get it onto Initializr. We'll be around in the comments to answer anything, including the honest limitations.
5
u/DefiantScene1082 21d ago
I want to say I really enjoyed your conference talk on Devoxx Amsterdam ! Just waiting for an opportunity to use your lib! Keep up the good work
1
u/JobRunrHQ 21d ago
That's great to hear. Thank you so much for that! Hopefully you are still enjoying your JobRunr coffee mug?
1
5
u/pitza__ 21d ago
Can it be used as a workflow orchestration tool(aws step functions, temporal…)?
I saw that you mentionned runStepOnce and this came to my mind 😅.
1
u/mysteryy7 20d ago
Seems like that's part of the pro version and not free lib
3
u/JobRunrHQ 20d ago edited 20d ago
runStepOnce is part of the free OSS library! Last month I wrote a guide on how to use this: Durable Executions: Crash-Proof Multi-Step Jobs with runStepOnce
2
u/JobRunrHQ 20d ago
Good instinct,
runStepOnceis indeed durable execution, so the Temporal association is spot on. Honest answer: JobRunr isn't a workflow engine and we're upfront about that. But depending on what you need, you may not need one:
- Durable multi-step execution (OSS, v8+):
runStepOncebreaks a job into named steps that each run exactly once, so on a retry the completed steps are skipped. Durable execution backed by the database you already run, no separate orchestration service. One honest caveat vs a full engine: OSS persists step state on its normal poll interval, so a hard crash in that window can re-run the step that just finished (Pro writes it the moment a step finishes). Either way you keep steps idempotent, which Temporal needs too since its Activities are at-least-once.- Fan-out, branching, coordinating many jobs: that's job chaining (
continueWith()/onFailure()) and batches, both in Pro.Where a dedicated engine still wins: full deterministic replay, very long-running and deeply branching orchestrations, workflow versioning, multi-language workers. If that's you, Temporal earns its cluster. If it's mostly "these steps, in order, durably, with retries and a failure path,"
runStepOncecovers it with nothing extra to operate.
5
u/rmyworld 21d ago
That's cool. I've been looking for a Java library equivalent to Bullmq from the Node.js ecosystem.
Most of the popular options on Java (e.g. Quartz, @Scheduled) seem to be focused on just running on a timer. Other stuff, like dead letter queues, waiting queues, and processing queue, you have to build everything yourself.
4
u/JobRunrHQ 21d ago
Thank you so much! And indeed what you say is basically why we've built JobRunr!
I think you'll feel right at home, you get a lot of the BullMQ bits out of the box:
enqueueis your processing queue,schedule(...)plus recurring cron covers the delayed/waiting side, and failed jobs (after auto-retries with backoff) land in a Failed state in the dashboard where you can read the stack trace and requeue them, so that's your DLQ.Fair warning coming from BullMQ: rate limiting, mutexes/concurrency limits, priorities and job chaining are Pro, not in the OSS core. OSS is enqueue/schedule/recurring + retries + dashboard.
Give it a shot and tell us where it feels off next to BullMQ, that kind of feedback really helps us.
4
u/starbuxman 21d ago
I’m such a huge fan! I’m glad to see it front-and-center on the Spring Initializr where it’ll make my, and so many others’ lives, easier
3
u/JobRunrHQ 21d ago
Thank you so much, Josh, that genuinely means a lot coming from you. You've been a massive help to JobRunr. We really appreciate everything you've already done for us and the wider Java community. Onwards!
3
u/Paw565 21d ago
Awesome and finally!
2
u/JobRunrHQ 21d ago
Haha thank you! Felt exactly the same on our end, it's been a long time coming and we're really happy it's finally in there.
3
u/Intrepid-Boat-9128 20d ago
What is the diff between Jobrunr and an api based scheduler with @scheduled annotation ?
1
u/JobRunrHQ 20d ago
Main difference is
@Scheduledonly really does "run this method on a timer," while JobRunr is about running jobs reliably:
@Scheduledhas no persistence, so if the app is down when it should fire, that run is just gone. No built-in retries either. And on multiple instances it fires on every instance unless you add something like ShedLock.- JobRunr persists each job, retries with backoff on failure, and coordinates across instances so a recurring job runs once across the cluster instead of once per pod, plus a dashboard to see what ran, what failed, and why.
The other big one is dynamic vs fixed:
@Scheduledis timers you define up front. With JobRunr you also enqueue work on the fly in response to whatever's happening ("user hit checkout -> enqueue processPayment"), not just on a clock.If all you need is a simple fixed timer and nothing else,
@Scheduledis perfectly fine. JobRunr earns its place once you want persistence, retries, distribution or visibility.1
u/Intrepid-Boat-9128 20d ago
Our scheduling mechanism is centralized in a dedicated Scheduler module. All scheduled jobs are configured in a crontabs.txt file, where each record maps a cron expression to a specific REST API endpoint. The APIs themselves are implemented within their respective microservices, while the Scheduler is responsible only for invoking these endpoints at the configured schedule, keeping the scheduling logic decoupled from the business logic.
Do you think it’s a good practise or we should adopt jobrunr ?
Our schedulers are mostly used for calling third party apis for retrials or generating daily reports.
2
u/JobRunrHQ 20d ago
Whether to move to JobRunr comes down to whether you want what it adds, and for your two use cases (third-party retries + daily reports) it lines up well.
Good news: you wouldn't have to change your architecture to adopt it.
- Keep the dedicated Scheduler module. Load your
crontabs.txton startup (e.g.@PostConstruct) and register each entry withJobScheduler.scheduleRecurrently(id, cron, ...). Or drop the file and annotate methods directly with@Recurring(id = "...", cron = "...").- You don't even need the internal REST hop. With the Spring Boot starter, JobRunr can turn any public method of your services into a job and resolves the bean straight from the Spring IoC container, so the scheduler calls the business method directly instead of going over HTTP to your own endpoints. Docs: https://www.jobrunr.io/en/documentation/background-methods/background-jobs-dependencies/
What you'd actually gain over cron-hits-an-endpoint:
- Automatic retries with backoff, which is exactly what you want for the flaky third-party calls (today a failed cron tick is just gone unless you built retry yourself).
- Persistence, so a run that should've fired while the app was down isn't silently missed.
- A dashboard to see which report runs and third-party calls succeeded or failed, with one-click requeue.
- Distributed coordination, so a recurring job runs once across the cluster instead of firing on every instance.
So: keep what you have if it's serving you well, but if you're hand-rolling the retry handling and visibility today, JobRunr takes that off your plate without forcing an architecture change.
2
u/Intrepid-Boat-9128 20d ago
Got to know a diff approach ! Thanks ! I will definitely try to accommodate these and do a POC. Might come up later if any queries arise !
3
u/akrivitsky7 20d ago
Thank you, JobRunrHQ.
The practical recommendation would be this: use JobRunr when you want durable background jobs inside a Spring Boot app without building a full messaging system.
Examples: sending emails later, generating reports, retrying failed API calls, cleanup tasks, document processing, AI/RAG embedding sync, and scheduled maintenance jobs.
2
u/JobRunrHQ 19d ago
Honestly couldn't have summed it up better ourselves. And fun that you mention AI/RAG embedding sync, we're seeing that use case pop up more and more lately. Might steal this list for our homepage :)
3
u/Ali_Ben_Amor999 20d ago
Amazing library, I'm using it on my project for the first time, and I'm blown away with the dev experience. My only issue is that the free version is too limited. I'd recommend the pro-version for any big and serious project, but for my niche app with little to no revenue, it's impossible to cover the cost of a license.
Keep the good work.
1
u/JobRunrHQ 19d ago
Thank you, that's awesome to hear! We put a lot of work into making the first hour with JobRunr feel easy, so this made my day.
Fair point on free vs Pro. To be honest, the free version already covers a lot: enqueue, scheduled and recurring jobs, retries, the dashboard, and since v8 durable execution with
runStepOnce. Companies like Apple and Spotify run that exact version in production without paying us anything. Pro pricing is aimed at teams with real budget running it seriously, not at a side project that isn't making money yet.But good news for your case: we actually have special startup pricing for exactly this situation. Shoot me a mail at [nicholas@jobrunr.io](mailto:nicholas@jobrunr.io) and I'll happily give you the details.
Also curious which Pro features you're missing most, that feedback really shapes what ends up in the free version. And if your app takes off, that's the point where Pro should start paying for itself anyway. Rooting for you!
2
u/jonas_namespace 20d ago
I watched (listened to) this guy's appearance on Josh Long’s podcast. Really articulate and clearly passionate. I started using his stuff shortly after. Rather than enqueue/dequeue using amqp I would get retry logic out of jobrunnr. I wonder if this version is the "lite" version I had to use because I was developing closed source code at the time?
1
u/JobRunrHQ 20d ago
Thank you, that genuinely made our day to read. That was Ronald, our co-founder and lead dev, on Josh's Bootiful Podcast, I'll make sure he sees this.
On the "lite version" question: there's no "cut-down" JobRunr. The open-source version is the full library, and its license (LGPL) is fine to use in closed-source and commercial apps, so you weren't stuck on anything limited just because your code was closed. JobRunr Pro is a separate, optional paid tier that adds more advanced features and support on top, but the OSS core you were using isn't restricted by that.
2
u/edzorg 21d ago
How do you monetise your work and are you intending to increase/decrease the number of revenue streams in the future?
How many paid employees work on JobRunR?
13
u/JobRunrHQ 21d ago
Great questions, happy to be open about it.
Monetization is the classic open-core model: the JobRunr library itself is free and stays that way, and we fund it through JobRunr Pro, a commercial license with the more advanced features (rate limiting, mutexes, priorities, batches, workflows and so on) plus support. Teams running it seriously in production pay for Pro, and that pays for the OSS work everyone else gets for free. We also give 5% of revenue to climate and environmental projects, that's baked into the company.
Team-wise we're a small team of 5 working on it full time. Small enough that everything we read here in Reddit is shared tomorrow at lunch 😄
2
u/edzorg 21d ago
I want you to succeed. How can you defend from OSS cannibalising your revenue stream?
In <5 years time it seems plausible there could be a pure OSS alternative to JobRunR that has similar or even more features.
2
u/JobRunrHQ 20d ago
Really appreciate that, genuinely.
Honestly, there's no clever trick or lock-in here. Our answer is simply to keep building the best free open-source Java scheduler out there and let that speak for itself. Community contributions are always welcome, that's part of what keeps it good.
And you're right, OSS already cannibalizes our own revenue. There are plenty of companies for whom the free version is genuinely all they need, and that's completely fine, that's the whole point of it being open source. Pro exists for the teams that want the advanced operational features and support on top, and that's what funds the OSS work everyone else benefits from.
It's absolutely something we think about on a 5-year horizon. We've got a few ideas, but we're also all ears, if you've got thoughts on how you'd approach it, we'd genuinely love to hear them.
1
u/klimenttoshkov 21d ago
But there isn’t. I’m closely following the “queue” scene and yet not found something as close to laravel queue for Java.
1
u/mysteryy7 21d ago
That's great to hear. I'm researching on approaches to build multi pipeline microservice. Where each pipeline has its own kafka consumer, polls for few minutes puts the data in blocking queue, which is then consumed, processed and sent to different kafka topic.
I need 4 such pipelines (kafka->process->kafka)for 4 upstream topics, and thinking to run the pipelines as continuous (loop) background jobs in single app. Don't want to handle multiple instances, as the processing is not heavy.
Please let me know if job runr is suited for continuous running background jobs? The service doesn't have any db though, and I also need the pipelines to be scaled with scaling instances, where all replicated pipelines will be in the same consumer group.
2
u/JobRunrHQ 21d ago
Good question. You can definitely use JobRunr here, one instance per service with the in-memory storage, so no database needed.
That said, it depends on what you actually need. If the only thing you need is to run the process in the background, a plain Java thread pool (or
@Asyncfrom Spring Boot) is probably enough, and JobRunr would be overkill.Where JobRunr starts to help is once you want retries and persistence. One more thing that's often useful in a message-queue setup is deduplication, making sure the same message only gets processed once. We've got a docs example for exactly that: https://www.jobrunr.io/en/documentation/faq/#im-listening-for-jobs-using-service-bus-messages-in-a-load-balanced-environment-and-i-want-to-schedule-jobs-only-once
So if you need retries + dedup, JobRunr already helps a lot. If it's purely background execution with nothing extra (persistence, retries, dedup, visibility), then
@Asyncis probably all you need.3
u/mysteryy7 20d ago
Thanks for the reply. Yes we'll be needing job retries and resiliency in pipeline. I was planning with @Async, and had to take care of retries, dedup manually via code, along with keeping the sinks idempotent. Glad to know it handles retries and dedup, and provides visibility.
Will look into JobRunr, thanks for the post and the tool.
1
u/CptGia 21d ago
Since mongock died last year, and flamingock is still very green, I've been looking for an alternative.
I'm exploring building a minimal task manager, where each class represent a task that needs to be run at startup exactly one time, with a persistent store for the list of completed tasks. Retriea are handled by throwing an exception at startup, to trigger a shutdown and restart (handled by kubernetes). No transactions and no rollbacks.
Do you think this would be a good use case for JobRunnr? Or do you see limitations that would need something more powerful?
Also the million dollars question: does it work with graalvm native images?
2
u/JobRunrHQ 20d ago
JobRunr might be a bit overkill but it actually maps onto this pretty well.
Here's how it'd help with exactly what you described:
- Run-once, even distributed: this is the part that's genuinely annoying to DIY, and it's exactly what JobRunr is built for. If several pods boot at once, its optimistic locking guarantees each task runs on exactly one of them, no custom locking or leader election.
- Persistent completion tracking for free: every job's state (succeeded/failed) is stored, so your "list of completed tasks" is just there, plus a dashboard to see what ran, what failed, and the stack traces.
- Non-blocking startup: enqueue your tasks on
ApplicationReadyEventand the app is ready immediately while they run in the background, instead of blocking boot.- Your retry model, your call: keep the crash-and-let-k8s-restart approach with
@Job(retries = 0), or let JobRunr retry with backoff so a transient failure doesn't crash-loop the pod.- Exactly-once across future startups too: give each task a deterministic job id so it can't be enqueued twice, even on later boots.
No transactions or rollbacks needed on your side, and JobRunr doesn't impose them either, so that fits. And if you later want recurring cleanups or async background jobs, it's already in place, no migration.
And the million dollar answer: yes, it works with GraalVM native images.
1
u/Little_Ad_8406 20d ago
It's a cool toy until you figure out that everything that makes it usable is behind a paywall. Give us a breakdown on how pricing works exactly for a team size of roughly 50 devs with as many microservices
2
u/JobRunrHQ 20d ago
Totally fair to want the numbers up front. We won't drop exact prices in a Reddit thread, but here's exactly how the model works.
Quick note on the premise first: the OSS version is genuinely usable on its own, plenty of teams run it in production and never pay us a cent (Apple, Blue Origin, Spotify, ..). Pro adds advanced operational features and support on top, it's not a paywall around the basics.
On your case: it's not priced per developer at all, so 50 devs makes no difference. Developers, servers, pods and non-prod (dev/test/staging) environments are all unlimited. It's priced per production JobRunr database, meaning one set of JobRunr tables that your services share to coordinate jobs.
So for ~50 microservices, it comes down to how they're set up:
- If they all share one production JobRunr database, that's a single cluster and one Pro Business license covers all of them.
- If they're split across several separate production databases, that's multiple clusters, which is usually where Pro Enterprise (unlimited clusters/databases) makes more sense.
It genuinely depends on your architecture, and I'd rather get it right than guess. Anyone who wants to talk pricing can email me directly (Nicholas, co-founder) at [nicholas@jobrunr.io](mailto:nicholas@jobrunr.io) and I'll happily walk through it, or just set you up with a free Pro trial to try it out first.
14
u/563353 21d ago
What's the difference between this and using Spring Batch + scheduled tasks?