Scheduling
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This Backend Development section is a practical guide, focused on Spring Boot services, to running work on a clock: refresh a cache every few minutes, send reminders every night, create a reminder for one member when a loan is made and cancel it when the book comes back, produce a report that must not run twice and must survive a crash. It complements the SpringBoot Reference, whose Scheduling & ShedLock page documents standard scheduling, and it compares that standard scheduling with the advanced features of Quartz Scheduler: persistent and clustered schedules, misfire policies, calendars, runtime job management and recovery after a crash.
The section documents Quartz 2.5.2 (released 2025-12-01; Java 11 or later, Jakarta namespace; there is no 3.x) on Spring Boot 4.1.1, which manages that version, and Spring Framework 7.0.9, whose spring-context-support provides SchedulerFactoryBean, QuartzJobBean and LocalDataSourceJobStore, with Java 21. Standard scheduling is compared with ShedLock 7.10.1 (Java 17 or later). Where Spring Boot 3.5 differs, the pages show a short "Spring Boot 3.5" delta: Boot 3.5.16 manages the same Quartz 2.5.2, so only the package of the Boot Quartz types (org.springframework.boot.autoconfigure.quartz instead of org.springframework.boot.quartz.autoconfigure) and Spring Framework 6.2.19 differ. The versions were verified on 5 October 2026, and each page states what it measured: behaviours were verified in a test project, against real databases in Docker where a database page says so.
What scheduling means for a backend service
A web request starts when a client sends it; scheduled work starts when the clock says so. That difference creates the problems this section is about:
-
Nobody is waiting. A failed nightly job is noticed late, so jobs must log, expose their state and be safe to run again.
-
The service has more than one copy. Three replicas behind a load balancer would send every reminder three times unless something makes exactly one of them run each job.
-
The clock does not stop for deployments. A restart over a fire time either loses the run or runs it late; the right behaviour is a decision (a misfire policy), not an accident.
-
Some schedules are data. "Remind this member in 14 days" is created at run time, must survive a restart and may be deleted before it fires.
-
Crashes happen halfway. A report that took five minutes and died at minute four has to be finished by somebody.
Spring’s @Scheduled solves the first problem class for the simple case and is the right default. Quartz solves the rest and costs a job store. The section helps you tell which jobs are which.
This section is for developers who:
-
use
@Scheduledtoday, possibly with ShedLock, and need to know when to move to Quartz and how -
are adding Quartz to a Spring Boot 4 (or 3.5) service and need the starter, the configuration and the database set-up that actually work
-
have to run Quartz on a particular database (PostgreSQL, MySQL, MariaDB, Oracle, SQL Server, H2 or another SQL engine) or want it with MongoDB or Redis
-
want to test scheduled jobs, observe them in production and run them in a cluster
The Bookshelf scenario
Every example builds on Bookshelf, the domain used by the Resilience, FastAPI, NestJS, Django, Next.js and Kubernetes sections of this site, so readers can compare stacks. Here it is a Spring Boot 4.1 bookshelf-service (Java 21) with five kinds of scheduled work, each of which motivates a feature:
-
Cache refresh of the catalogue every 5 minutes. Standard
@Scheduled(fixedDelay)is enough. -
Nightly overdue-loan reminders at 02:00 Europe/Madrid on business days.
@Scheduled(cron)with ShedLock first, then a QuartzCronTriggerwith a holidayCalendarand a misfire policy. -
Per-member "return your book" reminders, created at run time when a loan is made and cancelled when the book is returned. This needs Quartz: dynamic one-shot
SimpleTrigger`s, a `JobDataMapand persistence across restarts. -
Weekly statistics report, long running, which must not overlap itself and must recover after a crash:
@DisallowConcurrentExecutionandrequestsRecovery, in a three-node cluster. -
Monthly retention cleanup on the last business day:
LandLWin cron and aCalendar; it launches a Spring Batch job (linked).
The reference database is PostgreSQL; every database page re-runs the same jobs on its own database so the pages can be compared. The comparison page, Standard Scheduling vs Quartz, migrates the weekly report from @Scheduled to Quartz.
The final layout
The classes the pages build up to, in one Maven module. Each piece is shown on the page named beside it:
bookshelf-service
├── pom.xml spring-boot-starter-quartz, -jdbc, -actuator, the JDBC driver
└── src
├── main
│ ├── java/bookshelf
│ │ ├── BookshelfApplication.java
│ │ ├── ScheduleProperties.java record for bookshelf.schedule.* (Spring Boot Configuration)
│ │ ├── BookshelfSchedule.java JobDetail and Trigger beans (Getting Started, Spring Boot Configuration)
│ │ ├── CacheRefreshJob.java job 1, or @Scheduled in the standard version
│ │ ├── OverdueLoanRemindersJob.java job 2, a QuartzJobBean (Getting Started)
│ │ ├── MemberReminderJob.java job 3, created through the Scheduler (Dynamic Scheduling)
│ │ ├── ReminderController.java REST API that creates and cancels reminders
│ │ ├── WeeklyStatisticsJob.java job 4, DisallowConcurrentExecution and requestsRecovery (Clustering)
│ │ ├── RetentionCleanupJob.java job 5, launches a Spring Batch job (Jobs and Job Data)
│ │ └── JobAuditListener.java a global JobListener (Listeners and Plugins)
│ └── resources
│ ├── application.yml shared settings, below
│ ├── application-dev.yml memory store, fast schedules
│ ├── application-prod.yml PostgreSQL, clustered, schema owned by Flyway
│ └── db/migration
│ └── V1__quartz_tables.sql tables_postgres.sql from the Quartz jar
└── test
└── java/bookshelf unit tests, memory-store test, Testcontainers cluster test (Testing)
Source: Spring Boot: Quartz Scheduler. The layout is a map of the pages, not one downloadable project: the five jobs and the schedule beans were compiled and run together for Spring Boot Configuration. Individual pages use local property names in their own examples (for example bookshelf.reminders.), while the consolidated configuration below uses bookshelf.schedule..
The application.yml that every environment shares, with the schedule as configuration so that each environment can speed it up:
spring:
application:
name: bookshelf-service
quartz:
scheduler-name: BookshelfScheduler
wait-for-jobs-to-complete-on-shutdown: true # honoured with Quartz's own SimpleThreadPool, not with a Spring TaskExecutor
overwrite-existing-jobs: false
bookshelf:
schedule:
zone: Europe/Madrid
cache-refresh-interval: 5m
overdue-cron: "0 0 2 ? * MON-FRI"
weekly-cron: "0 0 6 ? * MON"
retention-cron: "0 30 3 LW * ?" # last weekday of the month
management:
endpoints:
web:
exposure:
include: health,quartz
Source: Spring Boot: Quartz Scheduler and Spring Boot: Actuator Endpoints. The same file, with the dev, test and prod profiles around it, is on Spring Boot Configuration. The shutdown flag makes Quartz wait for running jobs and record their completion only with its own SimpleThreadPool; with a Spring TaskExecutor it is not honoured, and await-termination keeps the JVM alive without the completion being recorded (see Production & Best Practices).
The production profile turns on the JDBC store and clustering:
spring:
datasource:
url: ${DB_URL}
username: ${DB_USER}
password: ${DB_PASSWORD}
quartz:
job-store-type: jdbc
jdbc:
initialize-schema: never
properties:
org.quartz.scheduler.instanceId: AUTO
org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.isClustered: "true"
Source: Quartz documentation: Configure Clustering with JDBC-JobStore. The credentials are placeholders resolved from the environment, never literal values. The Testcontainers cluster test that exercises this configuration with two schedulers is on Testing Scheduled Jobs.
Reading path
The pages are ordered so that each builds on the previous ones. Read them in this order the first time, then use them as a reference:
-
Foundations: this page, then the comparison with standard scheduling.
-
Quartz core concepts: fundamentals first, then the Spring Boot integration, jobs and job data, triggers, cron, misfires, calendars, listeners and plugins.
-
Job stores and databases: the generic page first, then PostgreSQL (the reference), then your own database page.
-
Clustering and Spring Boot: clustering, the complete configuration, and runtime scheduling.
-
Quality and operations: observability, testing, production practice, and migration.
-
The cheat sheet: the whole section condensed onto one printable page.
Readers who already use @Scheduled can read the SpringBoot Reference page Scheduling & ShedLock and then Standard Scheduling vs Quartz. Readers who have decided on Quartz can go through Quartz Fundamentals, Getting Started with Spring Boot and then the page of their database. The Cheat Sheet (PDF) condenses the section for quick lookup.
Source: the pages of this section.
What’s covered
Foundations
-
Scheduling (this page): the scenario, the baseline and the bibliography.
-
Standard Scheduling vs Quartz: a recap of
@Scheduledand ShedLock, what they cannot do, a feature-by-feature table, cron dialects, a node crash under each, a decision flowchart, a migration walkthrough and the same idea in other stacks.
Quartz core concepts
-
Quartz Fundamentals: what Quartz is, the 2.4 and 2.5 lines,
Scheduler,JobDetailandTrigger, the fluent DSL, the scheduler lifecycle and a plain-Java first example. -
Getting Started with Spring Boot: the starter, what
QuartzAutoConfigurationdoes,QuartzJobBean, dependency injection into jobs and a first durable job with a cron trigger. -
Jobs and Job Data: the job instance lifecycle,
JobDataMapand its merged map, concurrency and persistence annotations, durability, recovery,JobExecutionException, interruption, idempotent jobs and launching Spring Batch. -
Triggers & Schedules: common trigger attributes, the four trigger types, choosing one, cookbook recipes and the per-member one-shot reminder.
-
Cron Expressions: the Quartz grammar, special characters, the
CronExpressionAPI, time zones and DST, and the translation table to Spring and Unix. -
Misfires: what a misfire is,
misfireThreshold, every instruction per trigger type and choosing a policy per job. -
Calendars: the calendars that ship with Quartz, chaining and registering them, and a business-day calendar from configuration.
-
Listeners and Plugins: job, trigger and scheduler listeners, matchers, registering them in Spring Boot, and the plugins.
Job stores and databases
-
Job Stores & Persistence: the job store SPI, RAM versus JDBC, the
QRTZ_tables, schema management, a dedicated Quartz database and a database support matrix. -
One page per database, all with the same template (status, dependencies, schema with Flyway and Liquibase,
application.yml, transactions and locking, clustering, a Testcontainers test, pitfalls):-
Quartz on PostgreSQL, the reference
-
Embedded databases (H2, HSQLDB, Derby) and other SQL engines
-
MongoDB and Quartz: four options, since there is no official MongoDB job store
-
Quartz with Redis: the community store and the alternatives
-
Clustering and Spring Boot integration
-
Clustering: row locks, check-ins, fail-over and recovery, the clock requirement, tuning, scaling limits and Kubernetes.
-
Spring Boot Configuration: every
spring.quartz.*property with its default, raw Quartz properties, customizers, thread pools, profiles and the complete Bookshelf configuration. -
Dynamic Scheduling & Management: the injected
Scheduler, a reminders REST API, securing it and transactional consistency.
Quality and operations
-
Observability: the Actuator
quartzendpoint,scheduledtasks, listener-based metrics and health, JMX, logs and alerting. -
Testing Scheduled Jobs: unit tests, trigger tests,
@SpringBootTest, Testcontainers and a two-scheduler cluster test. -
Production & Best Practices: the official best-practices page item by item, graceful shutdown, pool sizing, maintenance and an anti-patterns table.
-
Migration and What’s Changed: Quartz 2.3 to 2.5, Boot 3.5 to 4.x, from
@Scheduledand ShedLock, between job stores, and the table of places where the official documentation is stale.
Cheat sheet
-
Cheat Sheet (PDF): every concept of the section on one printable page.
Where related material lives elsewhere on this site
The section links to existing pages instead of repeating them.
| Topic | Where |
|---|---|
Standard scheduling: |
Scheduling & ShedLock (stays in the SpringBoot Reference; this section extends it) |
The SpringBoot Reference and its cheat sheet |
|
Launching a Spring Batch job from Quartz |
Running a Job, Spring Batch Reference and Spring Batch: Getting Started |
The same idea in Quarkus |
|
A Kubernetes |
|
The same idea in NestJS, Django (Celery beat) and ASP.NET Core ( |
Events, Scheduling & Queues, Celery Integration and Hosting, Servers, and Environments |
SQL, DDL and transactions behind the |
|
Managing the Quartz schema with migrations |
|
MongoDB, transactions and replication, and Spring Data MongoDB |
|
Redis and distributed locks |
|
Base Actuator, Micrometer and logging setup |
|
JUnit, Mockito and Testcontainers |
Unit & Integration Testing and Spring Boot Integration Tests with Testcontainers |
Virtual threads and the executors behind Spring’s scheduler |
|
Configuration and profiles, and securing the management API |
|
Retrying a flaky call inside a job (not the same as Quartz’s |
Bibliography
Every source used to write this section, each linked to its official documentation. Individual pages list the subset they derive from under their own == References heading. Where the official Quartz documentation is stale, the pages follow the Quartz 2.5.2 source and Javadoc and link both; the places where they differ are collected on Migration and What’s Changed.
Official Quartz documentation
The 2.5.x documentation lives at quartz-scheduler.org/documentation/quartz-2.5.x (all links below start with that address).
-
Overview: the 2.5.x index, Introduction, Quick Start, Best Practices, FAQ
-
Tutorials:
-
Configuration reference: Main, Thread Pool, Listeners, Plugins, RMI, RAMJobStore, JDBC JobStoreTX, JDBC JobStoreCMT, DataSources, JDBC JobStore Clustering
-
Cookbook (the cookbook index): Create a Scheduler, Standby, Shutdown, Multiple schedulers, Define a job with data, Schedule a job, Unschedule a job, Store a job, Schedule a stored job, Update a job, Update a trigger, Job initialization plugin, List jobs, List triggers, Job triggers, Job listeners, Trigger listeners, Scheduler listeners, Every ten seconds, Every ninety minutes, Daily, Every other day, Weekly, Every other week, Monthly
Official Quartz project resources
-
The source repository: the README, the
examplesmodule with its examples guide, thejdbcjobstoreDDL scripts, the delegates and the Liquibase changelog -
Releases (v2.5.2, v2.5.1, v2.5.0, v2.4.1, v2.4.0)
Official Spring documentation
-
Spring Boot:
-
Spring Framework:
-
Project pages for versions: Spring Boot and Spring Framework
Databases and drivers
Each database page links its own subset.
-
PostgreSQL: documentation and pgJDBC
-
MySQL: documentation and Connector/J
-
MariaDB: documentation and Connector/J
-
Oracle Database: documentation and JDBC
-
SQL Server: documentation and the JDBC driver
-
H2, HSQLDB and Apache Derby
-
MongoDB: manual and the Java driver
-
Testcontainers: database modules (PostgreSQL, MySQL, MariaDB, Oracle Free, MSSQL) and the MongoDB module
Community job stores
Official repositories of those projects; both are flagged as community or unmaintained in the text, and neither worked on Quartz 2.5.x in the tests of the database pages.
-
quartz-mongodb (archived)