diff --git a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/ExecutorSchedulerBackend.java b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/ExecutorSchedulerBackend.java
new file mode 100644
index 000000000..d10eab6f4
--- /dev/null
+++ b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/ExecutorSchedulerBackend.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018-2026 Velocity Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.velocitypowered.proxy.scheduler;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link SchedulerBackend} backed by a real {@link ScheduledExecutorService}.
+ */
+public class ExecutorSchedulerBackend implements SchedulerBackend {
+
+ private final ScheduledExecutorService executor;
+
+ /**
+ * Creates a ExecutorSchedulerBackend with a default executor.
+ */
+ public ExecutorSchedulerBackend() {
+ this(Executors.newSingleThreadScheduledExecutor(
+ new ThreadFactoryBuilder()
+ .setDaemon(true)
+ .setNameFormat("Velocity Task Scheduler Timer")
+ .build()
+ ));
+ }
+
+ /**
+ * Creates a ExecutorSchedulerBackend with a given executor.
+ *
+ * @param executor The executor to use.
+ */
+ public ExecutorSchedulerBackend(ScheduledExecutorService executor) {
+ this.executor = checkNotNull(executor, "executor");
+ }
+
+ @Override
+ public ScheduledFuture> schedule(Runnable task, long delay, TimeUnit unit) {
+ return executor.schedule(task, delay, unit);
+ }
+
+ @Override
+ public ScheduledFuture> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
+ return executor.scheduleAtFixedRate(task, initialDelay, period, unit);
+ }
+
+ @Override
+ public void shutdown() {
+ executor.shutdown();
+ }
+}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/SchedulerBackend.java b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/SchedulerBackend.java
new file mode 100644
index 000000000..ab251ca27
--- /dev/null
+++ b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/SchedulerBackend.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018-2026 Velocity Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.velocitypowered.proxy.scheduler;
+
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Backend interface used by {@link VelocityScheduler} to schedule timer callbacks.
+ *
+ *
This is an internal abstraction that allows tests to replace the real-time scheduler
+ * with a deterministic implementation.
+ */
+interface SchedulerBackend {
+
+ /**
+ * Schedules a task to run once after the given delay.
+ *
+ * @param task the task to run
+ * @param delay the delay
+ * @param unit the delay unit
+ * @return a future representing the scheduled task
+ */
+ ScheduledFuture> schedule(Runnable task, long delay, TimeUnit unit);
+
+ /**
+ * Schedules a task to run at a fixed rate.
+ *
+ * @param task the task to run
+ * @param initialDelay the initial delay
+ * @param period the period between runs
+ * @param unit the time unit
+ * @return a future representing the scheduled task
+ */
+ ScheduledFuture> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit);
+
+ /**
+ * Shuts down the backend.
+ */
+ void shutdown();
+}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java
index 727832bd4..0a38582a7 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018-2023 Velocity Contributors
+ * Copyright (C) 2018-2026 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,7 +23,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginManager;
import com.velocitypowered.api.scheduler.ScheduledTask;
@@ -40,7 +39,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -60,20 +58,23 @@ import org.jetbrains.annotations.VisibleForTesting;
public class VelocityScheduler implements Scheduler {
private final PluginManager pluginManager;
- private final ScheduledExecutorService timerExecutionService;
+ private final SchedulerBackend backend;
private final Multimap