Minestom provides extensive configuration through system properties defined in ServerFlag. These flags control server behavior, networking, chunk management, and experimental features.
// Control server tick rate (default: 20 TPS)-Dminestom.tps=20// Maximum ticks to catch up if server falls behind (default: 5)-Dminestom.max-tick-catch-up=5
The catch-up mechanism prevents the server from spiraling when it falls behind, limiting how many ticks can be processed in rapid succession.
View distance is one of the most impactful performance settings:
// Base chunk view distance for instances (default: 8)-Dminestom.chunk-view-distance=8// Entity view distance in chunks (default: 5)-Dminestom.entity-view-distance=5// Entity synchronization interval in ticks (default: 20)-Dminestom.entity-synchronization-ticks=20
Lowering chunk view distance from 8 to 6 can significantly improve performance on servers with many players, while still maintaining good gameplay experience.
Control how many chunks are sent to players per tick:
// Minimum chunks per tick (default: 0.01)-Dminestom.chunk-queue.min-per-tick=0.01// Maximum chunks per tick (default: 64.0)-Dminestom.chunk-queue.max-per-tick=64.0// Multiplier for chunk sending rate (default: 1.0)-Dminestom.chunk-queue.multiplier=1.0
The chunk queue system dynamically adjusts how many chunks are sent based on player movement and server load:
public class ChunkLoadingExample { public void optimizeChunkLoading(Instance instance) { // Chunks are automatically managed by the chunk queue // The system balances between MIN_CHUNKS_PER_TICK and MAX_CHUNKS_PER_TICK // For heavy terrain generation, consider: // -Dminestom.chunk-queue.max-per-tick=32.0 // to prevent overwhelming the network }}
// Maximum packets processed per player per tick (default: 50)-Dminestom.packet-per-tick=50// Maximum queued packets per player (default: 1000)-Dminestom.packet-queue-size=1000// Keep-alive packet interval in ms (default: 10000)-Dminestom.keep-alive-delay=10000// Kick player after this many ms without keep-alive response (default: 15000)-Dminestom.keep-alive-kick=15000
Setting packet-per-tick too low may cause legitimate players to be rate-limited during normal gameplay. The default of 50 is suitable for most servers.
The ThreadDispatcher manages entity and chunk ticking across multiple threads:
import net.minestom.server.thread.ThreadDispatcher;import net.minestom.server.thread.ThreadProvider;public class CustomDispatcherExample { public void createCustomDispatcher() { // Create a dispatcher with 4 threads ThreadDispatcher<MyPartition, MyTickable> dispatcher = ThreadDispatcher.dispatcher( ThreadProvider.counter(), 4 ); // Start the dispatcher dispatcher.start(); // Create partitions (similar to chunks) MyPartition partition = new MyPartition(); dispatcher.createPartition(partition); // Add tickable elements MyTickable element = new MyTickable(); dispatcher.updateElement(element, partition); // Update all elements (called each tick) dispatcher.updateAndAwait(System.nanoTime()); // Refresh thread assignments (called after ticking) dispatcher.refreshThreads(); }}
Access entities on the current thread without acquisition:
import net.minestom.server.thread.Acquirable;import net.minestom.server.entity.Entity;public class LocalThreadOptimization { public void processLocalEntities() { // Get all entities on the current thread Acquirable.localEntities().forEach(entity -> { // Safe to access without acquisition // This is very fast as no locking is required entity.setGlowing(true); }); }}
When processing many entities, using Acquirable.localEntities() is much faster than acquiring each entity individually.
# Run all benchmarks./gradlew jmh# Run specific benchmark./gradlew jmh --args="PaletteGetBenchmark"# Run with custom parameters./gradlew jmh --args="-f 1 -wi 3 -i 5 PaletteGetBenchmark"