Persistent command queues and scheduled actions for Paper servers.
Schedule commands for later, queue actions for a player's next login, and keep everything safely persisted across server restarts.
Releases β’ Modrinth β’ Issues β’ MIT License
LilsDeferred is a lightweight utility plugin for Paper that allows server administrators to queue commands and actions to run later.
Tasks can either run after a specified amount of time or the next time a particular player joins the server.
Everything is persisted using SQLite, meaning queued tasks survive restarts and remain available for inspection, cancellation, retrying and historical review.
/deferred in 30m say The event has started!
/deferred player Lily next-login give Lily diamond 3
- Persistent scheduled console commands
- Persistent next-login player actions
- SQLite storage using WAL mode
- Dedicated asynchronous database executor
- No recurring SQLite polling for scheduled tasks
- In-memory priority queue for efficient scheduling
- Automatic retries for failed scheduled tasks
- Manual retry support
- Task cancellation
- Persistent task history
- Automatic configurable history cleanup
- Crash recovery for interrupted tasks
- Paginated task listings
- Detailed task inspection
- Interactive MiniMessage output
- Clickable Inspect actions in
/deferred list - Granular permissions
- Configurable messages
- Runtime reload support for safe configuration options
Commands can be scheduled using a simple duration format:
/deferred in <duration> <command...>
For example:
/deferred in 30s say Thirty seconds have passed!
/deferred in 10m broadcast The event begins now!
/deferred in 1h30m say Ninety minutes have passed!
Supported duration units:
| Unit | Meaning |
|---|---|
s |
Seconds |
m |
Minutes |
h |
Hours |
d |
Days |
w |
Weeks |
Units can also be combined:
1h30m
2d12h
1w2d
Commands can also be queued for the next time a player joins:
/deferred player <player> next-login <command...>
Example:
/deferred player Lily next-login give Lily diamond 3
The task remains stored until that player next connects.
If multiple actions are waiting for the same player, they are executed in creation order.
| Command | Description |
|---|---|
/deferred in <duration> <command...> |
Schedule a command for later |
/deferred player <player> next-login <command...> |
Queue a command for a player's next login |
/deferred list [page] |
View stored deferred tasks |
/deferred inspect <id> |
Inspect a task in detail |
/deferred cancel <id> |
Cancel a pending or failed task |
/deferred retry <id> |
Retry a failed task |
/deferred reload |
Reload configuration and messages |
Task entries shown by /deferred list include a clickable Inspect action for quickly opening the corresponding task.
| Permission | Description |
|---|---|
lilsdeferred.create |
Create scheduled and next-login tasks |
lilsdeferred.list |
View stored task listings |
lilsdeferred.manage |
Inspect, cancel and retry tasks |
lilsdeferred.reload |
Reload configuration and messages |
lilsdeferred.admin |
Grants all LilsDeferred permissions |
All permissions default to server operators.
LilsDeferred stores the state of every task explicitly.
PENDING
β
βΌ
RUNNING
β
ββββββββββββββββΊ COMPLETED
β
ββββββββββββββββΊ FAILED
Tasks may also transition through:
PENDING ββββββββββββΊ CANCELLED
FAILED ββ retry βββΊ PENDING
This state-based design also exists at the database level: SQL updates only allow valid transitions such as PENDING β RUNNING or RUNNING β COMPLETED.
Failed scheduled tasks can automatically retry according to the configured attempt limit and retry delay.
For example:
execution:
max-attempts: 3
retry-delay-seconds: 30A scheduled task could therefore progress through:
Attempt 1
β
FAILED
β
30 second delay
β
Attempt 2
β
FAILED
β
30 second delay
β
Attempt 3
β
FAILED
Once the maximum attempt count is reached, the task remains FAILED for administrator review.
NEXT_LOGIN tasks are not automatically retried. A failed login action requires manual retry so that its execution remains tied to a future login rather than immediately repeating.
LilsDeferred keeps its runtime, business logic and persistence layers deliberately separated.
Commands / PlayerJoinEvent
β
βΌ
DeferredTaskService
β β
βΌ βΌ
TaskRepository ScheduledTaskQueue
β β
βΌ βΌ
SQLite ScheduledTaskRunner
β
βΌ
TaskExecutor
β
βΌ
Paper Main Thread
DeferredTaskService
Contains task creation, validation, lifecycle, retry and cancellation logic.
TaskRepository
Handles persistent DeferredTask storage and SQL operations.
ScheduledTaskQueue
Maintains scheduled jobs in an in-memory priority queue ordered by execution time.
ScheduledTaskRunner
Checks the in-memory queue for due tasks at a configurable interval.
TaskExecutor
Safely dispatches deferred commands onto Paper's main server thread.
LilsDeferred uses SQLite for persistent task storage.
The database is configured with:
WAL journal mode
NORMAL synchronous mode
Foreign-key enforcement
Busy timeout protection
A single persistent SQLite connection is owned by a dedicated database executor thread.
Database operations return asynchronous CompletableFuture results and JDBC objects remain contained inside the persistence layer.
LilsDeferred also uses numbered schema migrations through SQLite's user_version.
This allows future releases to evolve the database schema without requiring users to delete or recreate their existing data.
For example:
Schema 1
β
Migration 2
β
Schema 2
β
Migration 3
β
Schema 3
Older installations automatically apply each required migration in order.
LilsDeferred is designed to keep recurring runtime work extremely small.
A common scheduler design repeatedly performs something like:
SELECT *
FROM deferred_tasks
WHERE execute_after <= ?every second.
LilsDeferred does not do this.
Instead, pending scheduled tasks are loaded from SQLite at startup and stored in a Java PriorityQueue.
Runtime checks therefore operate against:
PriorityQueue.peek()
rather than repeatedly querying the database.
All normal SQLite operations are submitted to a single dedicated executor:
Paper / Command / Event
β
βΌ
Database Job Queue
β
βΌ
LilsDeferred-Database
β
βΌ
SQLite
No normal database query is intentionally performed synchronously on Paper's main thread.
Indexes are maintained for the queries that benefit from them, including:
- pending scheduled task loading
- next-login player lookups
- historical task cleanup
- task listing order
If a server has been offline for a long period and many scheduled tasks become overdue, LilsDeferred does not attempt to submit the entire backlog in one queue check.
execution:
max-tasks-per-check: 100Remaining tasks are processed over later checks.
There is an unavoidable ambiguity when executing persistent commands:
Task marked RUNNING
β
Command dispatched successfully
β
Server crashes
β
COMPLETED state was never persisted
Automatically replaying that command could duplicate something important such as:
- item rewards
- economy payments
- permissions
- crate keys
- administrative actions
For this reason, tasks found in the RUNNING state during startup are not automatically executed again.
Instead, LilsDeferred marks them:
FAILED
with an explanation that their previous execution outcome is unknown.
An administrator can then inspect the task and manually retry it if appropriate.
Completed and cancelled task history can be automatically removed after a configurable number of days.
history:
retention-days: 30The cleanup system only removes old:
COMPLETED
CANCELLED
tasks.
It never automatically removes:
PENDING
RUNNING
FAILED
tasks.
Set the retention period to:
retention-days: 0to keep historical tasks indefinitely.
Cleanup runs periodically and the actual deletion is performed asynchronously by the database executor.
LilsDeferred ships with a fully commented config.yml.
Main settings include:
database:
file: "deferred.db"
busy-timeout-ms: 5000
execution:
check-interval-ticks: 20
max-tasks-per-check: 100
max-attempts: 3
retry-delay-seconds: 30
history:
retention-days: 30The following values are applied by:
/deferred reload
execution.max-attemptsexecution.retry-delay-secondshistory.retention-daysmessages.yml
These settings configure components that are created during plugin startup and therefore require a restart:
database.filedatabase.busy-timeout-msexecution.check-interval-ticksexecution.max-tasks-per-check
Player and administrator-facing messages are stored separately in:
messages.yml
LilsDeferred uses Adventure MiniMessage, allowing server owners to customise:
- colours
- gradients
- hover text
- clickable actions
- formatting
without modifying the plugin.
- Download the latest LilsDeferred release.
- Place the
.jarfile into your server'splugins/directory. - Start or restart the server.
- Configure
config.ymlandmessages.ymlas desired. - Use
/deferredto begin creating tasks.
LilsDeferred automatically creates and migrates its SQLite database when required.
- Paper 26.2+
- Java 25
LilsDeferred is developed and tested against Paper.
Other server implementations are not currently officially supported.
Clone the repository:
git clone https://github.com/LilyK-97/LilsDeferred.git
cd LilsDeferredBuild using the included Gradle Wrapper.
.\gradlew clean build./gradlew clean buildThe resulting plugin will be available in:
build/libs/
LilsDeferred is part of the Lils collection: a growing set of clean, lightweight Paper utilities with a focus on practical server administration, efficient implementation and maintainable code.
The goal of each project is to remain focused rather than becoming an oversized all-in-one plugin.
Bug reports, suggestions and contributions are welcome.
When reporting a bug, please include relevant server logs, your Paper version and steps to reproduce the issue where possible.
- GitHub: https://github.com/LilyK-97/LilsDeferred
- Releases: https://github.com/LilyK-97/LilsDeferred/releases
- Issues: https://github.com/LilyK-97/LilsDeferred/issues
- Modrinth: https://modrinth.com/plugin/lilsdeferred
LilsDeferred is open-source software licensed under the MIT License.
You are free to use, modify and redistribute the project in accordance with the terms of the license.
See the full LICENSE file for details.
Made with πΈLoveπΈ for Paper servers.

