8

I've set up a systemd service to run my Minecraft server. Now, I need it to repeat the start script when the server crashes. Here's my code so far:

#!/bin/bash

while true; do
    cd /home/mcserver/Spigot
    echo Starting Spigot...
    echo 600 > ./restart-info
    java -jar spigot.jar
    echo Server has stopped or crashed, starting again in 5 minutes...
    sleep 300
done

I can actually view the output of spigot.jar using systemctl status spigot, but I also want to control the server console, maybe using screen.

When I try to do this:

screen -S "Spigot" java -jar spigot.jar

I'll get the Must be connected to a terminal error. This command only works in a terminal (not in a script) and I can attach it using screen -r.

Is there any way to "bypass" this screen bug? I already tried to place script /dev/null before the screen command. I don't want to use screen with -d and -m because it'll run in the background and the script will keep restarting my server.

4
  • 1
    Why not use systemds restart feature?
    – Panki
    Commented Jul 13, 2020 at 9:00
  • I'm not sure if restart helps... I want the script to wait until java -jar spigot.jar stops. And I also want to run the jar file in screen so I can reattach to it at anytime.
    – adazem009
    Commented Jul 13, 2020 at 9:03
  • Why do you need to reattach? You can read the output with journalctl. Or do you need to interact with it?
    – Panki
    Commented Jul 13, 2020 at 9:04
  • Yes, I need to interact with it and read the output from systemctl status spigot.service
    – adazem009
    Commented Jul 13, 2020 at 9:05

4 Answers 4

24

I suspect you've stumbled on this blog post which uses screen to solve a problem where your minecraft server stops when you $ java -jar spigot.jar, then close your ssh or putty session. That method seems to have become the cannonical answer as to how to run a minecraft server, even though it isn't necessary.

systemd is a totally different (and better) solution to this problem, circumventing the need for screen. You can achieve everything you've done in your script with systemd service options.


To run a vanilla minecraft server, create /etc/systemd/system/minecraft.service with this content:

[Unit]
Description=Minecraft Server

[Service]
Type=simple
WorkingDirectory=/home/minecraft
ExecStart=java -Xmx1024M -Xms1024M -jar /home/minecraft/server.jar nogui
User=minecraft
Restart=on-failure

[Install]
WantedBy=multi-user.target

Set it to launch automatically after boot with systemctl enable minecraft.

You asked about how to control it:

$ sudo systemctl start minecraft   # Starts the service if it wasn't running
$ sudo systemctl stop minecraft    # Stops the service
$ sudo systemctl restart minecraft # Restarts the service
$ sudo systemctl status minecraft  # Find out how the service is doing
$ sudo journalctl -u minecraft -f  # Monitor the logs

This does everything except give you a means to send commands to the console. To do that, we'll set up a file that the server will listen to where you can write your commands by creating the following systemd units:

/etc/systemd/system/minecraft.socket:

[Unit]
PartOf=minecraft.service

[Socket]
ListenFIFO=%t/minecraft.stdin

and /etc/systemd/system/minecraft.service:

[Unit]
Description=Minecraft Server

[Service]
Type=simple
WorkingDirectory=/home/minecraft
ExecStart=java -Xmx1024M -Xms1024M -jar /home/minecraft/server.jar nogui
User=minecraft
Restart=on-failure
Sockets=minecraft.socket
StandardInput=socket
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Now you can send console commands by echoing stuff into that file:

echo "help" > /run/minecraft.stdin
echo "/stop" > /run/minecraft.stdin

What's also cool is that you can make your own custom sequences of commands and cat the entire file into the console. For example, if you play UHC, you can start a new world, have people log-in, then cat uhc.commands > /run/minecraft.stdin to set the gamerules, spread the players, and start the event.

9
  • 3
    What a fantastic answer! Finally an elegant (and standard) solution to the server's stdin problem!
    – MestreLion
    Commented Oct 27, 2020 at 15:18
  • This solves a major nuisance for me which I spent many hours trying to clear-up. Any way I tried to do the service unit with screen or tmux, while it seemed to shut down OK (the six "All chunks are saved" were there), following the log, I encountered multiple exceptions and stack trace messages from the jline console-handling package at shutdown. When I do it this way, without any multiplexer, the shutdown looks nice and clean. Commented Apr 8, 2021 at 3:34
  • I'm not running Minecraft, but something in the same line, and I was wondering... can I have both stdin and stdout on a systemd socket (possibly two, I guess...) and then naively attach to those two sockets and expect to have an interactive pseudoTTY? :-) Commented Oct 19, 2021 at 15:28
  • 1
    Replying to myself: one possibility of doing that is using a 'helper' command such as screen to do the magic: unix.stackexchange.com/a/454098/102206 Commented Oct 19, 2021 at 17:41
  • 1
    You can add ExecStop=echo "/stop" > /run/minecraft.stdin to stop the server gracefully and make sure everything is saved. Commented Feb 22 at 22:06
