Skip to main content
start.sh is the process that actually runs inside the tmux session. Its primary job is to run the Minecraft server and automatically restart it if it crashes — unless the shutdown was intentional.
You do not normally invoke start.sh directly. Use serv start instead, which sets up the tmux session, exports the necessary environment variables, and runs start.sh inside it.

How the loop works

The script registers a SIGINT/SIGTERM signal trap and runs in a while loop that continues as long as a keep_running flag is true. Each iteration of the loop:
  1. Checks for a stop_signal file before launching. If found, sets keep_running=false and removes the file — server does not start.
  2. Launches the Java process and blocks until it exits.
  3. Checks for stop_signal again immediately after the process exits.
  4. If keep_running is still true, waits 5 seconds, checks stop_signal one final time, then restarts.
# Trap SIGINT (Ctrl+C) and SIGTERM signals
trap handle_shutdown SIGINT SIGTERM

while $keep_running; do
    # Check for stop signal file
    if [ -f "stop_signal" ]; then
        echo "Stop signal file detected. Server will not restart after stopping."
        keep_running=false
        rm -f stop_signal
    fi

    java -Xmx3072M -Xms2048M -XX:+UseG1GC -XX:MaxGCPauseMillis=50 \
        -Dlog4j.configurationFile=log4j2.xml \
        -jar $SERVER_JAR nogui

    # Check again after server stops
    if [ -f "stop_signal" ]; then
        echo "Stop signal file detected. Server will not restart."
        keep_running=false
        rm -f stop_signal
    fi

    if $keep_running; then
        echo "Server restarting in 5 seconds..."
        echo "Press CTRL+C to prevent restart."
        sleep 5
        # Check one more time before restarting
        if [ -f "stop_signal" ]; then
            echo "Stop signal file detected. Server will not restart."
            keep_running=false
            rm -f stop_signal
        fi
    fi
done

echo "Server shutdown complete."

The stop_signal file

The stop_signal file is the mechanism that differentiates an intentional stop from a crash. The script also traps SIGINT and SIGTERM — pressing Ctrl+C inside the tmux session sets keep_running=false without needing the file.
EventWhat happens
serv stopCreates stop_signal, sends stop to the console — loop exits cleanly, no restart
Server crashNo stop_signal is created — loop detects the process exited, waits 5 seconds, restarts
serv restartSends stop to the console without creating stop_signal — loop restarts the server automatically
Ctrl+C in tmuxSIGINT trap sets keep_running=false — loop exits after the current server run finishes
This means a crash at any time — including during a snapshot window — will trigger an automatic restart within 5 seconds, unless serv stop was called first.

JVM flags

The server is launched with the following JVM arguments:
FlagValuePurpose
-Xmx3072MMaximum heap size (3 GB)
-Xms2048MInitial heap size (2 GB)
-XX:+UseG1GCUse the G1 garbage collector
-XX:MaxGCPauseMillis50Target maximum GC pause time of 50 ms
-Dlog4j.configurationFilelog4j2.xmlUse the custom log4j config to suppress noisy output
The jar is launched with the nogui flag, which disables the Swing GUI that would otherwise fail in a headless environment.

tmux session details

SettingValue
Session nameminecraft
tmux socket/tmp/minecraft-tmux
Working directory/usr/local/games/minecraft_server/java
Error log/tmp/minecraft_error.log
The socket is created with permissions 770 after the session is started, so users in the owning group can attach without root.

Graceful stop vs crash

Graceful stop via serv stop:
Stop signal file detected. Server will not restart after stopping.
Server shutdown complete.
Crash recovery:
Server restarting in 5 seconds...
Press CTRL+C to prevent restart.
The loop will then re-execute the java command and the server comes back up automatically. The tmux session and the loop itself remain alive throughout, so serv console continues to work even between restarts.