#!/bin/bash clear -x echo "This script should be run as root on a clean install of a Debian x64 system." echo "You can choose to do a full install, or update a server which was previously" echo "installed with the same script." echo -e "\nThis script does: create a user and a script to start the server." echo -e "\nThis script is able to: update existing servers, install Java, download the" echo "Fabric installer, install a Minecraft server, create a systemd unit with a" echo "socket, and create a cmd.sh script and 'cmd' command to run console commands." echo -e "\nNOTICE: The Minecraft Fabric installer supports versions from 1.14 to latest." echo " For another Minecraft version, download the desired server installer" echo " file manually before running this script." echo "NOTICE: The available options for Java are: 8, 16, 17, and 21." echo " For another Java version, install it manually." while true; do echo "" read -p " Are you ready to proceed? [Y/n] " proceed case $proceed in [yY]|"" ) break;; [nN] ) echo "=> Exiting."; exit;; * ) echo "=> Invalid response.";; esac done echo -e "\n=> Running compatibility tests..." echo -n "=> Checking curl... " curl --version &> /dev/null && echo "PASS" || { test1="failed"; echo "FAIL"; } echo -n "=> Checking systemd... " systemd-notify --booted && echo "PASS" || { test2="failed"; echo "FAIL"; } echo -n "=> Checking root privileges... " if [ "$EUID" == 0 ]; then echo "PASS"; else { test3="failed"; echo "FAIL"; } fi echo -n "=> Checking /etc/systemd/system/ permissions... " touch /etc/systemd/system/permission.test &> /dev/null && rm /etc/systemd/system/permission.test &> /dev/null && echo "PASS" || { test4="failed"; echo "FAIL"; } echo -n "=> Checking internet connection... " ping -c 1 8.8.8.8 &> /dev/null && echo "PASS" || { test5="failed"; echo "FAIL"; } echo -n "=> Checking DNS resolution... " ping -c 1 google.com &> /dev/null&& echo "PASS" || { test6="failed"; echo "FAIL"; } if [ "$test1" == "failed" ]; then echo "WARNING: curl command error, downloads will fail." testFailed="true"; fi if [ "$test2" == "failed" ]; then echo "WARNING: this system is not using systemd, please skip creating unit files." testFailed="true"; fi if [ "$test3" == "failed" ]; then echo "WARNING: root privileges are not detected, please run again as root." testFailed="true"; fi if [ "$test4" == "failed" ]; then echo "WARNING: cannot write to systemd directory, please skip creating unit files." testFailed="true"; fi if [ "$test5" == "failed" ]; then echo "WARNING: cannot connect to the internet, downloads will fail." testFailed="true"; fi if [ "$test6" == "failed" ]; then echo "WARNING: cannot resolve domain names, downloads will fail." testFailed="true"; fi if [ "$testFailed" == "true" ]; then while true; do echo "" read -p " At least one check failed, are you sure you want to proceed? [y/N] " skipwarn case $skipwarn in [yY] ) echo "=> Ignoring warnings..."; break;; [nN]|"" ) echo "=> Exiting."; exit;; * ) echo "=> Invalid response.";; esac done else echo -e "\n=> All checks complete." fi echo -e "\nChoose to install a new server or update an existing one." echo "WARNING: Only update an existing server if it was initially installed using this" echo " script, or if you know that it's been set up in the same way." echo "NOTICE: This will replace any existing /home/minecraft-old/ directory." while true; do echo "" read -p " Update an existing server? [y/N] " update case $update in [yY] ) update="true" echo "=> Updating..." break;; [nN]|"" ) echo "=> Skipping..." break;; * ) echo "=> Invalid response.";; esac done if [ "$update" == "true" ]; then echo -n " -> Removing previous minecraft-old directory... " rm -rf /home/minecraft-old/ && echo "DONE" echo -n " -> Backing up current minecraft home directory... " mv /home/minecraft/ /home/minecraft-old/ && echo "DONE" echo -n " -> Creating new minecraft home directory... " mkdir -p /home/minecraft && echo "DONE" echo -n " -> Copying world... " cp -R /home/minecraft-old/world/ /home/minecraft/world/ && echo "DONE" echo -n " -> Copying mods... " cp -R /home/minecraft-old/mods/ /home/minecraft/mods/ && echo "DONE" echo -n " -> Copying config... " cp -R /home/minecraft-old/config/ /home/minecraft/config/ && echo "DONE" echo -n " -> Copying defaultconfigs... " cp -R /home/minecraft-old/defaultconfigs/ /home/minecraft/defaultconfigs/ && echo "DONE" echo -n " -> Copying properties and player lists... " cp /home/minecraft-old/banned-ips.json /home/minecraft-old/banned-players.json /home/minecraft-old/ops.json /home/minecraft-old/server.properties /home/minecraft-old/whitelist.json /home/minecraft/ && echo "DONE" echo "=> Finished copying data." echo "NOTICE: After completing the installation, please check for other config files" echo " that you may want to keep such as mod-specific folders, logs, etcetera." echo "NOTICE: Mods must be updated manually, please do so before starting the server." else echo -e "\n=> Creating 'minecraft' user..." mkdir -p /home/minecraft useradd -d /home/minecraft/ minecraft &> /dev/null && { echo " -> Set a password for the user:" passwd minecraft } fi cd /home/minecraft/ echo -e "\nChoose a valid version of Java." echo "Minecraft requires Java 8 for 1.16.5 and earlier, Java 16 for 1.17-1.17.1," echo "Java 17 for 1.18-1.20.4, and Java 21 for 1.20.5 and later." while true; do echo "" read -p " Enter a valid version (ENTER to skip): " java case $java in "" ) echo "=> Skipping installation..." break;; * ) if [[ "$java" =~ ^[0-9]+$ ]]; then echo "=> Installing Java $java (Adoptium Eclipse Temurin)..." echo " -> Installing dependencies..." apt install -y wget apt-transport-https gpg echo " -> Installing GPG key..." wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | tee /etc/apt/trusted.gpg.d/adoptium.gpg > /dev/null echo " -> Adding apt repository..." echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list echo " -> Updating repositories..." apt update -y echo " -> Installing temurin-$java-jdk..." apt install -y temurin-$java-jdk && { echo "=> Installed Adoptium Eclipse Temurin $java." break } || { echo "=> Failed to install temurin-$java-jdk." } else echo "=> Invalid response." fi ;; esac done echo -e "\nChoose if you want to use the Fabric server installer." echo "The Fabric installer supports Minecraft 1.14 to latest." while true; do echo "" read -p " Use Fabric server installer? [Y/n] " method case $method in [yY]|"" ) install="true" method="fabric" break;; [nN] ) echo "=> Skipping Fabric installer..." break;; * ) echo "=> Invalid response.";; esac done if [ "$install" != "true" ]; then echo -e "\nChoose if you want to use an existing server installer." echo "This requires an existing and accessible .jar file." while true; do echo "" read -p " Use other server installer? [Y/n] " method case $method in [yY]|"" ) install="true" method="manual" break;; [nN] ) echo "=> Skipping manual installation..." break;; * ) echo "=> Invalid response.";; esac done fi if [ "$install" == "true" ]; then if [ "$method" == "fabric" ]; then echo "=> Downloading Fabric installer..." curl -s -o 'fabric-installer.jar' https://maven.fabricmc.net/net/fabricmc/fabric-installer/1.0.1/fabric-installer-1.0.1.jar while true; do echo "" read -p " Select valid Minecraft version to install (ENTER for latest): " mc case $mc in "" ) mc="latest" echo "=> Installing latest Fabric server..." java -jar fabric-installer.jar server -downloadMinecraft && break;; * ) echo "=> Installing $mc Fabric server..." java -jar fabric-installer.jar server -mcversion $mc -downloadMinecraft && break || echo "=> Invalid version.";; esac done echo "=> Installed $mc Fabric server." echo "=> Removing installer..." rm fabric-installer.jar elif [ "$method" == "manual" ]; then echo -e "\nEnter the server installer file path." echo "Working directory is /home/minecraft/ for relative file paths." while true; do echo "" read -p " Enter the server installer path: " serverjar if [ ! -f $serverjar ]; then echo "=> File not found." else echo "=> Found file." break fi done echo -e "\nChoose server installer arguments." echo "Forge should use 'nogui --installServer' args." echo "You can use '--nogui' on newer versions." while true; do echo "" read -p " Enter any installer arguments (ENTER for 'nogui'): " serverArgs case $serverArgs in "" ) serverArgs="nogui" esac echo "=> Installing server..." java -jar $serverjar $serverArgs && { echo "=> Installed server." break } || { echo "=> Failed to install server." } done echo "=> Removing installer..." rm $serverjar fi echo "=> Running server once to generate files..." if [ "$method" == "fabric" ]; then runjar="fabric-server-launch.jar" elif [ "$method" == "manual" ]; then while true; do echo "" read -p " Enter the .jar file that runs the server (ENTER for *.jar): " runjar case $runjar in "" ) runjar=$(ls *.jar | head -1);; esac if [ ! -f $runjar ]; then echo " -> File not found." else break fi done echo " -> Selected $runjar" echo " -> Running server..." fi java -jar $runjar nogui echo "=> Server files created." while true; do echo "" read -p " Agree to the EULA? [Y/n] " eula case $eula in [yY]|"" ) if [ ! -f eula.txt ]; then echo "eula=true" > eula.txt else sed -i '3s/.*/eula=true/' eula.txt fi echo "=> EULA set to true." break;; [nN] ) echo "=> Skipping..." break;; * ) echo "=> Invalid response.";; esac done fi echo "=> Creating run script with Java arguments..." touch /home/minecraft/run.sh chmod +x /home/minecraft/run.sh echo -e "\nChoose allocated server memory in GB" echo "You should leave roughly 1-2GB available for the host. E.g. use up to 7GB if" echo "the system has 8GB. I recommend using 3-6 for best performance, and avoiding >8" echo "unless there are many players." while true; do echo "" read -p " Enter allocated memory [1-24] (ENTER to use 4GB): " mem case $mem in [1-9]|1[0-9]|2[0-4] ) echo "=> Allocating ${mem}GB..." break;; "" ) echo "=> Allocating 4GB..." mem="4" break;; * ) echo "=> Invalid response.";; esac done if [ ! "$runjar" ]; then echo -e "\n=> No .jar file selected to run server." while true; do echo "" read -p " Enter the .jar file that runs the server (ENTER for *.jar): " runjar case $runjar in "" ) runjar="*.jar";; esac if [ ! -f $runjar ]; then echo " -> File not found." else break fi done fi cat > /home/minecraft/run.sh << EOF java -Xmx${mem}G -Xms${mem}G -XX:+UseG1GC -Dsun.rmi.dgc.server.gcInterval=2147483646 -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=20 -XX:G1ReservePercent=20 -XX:MaxGCPauseMillis=50 -XX:G1HeapRegionSize=32M -jar $runjar nogui EOF echo "=> Created run script in home directory." if [ "$update" != "true" ]; then echo -e "\nChoose to set up autostart by creating systemd unit files." echo "This would allow you to start/stop the server with systemctl, view logs with" echo "journalctl, and send commands to Minecraft's server console in the background." while true; do echo "" read -p " Do you want to set up a systemd unit? [Y/n] " sysd case $sysd in [yY]|"" ) sysd="install" break;; [nN] ) sysd="skip" echo "=> Skipping..." break;; * ) echo "=> Invalid response.";; esac done fi if [ "$sysd" == "install" ]; then echo "=> Creating systemd unit files..." cat > /etc/systemd/system/minecraft.service << EOF [Unit] Description=A dedicated Minecraft server After=network.target [Service] User=minecraft Type=simple KillSignal=SIGCONT Sockets=minecraft.socket WorkingDirectory=/home/minecraft/ ExecStart=/bin/sh -c "/home/minecraft/run.sh < /run/minecraft.control" ExecStop=/bin/sh -c "echo '/stop' > /run/minecraft.control" Type=exec Restart=on-failure [Install] WantedBy=multi-user.target EOF cat > /etc/systemd/system/minecraft.socket << EOF [Unit] BindsTo=minecraft.service [Socket] ListenFIFO=/run/minecraft.control FileDescriptorName=control RemoveOnStop=true SocketMode=0660 SocketUser=minecraft EOF echo "=> Created systemd unit files." systemctl enable minecraft.service &> /dev/null echo "=> Enabled systemd service." echo "=> The server is set up to autostart and restart automatically." echo "=> Creating cmd script in home directory..." cat > /home/minecraft/cmd.sh << EOF #!/bin/bash sendCommand() { sleep 0.125 echo "\$*" > /run/minecraft.control } sendCommand "\$*" & journalctl -o cat -fn 0 -u minecraft.service EOF chmod +x /home/minecraft/cmd.sh echo "=> Created console script in home directory." echo "INFO: The console script passes all arguments to the server, and returns the" echo " output until stopped with Ctrl+C." echo -e "\nChoose to install a cmd.sh link in /usr/bin/." echo "Creates a link to /home/minecraft/cmd.sh, allowing you to run server commands" echo "more easily using just 'cmd'. E.g. 'cmd /gamerule keepInventory true'" while true; do echo "" read -p " Add cmd.sh link at /usr/bin/cmd? [Y/n] " cmd case $cmd in [yY]|"" ) if which cmd &> /dev/null; then cmd="false" echo "=> Command 'cmd' already exists. Skipping..." break else cmd="true" ln -s /home/minecraft/cmd.sh /usr/bin/cmd echo "=> Created 'cmd' link." break fi ;; [nN] ) cmd="false" echo "=> Skipping..." break;; * ) echo "=> Invalid response.";; esac done fi chown -R minecraft /home/minecraft echo "=> User 'minecraft' now owns all files in home directory." if [ "$install" == "true" ]; then echo -e "\nInstallation complete, server is not running." echo "NOTICE: Please configure server.properties before starting the server." if [ "$fabric" == "install" ]; then echo "NOTICE: It's recommended to download performance mods such as Lithium," echo " FerriteCore, and ModernFix." fi if [ "$sysd" == "install" ]; then echo -e "\nSystemd is in use, the server will automatically start on boot." echo "NOTICE: The server can be stopped by using either systemd, the /stop command," echo " or shutting down the host. Each method performs a clean shutdown, so the" echo " server won't lose data. You may notice a delay shutting down the host as" echo " the Minecraft server stops." echo -e "\nMany commands are available with systemd, some important ones are:" echo "Executing commands in the Minecraft server console using a script:" echo " $ ./cmd.sh " if [ "$cmd" == "true" ]; then echo "Or by using the cmd command:" echo " # cmd " fi echo "View the server logs with journalctl: (Shift+G jumps to the end)" echo " $ journalctl -u minecraft" echo "Or follow them as they update with:" echo " $ journalctl -fu minecraft" echo "And easily perform a clean start/stop of the server with:" echo " # systemctl {start,stop,restart} minecraft" else echo -e "\nStart the server with the run script, or set up a user crontab for autostart." echo " $ ./run.sh" fi else echo "=> Finished." fi