JobRunr is a powerful, distributed background job processing library for Java and Kotlin. It integrates seamlessly with Spring Boot, allowing developers to execute jobs asynchronously with minimal configuration. A key feature of JobRunr is its ability to function with or without a database, making it highly flexible depending on project requirements.
This article will explore how to use JobRunr in Spring Boot in both modes: with a database for persistence and durability, and without a database for in-memory job execution. It covers only the free feature of JobRunr.
1. What is JobRunr?
JobRunr is a Java library for background processing and job scheduling. It allows developers to offload long-running or time-consuming tasks to a separate worker thread, ensuring the main application remains responsive.

Important: the jobrunr-spring-boot-starter-2 is removed since JobRunr v8. In JobRunr Pro, support is still available.
To add JobRunr to your Spring project, these are the steps you need to take:
Depending on your version of Spring Boot, add the jobrunr-spring-boot-3-starter dependency to your project Configure JobRunr via the Spring application.properties file Inject the JobScheduler or JobRequestScheduler bean and use it to create background jobs! Do you want to create jobs that automatically participate in the transactions managed by Spring? Then checkout JobRunr Pro!
Key Features of JobRunr
- Asynchronous job execution
- Supports Spring Boot out of the box
- Can be used with or without a database
- Distributed job processing
- Retry mechanism for failed jobs
- User-friendly job dashboard
2. Setting Up JobRunr in Spring Boot
To use JobRunr in a Spring Boot application, developers must add the required dependencies.
Add JobRunr Dependency
Include the JobRunr starter dependency in your pom.xml
:
<!-- https://mvnrepository.com/artifact/org.jobrunr/jobrunr --> <dependency> <groupId>org.jobrunr</groupId> <artifactId>jobrunr</artifactId> <version>7.4.1</version> </dependency>
3. Using JobRunr Without a Database (In-Memory Mode)
JobRunr supports an in-memory mode, which means jobs are not persisted and are lost when the application restarts. This mode is helpful for development, testing, or lightweight applications.
Configuring JobRunr Without a Database
By default, if no database is configured, JobRunr runs in-memory.
import org.jobrunr.configuration.JobRunr; import org.jobrunr.scheduling.JobScheduler; import org.jobrunr.storage.InMemoryStorageProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JobRunrConfig { @Bean public JobScheduler initJobRunr(InMemoryStorageProvider storageProvider) { return JobRunr.configure() .useStorageProvider(storageProvider) .useBackgroundJobServer() .useDashboard() .initialize() .getJobScheduler(); } @Bean public InMemoryStorageProvider storageProvider() { return new InMemoryStorageProvider(); } }
Creating and Enqueuing a Job
import lombok.extern.slf4j.Slf4j; import org.jobrunr.scheduling.JobScheduler; import org.springframework.stereotype.Service; // Example service with a background job @Service @Slf4j public class ExampleJobService { public void performBackgroundTask(String taskData) { log.info("Performing background task with data: {}", taskData); // Simulate some work try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } log.info("Background task completed for: {}", taskData); } public void scheduleRecurringJob(JobScheduler jobScheduler) { // Schedule a recurring job that runs every minute jobScheduler.scheduleRecurrently( "*/1 * * * *", // Cron expression (every minute for demonstration) () -> performBackgroundTask("Recurring Task") ); } public void enqueueSimpleJob(JobScheduler jobScheduler) { // Enqueue a one-time background job jobScheduler.enqueue(() -> performBackgroundTask("One-time Task")); } }
Creating a RESTful controller
import com.example.jobrunr.service.ExampleJobService; import org.jobrunr.scheduling.JobScheduler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class JobController { private final JobScheduler jobScheduler; private final ExampleJobService jobService; @Autowired public JobController(JobScheduler jobScheduler, ExampleJobService jobService) { this.jobScheduler = jobScheduler; this.jobService = jobService; } @PostMapping("/trigger-job") public ResponseEntity<String> triggerJob() { // Enqueue a job jobService.enqueueSimpleJob(jobScheduler); return ResponseEntity.ok("Job enqueued successfully"); } @PostMapping("/schedule-recurring-job") public ResponseEntity<String> scheduleRecurringJob() { // Schedule a recurring job jobService.scheduleRecurringJob(jobScheduler); return ResponseEntity.ok("Recurring job scheduled successfully"); } }
Pros and Cons of In-Memory Mode

4. Using JobRunr with a Database (Persistent Mode)
Database persistence is recommended for production applications to ensure jobs are not lost upon application restarts or crashes.

Important: you will need to add the correct dependency (jdbc-driver) for each of the databases below.
SQL databases Setting up an SQL database is easy-peasy because you probably don’t need to do a thing!
Sit back, relax and let JobRunr do the work for you! By default, JobRunr will automatically create the necessary tables for your database. Just like Liquibase and Flyway, it comes with a database migration manager that manages the database for you.
Supported Databases
JobRunr supports multiple databases, including:
- PostgreSQL
- MySQL
- MariaDB
- SQL Server
- H2 (for testing)
Set up PostgreSQL Database Server

Database Configuration in application.properties
For PostgreSQL:
spring.datasource.url=jdbc:postgresql://localhost:5432/jobrunrdb spring.datasource.username=postgres spring.datasource.password=secret spring.datasource.driver-class-name=org.postgresql.Driver
Include the starter dependency in your pom.xml
:
<!-- https://mvnrepository.com/artifact/org.jobrunr/jobrunr-spring-boot-3-starter --> <dependency> <groupId>org.jobrunr</groupId> <artifactId>jobrunr-spring-boot-3-starter</artifactId> <version>7.4.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency>
Configuring JobRunr to Use the Database
@Configuration public class JobRunrConfig { @Bean public JobScheduler initJobRunr(DataSource dataSource) { return JobRunr.configure() .useStorageProvider(new PostgresStorageProvider(dataSource, StorageProviderUtils.DatabaseOptions.CREATE)) .useBackgroundJobServer() .useDashboard() .initialize() .getJobScheduler(); } }
Pros and Cons of Database Mode

Table Jobrunr in Database

Example Query
-- Check background job servers SELECT * FROM public.jobrunr_backgroundjobservers; -- Check job status SELECT id, state, createdAt, scheduledAt FROM public.jobrunr_jobs ORDER BY createdAt DESC; -- Check recurring jobs SELECT id, cronExpression, nextRun FROM public.jobrunr_recurring_jobs; -- Check migrations SELECT * FROM public.jobrunr_migrations;
JobRunr Database Tables Explanation
1. public.jobrunr_backgroundjobservers
This table tracks the background job servers in the system.
Columns:
id
Unique identifier for each background job server instancename
Name of the background job serverworker_pool_size
Number of worker threads in the job serversystem_total_memory
Total system memory availablefree_memory
Available free memorymax_memory
Maximum memory allocationis_leader
Boolean indicating if this server is the leaderlast_hearthbeat
Timestamp of the last heartbeat signalstarted_at
Timestamp when the server was startedserver_status
Current status of the server (e.g., RUNNING, STOPPED)
2. public.jobrunr_jobs
Stores all individual jobs processed by JobRunr.
Columns:
id
Unique job identifierversion
Version number for optimistic lockingjob_signature
Unique signature of the job method and parametersstate
Current state of the job (ENQUEUED, PROCESSING, SUCCEEDED, FAILED)created_at
Timestamp when the job was createdupdated_at
Timestamp of last updatescheduled_at
Scheduled execution timeenqueued_at
Time when the job was enqueuedprocessing_at
Time when the job started processingfailed_at
Time of job failuresucceeded_at
Time of successful job completionjob_json
JSON representation of the job detailsjob_state_json
JSON with detailed job state information
3. public.jobrunr_metadata
Stores miscellaneous metadata for JobRunr.
Columns:
key
Unique identifier for the metadata entryvalue
Value associated with the keycreated_at
: Timestamp when metadata was createdupdated_at
: Timestamp of last update
4. public.jobrunr_migrations
Tracks database schema migrations for JobRunr.
Columns:
script
Name of the migration scriptexecuted_at
Timestamp when the migration was executedsuccess
Boolean indicating if migration was successfulscript_content
Full content of the migration script
5. public.jobrunr_recurring_jobs
Stores information about recurring jobs.
Columns:
id
Unique identifier for the recurring jobversion
Version for optimistic lockingjob_signature
Signature identifying the recurring job methodcron
Cron expression defining job recurrencenext_run
Timestamp of the next scheduled job executioncreated_at
Timestamp when the recurring job was createdupdated_at
Timestamp of last updatejob_json
JSON representation of the recurring job detailsstate
Current state of the recurring job
Test JobRunr via Postman
POST to /trigger-job
to enqueue a one-time job

c.e.jobrunr.service.ExampleJobService : Performing background task with data: One-time Task c.e.jobrunr.service.ExampleJobService : Background task completed for: One-time Task

POST to /schedule-recurring-job
to schedule a recurring job

c.e.jobrunr.service.ExampleJobService : Performing background task with data: Recurring Task c.e.jobrunr.service.ExampleJobService : Background task completed for: Recurring Task

5. JobRunr Dashboard
One of JobRunr’s best features is its web-based dashboard that provides insights into job execution.
Enabling the Dashboard
Add this configuration application.properties
:
jobrunr.dashboard.enabled=true jobrunr.dashboard.port=8000
Start the application and visit:
http://localhost:8000/
The JobRunr dashboard shows active, failed, and scheduled jobs.



6. Scheduling Recurring Jobs
Developers can also schedule recurring jobs using JobRunr.
Example: Run a Job Every Hour
@Service @Slf4j public class RecurringJobService { private final JobScheduler jobScheduler; public RecurringJobService(JobScheduler jobScheduler) { this.jobScheduler = jobScheduler; } @PostConstruct public void scheduleRecurringJob() { jobScheduler.scheduleRecurrently(Cron.hourly(), () -> log.info("Recurring job running...")); } }
7. Handling Job Failures & Retries
JobRunr automatically retries failed jobs.
Example: Handling Exceptions in Jobs
@Service public class FaultTolerantJobService { private final JobScheduler jobScheduler; public FaultTolerantJobService(JobScheduler jobScheduler) { this.jobScheduler = jobScheduler; } public void scheduleFailingJob() { jobScheduler.enqueue(() -> { throw new RuntimeException("Simulating a job failure"); }); } }
JobRunr will retry the failed job up to 10 times before marking it as permanently failed.

JobRunr handles all exceptions that occur both in internal (belonging to JobRunr itself), and external methods (jobs, filters and so on), so it will not bring down the whole application. All internal exceptions are logged (so, don’t forget to enable logging) and in the worst case, background processing of a job will be stopped after 10 retry attempts with a smart exponential back-off policy.
JobRunr Pro
JobRunr Pro offers advanced features that enhance job processing, especially in production environments.

Why JobRunr Pro?
- JobRunr Pro’s advanced features can significantly improve reliability and performance for production environments with high numbers of jobs.
- The ability to define custom retry policies and batch jobs provides greater flexibility and control over job processing.
- The enhanced dashboard and dedicated support make monitoring and managing JobRunr in production more manageable.
Comparing JobRunr with Other Job Schedulers

JobRunr stands out for its simplicity and seamless Spring Boot integration, making it an excellent choice for background job execution.
9. When to Use JobRunr Without vs. With a Database

10. Conclusion
JobRunr is a powerful, developer-friendly background job processing library for Spring Boot applications.
- Use in-memory mode for quick jobs and testing.
- Use database mode for production-grade applications with persistence.
- Take advantage of JobRunr’s dashboard for job monitoring.
Integrating JobRunr into your Spring Boot projects allows developers to manage background jobs with minimal effort.