4

I don't want to use screen with -d and -m because it'll run in the background and the script will keep restarting my server.

So use -D instead of -d (note the capital case!)

-D -m This also starts screen in "detached" mode, but doesn't fork a new process. The command exits if the session terminates.

As screen won't fork a new process, it will block while java is running, and exit when the server stops.

As a bonus, if you add -S spigot you can monitor the output (and even send commands!) from any terminal by attaching to that screen

And... since you're using a systemd service anyway (and you should indeed), why are you doing this restart loop in your script? Let systemd handle that for you using Restart=always and RestartSec=5min

3
  • Thanks for your answer. I'll try this today and let you know. :)
    – adazem009
    Commented Oct 1, 2020 at 8:17
  • This is all I need for the server right now. Thanks again!
    – adazem009
    Commented Oct 1, 2020 at 14:18
  • -D was best answer for me.thanks. Commented Aug 27, 2022 at 1:56
0

I've found a solution for this, but I won't be able to view output using systemctl status spigot.

#!/bin/bash

while true; do
    cd /home/mcserver/Spigot
    echo Starting Spigot...
    screen -d -m -S Spigot java -jar spigot.jar
    SCREEN_NAME="Spigot"
    while screen -list | grep -q $SCREEN_NAME
    do
        sleep 1
    done
    echo Server has stopped, starting again in 5 minutes...
    sleep 300
done

This script will wait until the screen session terminates. I can now view the output or interact with the console using screen -r or if I have multiple screen sessions running, I can attach using screen -D -R "Spigot".

0

To continue the idea of just using systemd, because I found this when my screen script broke, I wrote a service on Ubuntu 24.04 that is a drop in (with editing) replacement. With the same function as the screen script, and multiple java version support.

#
# MineCraft Server managment systmed service. Screen Not Required!
#
# Replace <version> with your server version, No . or spaces! Name the service,
# MCS-<version>@.service Eg; [email protected]
# Replace <instance> with the name of the folder for your server in /opt/minecraft
# !!!BE SURE TO  READ THE ExecStart Note!!!
# As root cretae a file '/etc/systemd/system/MCS-<version>@.socket' and place these entries in it,
# [Unit]
# PartOf=MCS-<version>.service
#
# [Socket]
# ListenFIFO=%t/MCS-<version>@.stdin
#
# ~~~~Service Controls~~~~~~
# Enable or Disable Autostart
# sudo systemctl <enable/disable> MCS-<version>@<instance>
#
# sudo systemctl startMCS-<version>@<instance>      # Starts the service if it wasn't running
# sudo systemctl stop MCS-<version>@<instance>      # Stops the service
# sudo systemctl restart MCS-<version>@<instance>>  # Restarts the service
# !!! ONLY WORKS HERE !!!
# echo "<comand>" > /run/ MCS-<version>@.stdin      # Send commands to the console
#
# !!! As a member of the mincraft group. ctl+C to exit !!!
# systemctl status MCS-<version>@<instance>         # Find out how the service is doing
# journalctl -u MCS-<version>@<instance>> -f        # Monitor the logs 
#

[Unit]
# Can be anything you like, it helps to inclue the version. %i Is the server folder name. 
Description=A MineCraft Server: %i 
After=network.target

[Service]
Type=simple
# !!! USE ONLY ONE !!!
# For <1.20.6 Uncomemt the java 17 enry and coment out the java 21 entry.
# =>1.20.6 uses java 21 until further notice.
#Environment="JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64/bin pdfsam"
Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64/bin pdfsam"

WorkingDirectory=/opt/minecraft/%i
User=minecraft
Group=minecraft
Restart=on-failure
RestartSec=10
StartLimitInterval=60s
StartLimitBurst=2

# !!! Change this !!!!
Sockets=MCS-<version>@.socket

StandardInput=socket
StandardOutput=journal         
StandardError=journal
# Short name for the log Eg; =MineCraft 1.20.4 %i. %i Is the server folder name.
SyslogIdentifier=MineCraft %i

# !!! Change these too! <version> x3 !!!!
ExecStop=/bin/sh -c 'echo "say SERVER SHUTTING DOWN IN 10 SECONDS. SAVING ALL MAPS..." > /run/[email protected] && sleep 10 && echo "say SERVER SHUTTING DOWN NOW." > /run/[email protected] && echo "save-all" > /run/[email protected]'

# Xmx/Xms Leave equal unless you know what are doing or are told to change it.
# The full name of the server jar goes here. Eg; abric-server-mc.1.20.4-loader.0.15.11-launcher.1.0.1.jar
ExecStart=java -Xmx4g -Xms4G -jar <"Your Server jar's full name"> nogui

[Install]
WantedBy=multi-user.target

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .