Self Hosted Minecraft: Devlog #1. Running Minecraft as a Systemd Service2020-05-28 22:40
The official Minecraft Server implementation is written in Java. As of today, the recommended JRE version for Minecraft is Java 8.
Depending on the Minecraft version or variation you're running, the compatibility with different Java versions might differ. Do some research about the correct Java version to run. In my case, I was trying to host Minecraft Forge 1.12.2, which only works with Java 8, so, we'll stick with that version from now on.
Once you have Java available in your
PATH, running Minecraft Server is really straightforward:
java -jar minecraft_server.jar
That command will start an interactive process, which provides both the backend for the server and a command line interface for interacting with it. Having this interactive shell is useful for performing maintenance tasks, but we want the process to run as a service using Systemd and get automatic restarts if it fails, start on boot, etc. So... how can we do that?
First, let's create a simple Systemd
.service file for our server:
[Unit] Description=Minecraft After=network.target [Service] Restart=on-failure RestartSec=3 WorkingDirectory=/srv/minecraft ExecStart=/usr/bin/java -jar minecraft_server.jar [Install] WantedBy=multi-user.target
This service described as
Minecraft will run
/usr/bin/java -jar minecraft_server.jar on the directory
/srv/minecraft. If the process exists with any other state than a successful one, it will be restarted after
3 seconds. Also, it's gonna start on system's boot, right after networking is available.
That's a pretty common Systemd service definition, but it will run Minecraft on the background without interactivity. To make the process run in background and maintain interactivity, we can use GNU Screen and some tweaking on our Systemd service definition.
Screen is a full-screen window manager that multiplexes a physical terminal between several processes, typically interactive shells. [...] When screen is called, it creates a single window with a shell in it (or the specified command) and then gets out of your way so that you can use the program as you normally would. [...] Programs continue to run when their window is currently not visible and even when the whole screen session is detached from the users terminal.
With Screen we can run a program and detach it from our terminal. Then we can reattach to it from anywhere.
In the following example, notice the "detached" message inside the terminal on the left. That happens when you use
Ctrl + a and then
d (Action -> Detach).
screen puts us in an interactive shell to run any program, but we could run
screen directly with a program of our choice:
screen ping 18.104.22.168
That's running the program
ping 22.214.171.124 directly on top of
screen. It will keep the same functionality as the previous example of being able to interact and detach, but the screen session will last as long as the specified program. In this case, as soon as you produce a SIGINT (
Ctrl + c), it will stop
ping and finish the
Now we could run
screen... but starting the program detached from it, instead of attached at start.
screen -dm ping 126.96.36.199 # -d means "detach" # -m means "force to create a new session instead of reusing an existing one"
You'll notice that running that command produces no output. That's because it starts directly detached, on the background. Run
screen -r to re-attach to it.
We could even put a "sockname" to the sessions when creating them. The session's name is the combination of the process' PID and the sockname.
screen -dmS coolping ping 188.8.131.52
After that, run
screen -ls and see the name of the session. It should be something like "xxxx.coolping".
If there's no sockname defined, it gets one by default. Defining a sockname is useful because it lets us re-attach to sessions by a recognisable name, which comes handy when you have more than one session and need to re-attach to an specific one.
screen -r coolping # is better than screen -r 8381
Now we have a way to run any program, in the background, keeping interactivity! Let's add it to our Systemd service definition:
[Unit] Description=Minecraft After=network.target [Service] Type=forking Restart=on-failure RestartSec=3 WorkingDirectory=/srv/minecraft ExecStart=/usr/bin/screen -dmS minecraft /usr/bin/java -jar minecraft_server.jar [Install] WantedBy=multi-user.target
Notice that I didn't just prepend
/usr/bin/screen -dmS minecraft to
ExecStart. I also added
Type=forking to the
In Systemd there are many types of services. The most simple is, well,
Type=simple, which considers that a service has a main process, the first one. As long as the process is alive, the service is alive too, and will try to restart the service if it finishes, depending always on the restart policy.
Type=forking is a little bit different:
The parent process is expected to exit when start-up is complete and all communication channels are set up. The child continues to run as the main service process, and the service manager will consider the unit started when the parent process exits.
That's exactly what happens when using
screen. When running
screen -dmS coolping ping 184.108.40.206 the first process (
screen) starts a child process (in this case, a wrapped
ping) and then it finishes, leaving the child process alone. In this scenario,
Type=forking considers the child process as the new main process.
We have covered how to start the service properly, but... how do we stop a Minecraft process, in background, inside screen, inside a Systemd service?
To see an example, run again this command we saw earlier in the post:
java -jar minecraft_server.jar
Once the world is loaded (You'll know because there's a message saying "Done!"), produce a SIGINT by typing
Ctrl + c.
What you should see is Minecraft closing itself safely. Disconnecting all the players, saving the world, and finally finishing the process. Minecraft always does this when receiving a SIGINT, and you can send a SIGINT (Or any other signal) to any running process, interactively or not.
Screen also receives signals. When a screen session receives a SIGINT, it will send a SIGINT to it's wrapped process, too.
And we can instruct Systemd to send an specific signal when stopping a running service:
[Unit] Description=Minecraft After=network.target [Service] Type=forking Restart=on-failure RestartSec=3 WorkingDirectory=/srv/minecraft ExecStart=/usr/bin/screen -dmS minecraft /usr/bin/java -jar minecraft_server.jar KillSignal=SIGINT TimeoutStopSec=60 [Install] WantedBy=multi-user.target
KillSignal=SIGINT tells Systemd to use a SIGINT whenever it needs to stop the running service. A SIGINT signal provides a process time to stop safely: Persisting data to disk, closing communication channels gracefully, etc.
Systemd also gives a maximum time span for the process to end, and if it doesn't end on time, it will send a SIGKILL, which means killing the process instantly. In our case it's
TimeoutStopSec=60. Usually a whole minute for a process to stop is a lot, but in case of Minecraft it's adequate because world information is pretty delicate, and you don't want corrupted maps.
That's all! Hope this post was useful for you. Don't hesitate to contact me if you have any questions or comments.
Remember that this devlog is about my process of building Ourcraft, a tool to help you on the endeavor of hosting your own Minecraft server.
See you in the next one!