Most Minecraft restart tutorials are a single cron line that stops the server, waits sixty seconds, and starts it again. That works right up until the night a world save is still flushing when the process dies, or a player loses an inventory to a restart nobody warned them about. This guide exists to replace that pattern with something you can leave running for a year without opening a ticket queue.
It is written for server owners and administrators running Paper or Purpur on Linux who want scheduled restarts to be boring: predictable for players, safe for the world, and observable when something goes wrong. Every number below comes from a real test box, not from an estimate. Intel Core i5-13400F, 16 threads, 14 GB RAM, Ubuntu 26.04, OpenJDK 25.0.4.1, Paper 26.2 build 129, Java flags -Xms2G -Xmx4G -XX:+UseG1GC, holding 22,801 pre-generated chunks. The timer was fired, the journal was read, and the timings were written down.
Two full restarts were measured end to end, including the warning countdown, the save flush, the graceful stop, the start, and an RCON readiness poll. Total downtime landed around eight seconds. That number is the entire point of the architecture below: a restart your players barely notice is a restart that is safe to put on a timer.
Why Schedule a Restart at All (and When You Should Not)
Be honest about the reason before you configure anything. A restart is a garbage collector of last resort. It fixes nothing; it resets a process. That is still useful, because several things inside a long-running JVM only move in one direction.
Memory fragmentation and heap growth
With -Xms2G -Xmx4G the JVM is allowed to climb to 4 GB and, in practice, rarely gives that memory back in a way that helps a chunk-heavy workload. The resident set settles high, garbage collection has more to walk on every cycle, and pause times creep up. After a restart the heap returns to its floor and the pause profile returns with it. Confirm this rather than assuming it: spark will show you heap after a full GC over several days. If that line climbs steadily, restarts are buying you time while a leak is still there.
Plugin and cache leaks
Plenty of plugins keep a map keyed by player UUID or name that is appended to but never pruned. Region tools, hologram managers, custom scoreboard handlers, and economy caches are common offenders. None of them necessarily look broken; they simply hold references to objects the game considers dead, so the collector cannot reclaim them. A restart is the only cheap way to clear that state, which is exactly why scheduled restarts are so common on servers running thirty or more plugins. If the heap graph shows a stair-step pattern that resets at every restart, you have found the leak and the workaround in the same graph.
Entity and chunk buildup
Spawn chunks never unload. Over a week that means dropped items from farms, XP orbs nobody picked up, armor stands, item frames, and stray mobs accumulate in a region that is always ticking. Farms that spill items onto hopper-less floors are the classic source. A restart does not delete entities, but it does reset the ticking and pathfinding state around them, and combined with a periodic cleanup it keeps that bubble from turning into a permanent tax on your tick budget.
When a restart is unnecessary
This is the part most restart guides skip. A healthy server does not need hourly restarts, and restarting more often is not a sign of good administration.
- TPS sits at 20.0 with stable MSPT across hours, not just at the moment you check.
- Heap after a full GC is flat over several days in spark.
- You run a whitelisted server with five to ten concurrent players and no heavy farms.
- You restarted an hour ago and the lag came straight back. That is a bug or a configuration problem, not an uptime problem.
Hourly restarts on a small server are cargo cult. If lag returns within ten minutes of a restart, the restart is not the fix: audit your plugin list with How to Audit and Trim Plugins to Reduce Server Lag and check your chunk load with View Distance vs Simulation Distance: Cutting Chunk Lag for Real. Schedule a restart to reset accumulated state, not to paper over a defect you refuse to diagnose.
Choosing a Schedule Players Can Live With
A restart is only free if players expect it. The schedule is a communication decision as much as an operations decision.
Pick the hour from data, not from a tutorial
Do not copy 04:00 because someone else did. Pull your own concurrency curve from Plan, a playercount plugin, or plain timestamps in your server log. On the test box the quiet window was three hours earlier than the owner assumed, because most of the player base lived in a different timezone than the machine.
Timezone, Persistent, and RandomizedDelaySec
To quote the systemd.timer documentation, calendar events are evaluated in the local timezone of the host. Run timedatectl and confirm what that is before you write the line. Then record the schedule twice: once in UTC in your notes, and once in the local time of your player base for the MOTD.
Persistent=true is the directive people skip. systemd writes a stamp file under /var/lib/systemd/timers, and if the machine was rebooting at 04:00 the timer fires once shortly after boot instead of silently skipping a day. On a server that was powered off for a week it still runs only once, which is what you want.
RandomizedDelaySec spreads load across a fleet. For a single Minecraft server it is the wrong tool, because it makes the restart time unpredictable, and unpredictable downtime is precisely what annoys players. Leave it at 0 unless several servers share one host, where a staggered value stops seven JVMs from booting at the same second and fighting for CPU.
| Pattern | Good fit | Caveat |
|---|---|---|
| Daily, single low-population hour | Most servers under 100 concurrent | Requires knowing your real low point |
| Twice daily | Large public servers with heavy farms and long uptime | Doubles downtime exposure and ticket surface |
| Weekly | Small whitelisted friend servers | Leaks still accumulate; watch heap after GC |
| Never, manual only | Stable, well-monitored servers | Requires discipline; a leak still arrives at 2 a.m. |
The systemd Setup: Three Units and One Script
The split matters. The JVM runs under a long-lived service unit. A oneshot unit runs the orchestration. A timer triggers the oneshot. Because the orchestrator is a separate unit from the server process, it can stop and start the server without killing itself mid-sequence, which is exactly the failure a naive cron job produces.
1. /etc/systemd/system/minecraft.service
[Unit]
Description=Minecraft Paper Server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=minecraft
Group=minecraft
WorkingDirectory=/srv/minecraft
ExecStart=/usr/bin/java -Xms2G -Xmx4G -XX:+UseG1GC -jar paper.jar --nogui
KillSignal=SIGINT
TimeoutStopSec=120
Restart=on-failure
RestartSec=10
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
KillSignal=SIGINT is the most important line in the file. Paper installs a shutdown hook that saves worlds and flushes region files when the JVM receives an interrupt. TimeoutStopSec=120 gives a large world time to write before systemd escalates. SuccessExitStatus=143 declares the normal interrupt exit code a success so that Restart=on-failure does not fire after every scheduled restart. Full directive behavior is in the systemd.service documentation.
2. /etc/systemd/system/minecraft-restart.service
[Unit]
Description=Scheduled Minecraft restart
After=minecraft.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/minecraft-restart
Nice=5
Type=oneshot tells systemd this unit finishes when the script exits, and it waits for that exit before considering the unit done. Nice=5 lowers the priority of the orchestration work so countdown messages and RCON calls do not compete with the tick threads.
3. /etc/systemd/system/minecraft-restart.timer
[Unit]
Description=Daily Minecraft restart timer
[Timer]
OnCalendar=*-*-* 04:00:00
Persistent=true
RandomizedDelaySec=0
[Install]
WantedBy=timers.target
A timer unit is paired with the service of the same base name, so minecraft-restart.timer activates minecraft-restart.service automatically. On the test box the timer registered cleanly and systemctl list-timers showed the next run roughly nine hours ahead. If you want a second daily restart, add another OnCalendar line rather than a second timer.
4. /usr/local/bin/minecraft-restart
#!/usr/bin/env bash
set -euo pipefail
RCON_HOST=127.0.0.1
RCON_PORT=25575
RCON_PASS="change-me"
LOG=/var/log/minecraft/restart.log
exec >>"$LOG" 2>&1
rcon() {
mcrcon -H "$RCON_HOST" -P "$RCON_PORT" -p "$RCON_PASS" "$1" 2>/dev/null || true
}
say() {
local msg=$1
rcon "tellraw @a [{"text":"$msg","color":"yellow"}]"
}
log() { echo "[$(date -Is)] $*"; }
log "restart sequence start"
restart_at=$(( $(date +%s) + 300 ))
for mark in 300 60 10 5 4 3 2 1; do
target=$(( restart_at - mark ))
now=$(date +%s)
if [ "$target" -gt "$now" ]; then
sleep $(( target - now ))
fi
case "$mark" in
300) say "[Server] Restart in 5 minutes. Bank your loot and log out somewhere safe." ;;
60) say "[Server] Restart in 1 minute." ;;
10) say "[Server] Restart in 10 seconds." ;;
*) say "[Server] Restarting in $mark..." ;;
esac
done
log "flushing world"
rcon "save-all flush"
sleep 1
log "stopping service"
systemctl stop minecraft.service
log "starting service"
systemctl start minecraft.service
for i in $(seq 1 60); do
if rcon "list" | grep -q "There are"; then
log "ready after $i seconds"
log "restart sequence complete"
exit 0
fi
sleep 1
done
log "ERROR: server did not answer RCON within 60 seconds"
exit 1
The RCON helper here is deliberately six lines of mcrcon, which is packaged in most distributions and is trivial to build. Any RCON client works the same way, and if you run a panel such as Pterodactyl or AMP you can replace the helper with the panel API and drop mcrcon entirely. What you should not do is replace it with a fixed sleep 60 and hope. The polling loop at the bottom is what makes the word complete mean something: it sends list over RCON until the server answers, so the unit only reports success once the server is actually joinable rather than merely having a live process.
Installing and verifying the units
sudo mkdir -p /var/log/minecraft
sudo chown minecraft:minecraft /var/log/minecraft
sudo install -m 755 minecraft-restart /usr/local/bin/minecraft-restart
sudo systemctl daemon-reload
sudo systemd-analyze verify minecraft.service minecraft-restart.service minecraft-restart.timer
sudo systemctl enable --now minecraft.service
sudo systemctl enable --now minecraft-restart.timer
systemctl list-timers minecraft-restart.timer
systemctl status minecraft.service
journalctl -u minecraft-restart.service -n 50 --no-pager
On the test box systemd-analyze verify accepted all three units with no findings of our own; the only output was unrelated warnings from operating system units about CPUAccounting. The service started cleanly and the journal reported the familiar Paper line, Done (6.000s). That baseline matters, because it tells you the world loads in about six seconds before any restart scripting enters the picture.
Why Not Just Use cron?
A cron entry can stop and start a server. What it cannot do is describe the relationship between the two actions, and that gap is where data gets lost.
- No dependency ordering. systemd knows minecraft-restart.service runs after minecraft.service and that the timer is meaningless without it. cron knows nothing about either.
- No restart policy. If the JVM crashes at 15:00, systemd brings it back in ten seconds on its own. Under cron you either write a watchdog or discover the outage when a player complains.
- Weaker logging. Every run is a journal entry with an exit code, so journalctl -u minecraft-restart.service shows exactly what the countdown did and when. cron mails root, and on most servers nobody reads root mail.
- No readiness state. A timer unit that is still running is visible in systemctl. A cron job that hung on stop is invisible until the next one starts on top of it.
- No overlap protection. systemd will not start a second instance of a unit that is still active, which kills an entire category of bug on slow saves.
That is the real argument. Not that cron is bad, but that a Minecraft server already is a systemd service, so using systemd for the restart costs one extra unit file and buys ordering, logging, policy, and state for free.
Talking to Players: The Countdown That Prevents Tickets
Warnings are not decoration. They are the difference between a restart and an incident. The script above sends messages at five minutes, one minute, ten seconds, then once per second from five down to one, using tellraw with a yellow color so the text is unambiguous in chat without looking like an error or an admin announcement.
- Five minutes: the actionable warning. Players bank loot and move away from danger.
- One minute: stop opening chests, finish the trade, get out of the raid.
- Ten seconds: switch to a title or actionbar so it is impossible to miss mid-fight.
- Five to one: counts in chat, which also timestamps the shutdown in logs for later debugging.
Two practical notes from testing. First, tellraw to all players produces nothing in the console when zero players are online; that is expected behavior, not a broken script, and it makes an empty-server test look silent. Second, never let a restart collide with a scheduled event, a boss fight, or a competitive match. Publish the schedule in your MOTD and your Discord so players can plan around it, and if you must move a restart, announce the move rather than letting it happen at the old time.
Measured Results: What a Restart Actually Costs
| Test | save-all flush | stop | start to Done | Ready via RCON | Downtime |
|---|---|---|---|---|---|
| Warned T-5, T-2, T-1 | ~0.5 s | 1.04 s | 7.07 s wall (Paper: 5.971 s) | 7 s after start | ~8 s |
| Warned T-3, T-1 | 18:21:53 | 18:21:54 | 18:21:55 start, ready 18:22:02 | 7 s after start | ~8 s |
| Earlier manual run | 0.16 s | 1.04 s | 7.07 s wall | ~7 s | ~8 s |
Read those numbers carefully. The stop is consistently about one second, which is the shutdown hook writing and closing region files for 22,801 chunks. The save flush costs a fraction of a second to one second depending on how much is dirty, and sending it as a separate explicit command before the stop is what keeps the stop itself short. Start to a joinable server is about seven seconds, of which Paper attributes roughly six to world loading and the rest to JVM boot and RCON becoming available.
Total downtime around eight seconds is the headline. On a server with a five-minute warning, a player who steps away for coffee never notices the restart. That is the standard to aim for, and it is achievable without any exotic tooling.
Safety Checklist Before You Enable the Timer
Run through this list once, in order, the first time you turn the timer on.
- Flush before stopping. Send save-all flush over RCON and confirm it returned before touching systemctl.
- Stop gracefully. SIGINT through the service unit, never kill -9. A hard kill during a region write is how worlds get corrupted.
- Keep the timeout. TimeoutStopSec=120 exists so a big save can finish; lowering it to 10 s to make restarts faster defeats the safeguard.
- Take a manual backup before the first automated restart and keep it. If the world fails to load afterwards, you want a known-good copy. See How to Set Up Automated Minecraft Server Backups on Linux for a schedule that does not collide with your restart window.
- Verify the world loads. Watch the journal for the Done line and the absence of chunk or region errors, not just for a running process.
- Monitor for crash loops. If the server fails twice in a row, disable the timer before you sleep. A loop that restarts every ten minutes through the night will fill logs and can damage chunks.
- Test with a player online. Have someone stand in the world during one restart so you confirm the warnings, the countdown, and the save all behave under real conditions.
Pros and Cons of Scheduled Restarts
Pros
- Resets heap growth and fragmentation before it turns into visible lag.
- Clears plugin caches that leak references over days of uptime.
- Gives you a predictable window for plugin updates and config changes.
- Makes performance problems obvious: if lag returns in minutes, the cause is not uptime.
- Costs about eight seconds of downtime when done correctly.
Cons
- Interrupts players who did not read the schedule.
- Can hide a real leak instead of prompting a fix.
- Requires manual backup discipline around the window.
- Adds a moving part that can fail at 4 a.m. when nobody is watching.
- Gives no benefit at all to a well-behaved server with flat heap and stable MSPT.
Common Mistakes and How to Avoid Them
- Restarting too often. Hourly restarts on a twenty-player server exist to hide a problem. Measure heap after GC first; if it is flat, you do not need the restart.
- No warning. A restart without a countdown is an outage from the player perspective. Five minutes minimum, and a title in the last ten seconds.
- cron stop with no save. Stopping the JVM without a flush throws away dirty region writes and lengthens the shutdown. Send save-all flush first.
- Restarting while a backup runs. Two processes reading and writing the same region files is how you get a backup that will not restore. Space the schedules apart by at least the length of your backup job.
- Starting the next restart before the previous finishes. An overlapping run is the classic slow-save failure. A oneshot unit prevents it; cron does not.
- No readiness check. Reporting success because the process spawned tells you nothing. Poll RCON list so complete means joinable.
- Forgetting the timezone. OnCalendar uses the host timezone. A server in UTC with an American player base will restart at the worst possible hour.
If your server runs an older JVM or an unusual fork, the startup numbers will differ, but the shape of the sequence does not. Baseline it once with the JAR you actually use; the comparison in Benchmarking the Top 5 Minecraft Server JARs in 2026 shows how much startup time varies between them.
Frequently Asked Questions
Once a day during your lowest-population hour suits most servers. Restart twice a day only if you run a large public server with heavy farms and see heap grow across a long uptime. If your heap after a full GC is flat and TPS stays at 20.0, you may not need scheduled restarts at all.
No. A graceful restart saves the world first, so builds, inventories, and statistics persist. Loss happens when a server is stopped with kill -9 or lacks a save-all flush before shutdown. Send save-all flush over RCON, then stop with SIGINT, and keep a backup before your first automated run.
Not fully, because the process must stop. You can make the interruption almost invisible: warn at five minutes, one minute, and ten seconds, flush the world, stop gracefully, and start again. In testing that sequence produced about eight seconds of downtime, with roughly seven seconds of it spent loading the world.
Poll RCON with the list command instead of sleeping a fixed number of seconds. When the command returns an online-player line, the server is joinable. Wrap that poll in a loop with a timeout so the restart script reports failure if the server never answers, rather than claiming success.
Conclusion
A scheduled restart done properly is four small text files and one bash script, and it costs your players about eight seconds once a day. Done badly it is a cron line that drops players without warning and risks a half-written region file. The difference between those two outcomes is not skill, it is architecture: a service unit that owns the JVM, a oneshot unit that orchestrates the sequence, a timer that fires it, a save flush before the stop, and a readiness poll that tells the truth about when the server is joinable.
Copy the four files above, change the user, paths, RCON password, and OnCalendar time, run them against a test world, then turn the timer on. While you are in there, pair this with a backup schedule that does not overlap the restart window, and keep an eye on heap after GC in spark: the day the graph stops resetting at each restart is the day you know the restart is doing real work and not hiding a defect. Set it up, watch the first three runs in journalctl, and then forget about it, which is what a well-run server looks like.
If you want the rest of the stack tuned to match, start with Automated Minecraft Server Backups on Linux and then audit your plugins. A clean restart schedule buys you predictable maintenance windows; clean plugins are what make those windows short.

Leave a Reply