Last active
July 5, 2025 20:06
-
-
Save CraigWilsonOZ/983bd539a1bbc17a1a726e63b885a6f6 to your computer and use it in GitHub Desktop.
Minecraft: Java Edition server on Ubuntu setup script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
#============================================================================== | |
# Minecraft Java Server Setup Script | |
# | |
# Author: Craig Wilson | |
# Version: 1.2 | |
# Last Modified: 2025-06-21 | |
# | |
# Description: | |
# This script automates the setup of a Minecraft Java Edition server on a | |
# Debian-based system. It is designed to be rerunnable; it will check the | |
# system state and only perform actions that are necessary. | |
# | |
# Features: | |
# - Installs necessary dependencies. | |
# - Creates a dedicated user and directories for the server. | |
# - Downloads the Minecraft server JAR file (if not present). | |
# - Configures server properties and RCON access. | |
# - Sets up scripts for starting the server and managing backups. | |
# - Schedules a daily backup via a cron job (if not present). | |
# | |
# Security Note: | |
# The scripts 'start-service-minecraft.sh' and 'stop-service-minecraft.sh' | |
# require the '$MINECRAFT_USER' user to have sudo privileges for systemctl. | |
# | |
# Tested on: | |
# - Ubuntu 24.04 (Lunar Lobster) | |
# - Minecraft Java Edition 1.21.7 | |
# - Openjdk-21-jre-headless | |
# | |
# Readme: | |
# https://craigwilson.blog/post/2025/2025-06-21-minecraftserver/ | |
#============================================================================== | |
# --- Script Configuration and Preamble --- | |
# Exit immediately if a command exits with a non-zero status. | |
set -euo pipefail | |
# Set DEBIAN_FRONTEND to noninteractive to prevent prompts. | |
export DEBIAN_FRONTEND=noninteractive | |
# --- Function Definitions --- | |
# | |
# Performs initial checks to ensure script can run successfully. | |
# | |
initial_checks() { | |
echo "▶ Performing initial checks..." | |
# Check for root privileges | |
if [ "$EUID" -ne 0 ]; then | |
echo "ERROR: Please run this script as root or using sudo." >&2 | |
exit 1 | |
fi | |
} | |
# | |
# Displays the help message for the script. | |
# | |
display_help() { | |
cat << EOF | |
Minecraft Java Server Setup Script - v1.2 | |
This script automates the setup of a Minecraft Java Edition server on a Debian-based system. | |
It is rerunnable and will skip steps that are already completed. | |
Usage: | |
sudo ./setup-minecraft.sh | |
Options: | |
-h, --help Display this help message and exit. | |
The script must be run with root (sudo) privileges. All server configuration | |
can be adjusted in the '== Configuration ==' section of the script file. | |
EOF | |
} | |
# === Configuration === | |
MINECRAFT_USER="minecraft" | |
INSTALL_DIR="/opt/minecraft" | |
SERVER_DIR="$INSTALL_DIR/server" | |
TOOLS_DIR="$INSTALL_DIR/tools" | |
LOGS="$INSTALL_DIR/logs" | |
BACKUP_DIR="$INSTALL_DIR/backup" | |
MCRCON_DIR="$TOOLS_DIR/mcrcon" | |
MINECRAFT_JAR_URL="https://piston-data.mojang.com/v1/objects/05e4b48fbc01f0385adb74bcff9751d34552486c/server.jar" | |
# Addtional server.jar files can be found at | |
# https://gist.github.com/cliffano/77a982a7503669c3e1acb0a0cf6127e9 | |
JAR_NAME="server.jar" | |
PASSWORD=$(date +%s | sha256sum | base64 | head -c 32 ; echo) | |
# Memory settings for the server. | |
# Adjust these values based on your system's available RAM. | |
MAX_MEMORY="8192M" # Maximum Heap memory allocation for the server | |
MIN_MEMORY="8192M" # Initial memory allocation for the server | |
# Minecraft and RCON ports. | |
MINECRAFT_PORT="25565" | |
RCON_PORT="25575" | |
# Maximum number of players allowed on the server. | |
MAX_PLAYERS="20" | |
# Open JDK version to use. | |
# Ensure this matches the installed version on your system. | |
JDK_VERSION="openjdk-21-jre-headless" | |
# --- Script Execution --- | |
# Parse command-line options for help flag | |
if [[ "${1-}" =~ ^(-h|--help)$ ]]; then | |
display_help | |
exit 0 | |
fi | |
# Call the initial checks function. | |
initial_checks | |
echo "[+] Starting Minecraft Java Server Setup" | |
# === Install Dependencies === | |
echo "▶ Installing dependencies..." | |
sudo apt-get update | |
sudo apt-get install -y $JDK_VERSION git build-essential cron | |
# === Create User and Directories === | |
echo "▶ Ensuring user and directories exist..." | |
# Create the user; the || true part prevents errors on reruns | |
sudo useradd -r -m -U -d "$INSTALL_DIR" -s /bin/bash "$MINECRAFT_USER" || echo "✔ User '$MINECRAFT_USER' already exists." | |
# Create the main directories | |
sudo mkdir -p "$SERVER_DIR" "$TOOLS_DIR" "$BACKUP_DIR" | |
# NEW: Create a symbolic link for the logs | |
# First, remove the logs directory if it exists as a normal directory | |
if [ -d "$LOGS" ] && [ ! -L "$LOGS" ]; then | |
sudo rmdir "$LOGS" | |
fi | |
# Then, link the server's default log directory to our desired location | |
if [ ! -e "$LOGS" ]; then | |
echo "▶ Linking server logs directory..." | |
# The server will create /opt/minecraft/server/logs, so we link it to /opt/minecraft/logs | |
sudo -u "$MINECRAFT_USER" ln -s "$SERVER_DIR/logs" "$LOGS" | |
fi | |
# Set final ownership | |
sudo chown -R "$MINECRAFT_USER":"$MINECRAFT_USER" "$INSTALL_DIR" | |
# We use -h to change the ownership of the link itself, not what it points to | |
sudo chown -h "$MINECRAFT_USER":"$MINECRAFT_USER" "$LOGS" | |
# === Download Minecraft Server === | |
if [ ! -f "$SERVER_DIR/$JAR_NAME" ]; then | |
echo "▶ Downloading Minecraft server JAR..." | |
sudo -u "$MINECRAFT_USER" wget -O "$SERVER_DIR/$JAR_NAME" "$MINECRAFT_JAR_URL" | |
else | |
echo "✔ Server JAR already exists. Skipping download." | |
fi | |
# === Accept EULA === | |
echo "▶ Ensuring EULA is accepted..." | |
sudo -u "$MINECRAFT_USER" tee "$SERVER_DIR/eula.txt" > /dev/null <<EOF | |
eula=true | |
EOF | |
# === Create Server Properties === | |
echo "▶ Creating/updating server.properties..." | |
sudo -u "$MINECRAFT_USER" tee "$SERVER_DIR/server.properties" > /dev/null <<EOF | |
#Minecraft server properties | |
accepts-transfers=false | |
allow-flight=false | |
allow-nether=true | |
broadcast-console-to-ops=true | |
broadcast-rcon-to-ops=true | |
bug-report-link= | |
difficulty=easy | |
enable-command-block=false | |
enable-jmx-monitoring=false | |
enable-query=false | |
enable-rcon=true | |
enable-status=true | |
enforce-secure-profile=true | |
enforce-whitelist=false | |
entity-broadcast-range-percentage=100 | |
force-gamemode=false | |
function-permission-level=2 | |
gamemode=survival | |
generate-structures=true | |
generator-settings={} | |
hardcore=false | |
hide-online-players=false | |
initial-disabled-packs= | |
initial-enabled-packs=vanilla | |
level-name=world | |
level-seed= | |
level-type=minecraft\:normal | |
log-ips=true | |
max-chained-neighbor-updates=1000000 | |
max-players=$MAX_PLAYERS | |
max-tick-time=60000 | |
max-world-size=29999984 | |
motd=A Minecraft Server | |
network-compression-threshold=256 | |
online-mode=true | |
op-permission-level=4 | |
pause-when-empty-seconds=60 | |
player-idle-timeout=0 | |
prevent-proxy-connections=false | |
pvp=true | |
query.port=$MINECRAFT_PORT | |
rate-limit=0 | |
rcon.password=$PASSWORD | |
rcon.port=$RCON_PORT | |
region-file-compression=deflate | |
require-resource-pack=false | |
resource-pack= | |
resource-pack-id= | |
resource-pack-prompt= | |
resource-pack-sha1= | |
server-ip= | |
server-port=$MINECRAFT_PORT | |
simulation-distance=10 | |
spawn-monsters=true | |
spawn-protection=16 | |
sync-chunk-writes=true | |
text-filtering-config= | |
text-filtering-version=0 | |
use-native-transport=true | |
view-distance=10 | |
white-list=false | |
EOF | |
# === Install and Build MCRCON === | |
if [ ! -d "$MCRCON_DIR" ]; then | |
echo "▶ Cloning and building mcrcon..." | |
sudo -u "$MINECRAFT_USER" git clone https://github.com/Tiiffi/mcrcon.git "$MCRCON_DIR" | |
sudo -u "$MINECRAFT_USER" bash -c "cd '$MCRCON_DIR' && gcc -std=gnu11 -pedantic -Wall -Wextra -O2 -s -o mcrcon mcrcon.c" | |
else | |
echo "✔ mcrcon directory already exists. Skipping clone and build." | |
fi | |
# === Create Helper Scripts === | |
echo "▶ Creating/updating helper scripts..." | |
sudo -u "$MINECRAFT_USER" tee "$TOOLS_DIR/connect-mcrcon.sh" > /dev/null <<EOF | |
#!/bin/bash | |
$MCRCON_DIR/mcrcon -H 127.0.0.1 -P $RCON_PORT -p "$PASSWORD" -t | |
EOF | |
sudo -u "$MINECRAFT_USER" tee "$TOOLS_DIR/backup-minecraft.sh" > /dev/null <<EOF | |
#!/bin/bash | |
function rcon { | |
"$MCRCON_DIR/mcrcon" -H 127.0.0.1 -P $RCON_PORT -p "$PASSWORD" "\$1" | |
} | |
echo "Running Minecraft backup..." | |
rcon "save-off" | |
rcon "save-all" | |
sleep 10 # Give the server a moment to save | |
tar -cvpzf "$BACKUP_DIR/server-\$(date +%F-%H-%M).tar.gz" "$SERVER_DIR" | |
rcon "save-on" | |
echo "Backup complete." | |
# Delete older backups | |
find "$BACKUP_DIR" -type f -mtime +31 -name '*.gz' -delete | |
EOF | |
sudo -u "$MINECRAFT_USER" tee "$TOOLS_DIR/start-minecraft.sh" > /dev/null <<EOF | |
#!/bin/bash | |
cd "$SERVER_DIR" || exit | |
/usr/bin/java -Xmx$MAX_MEMORY -Xms$MIN_MEMORY -jar "$JAR_NAME" nogui | |
EOF | |
sudo -u "$MINECRAFT_USER" tee "$TOOLS_DIR/start-service-minecraft.sh" > /dev/null <<EOF | |
#!/bin/bash | |
sudo systemctl start minecraft | |
EOF | |
sudo -u "$MINECRAFT_USER" tee "$TOOLS_DIR/stop-service-minecraft.sh" > /dev/null <<EOF | |
#!/bin/bash | |
sudo systemctl stop minecraft | |
EOF | |
# Make all scripts in the tools directory executable | |
sudo chmod +x $TOOLS_DIR/*.sh | |
# === Create Backup Cron Job === | |
CRON_JOB="0 3 * * * $TOOLS_DIR/backup-minecraft.sh >> $LOGS/backup.log 2>&1" | |
if ! sudo -u "$MINECRAFT_USER" crontab -l | grep -Fq "$CRON_JOB"; then | |
echo "▶ Creating daily backup cron job..." | |
(sudo -u "$MINECRAFT_USER" crontab -l 2>/dev/null || true; echo "$CRON_JOB") | sudo -u "$MINECRAFT_USER" crontab - | |
else | |
echo "✔ Backup cron job already exists. Skipping." | |
fi | |
# === Create Systemd Service === | |
echo "▶ Creating/updating systemd service file..." | |
sudo tee /etc/systemd/system/minecraft.service > /dev/null <<EOF | |
[Unit] | |
Description=Minecraft Java Server | |
After=network.target | |
[Service] | |
User=$MINECRAFT_USER | |
WorkingDirectory=$SERVER_DIR | |
ExecStart=/usr/bin/java -Xmx$MAX_MEMORY -Xms$MIN_MEMORY -jar $SERVER_DIR/$JAR_NAME nogui | |
ExecStop=$MCRCON_DIR/mcrcon -H 127.0.0.1 -P $RCON_PORT -p $PASSWORD stop | |
Restart=on-failure | |
SuccessExitStatus=0 1 | |
[Install] | |
WantedBy=multi-user.target | |
EOF | |
# === Enable and Start Service (Safely) === | |
sudo systemctl daemon-reload | |
if ! sudo systemctl is-active --quiet minecraft; then | |
echo "▶ Starting and enabling the minecraft service..." | |
sudo systemctl enable --now minecraft | |
else | |
echo "✔ Minecraft service is already active. Ensuring it is enabled on boot." | |
sudo systemctl enable minecraft | |
echo "[!] The service definition or server properties may have changed. Run 'sudo systemctl restart minecraft' to apply all changes." | |
fi | |
echo "" | |
echo "[+] Setup Complete." | |
echo "[*] To connect to the server console, run: $TOOLS_DIR/connect-mcrcon.sh" | |
echo "[*] Your RCON password is: $PASSWORD" | |
echo "" | |
echo "### Security Note ###" | |
echo "To use the 'start/stop-service-minecraft.sh' scripts, you must grant sudo privileges." | |
echo "Run 'sudo visudo' and add the following line:" | |
echo "$MINECRAFT_USER ALL=(ALL) NOPASSWD: /usr/bin/systemctl start minecraft, /usr/bin/systemctl stop minecraft" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment