I recently set up a Minecraft server. I usually do this by running the downloaded .jar file in a screen session. This time though, I did it a little bit better.

Systemd

I started by creating a systemd configuration for the server. Since I was using screen, I also found a way to extract the screen PID from inside the launch script and write it to a file so systemd can read it. The snippet for this is as follows:

screen -ls | grep .minecraft | grep -oP "([0-9]*(?=.minecraft))" > minecraft.pid

This will fetch all screens called minecraft (which should be only one) and writes it's PID to the file minecraft.pid in the current workingdir.

The configuration file for systemd is pretty small and IMHO self explaining. The only things that need to be changed are the path where the server is installed and the user it shall run as.

[Unit]
Description=Minecraft Server
After=syslog.target network.target
Conflicts=minecraft-server.service

[Service]
Type=forking
User=lerk
Group=lerk
PIDFile=/home/lerk/minecraft/minecraft.pid
ExecStart=/home/lerk/minecraft/start-server.sh
ExecReload=/home/lerk/minecraft/backup-server.sh
ExecStop=/home/lerk/minecraft/stop-server.sh

[Install]
WantedBy=default.target

Start script

To start the server, I wrote a small bash script that resides inside the main minecraft folder. It basically kills leftover screen sessions, starts a new one and then starts the server inside it. After that it fetches the PID of the screen using the snippet above and prints out the PID file's content (for debugging reasons). You also need to update the paths here.

#!/bin/bash
cd /home/lerk/minecraft
echo "cleaning leftover screens..."
screen -wipe
echo "starting minecraft screen..."
screen -dmS minecraft bash
screen -S minecraft -X stuff 'java -Xmx8G -Xms2G -jar server.jar nogui'$'\n'
echo "fetching pid..."
screen -ls | grep .minecraft | grep -oP "([0-9]*(?=.minecraft))" > minecraft.pid
cat minecraft.pid

Stop script

The stop script will notify anyone currently playing on the server using the say command and stops the server afterwards. Please note that it might be unsafe just to kill the screen, therefore the script sends the actual stop command to the server to shut it down. You also need to update the paths here.

#!/bin/bash
set -euo pipefail
cd /home/lerk/minecraft
screen -S minecraft -rX stuff 'say Server stopped by script! Stopping in 5...'$'\n'
sleep 5
screen -S minecraft -rX stuff 'stop'$'\n'
exit 0

Backups

Since I am a git junkie, I also backup the minecraft server using git (that way I could easily roll back changes made by griefers or similar).

I decided to link this script to the reload command so I can call systemctl reload minecraft.service from a cronjob to do automatic backups.

The backup script will stop the server, commit all changes made, sync with the server using git pull --rebase (this allows for "updates" to the minecraft server itself by using the GitLab WebIDE), push the new commit and start the server again. Note that you also need to update the paths and the commit author here.

#!/bin/bash
set -euo pipefail
cd /home/lerk/minecraft
screen -S minecraft -X stuff 'say Backup starting...'$'\n'
screen -S minecraft -rX stuff 'stop'$'\n'
sleep 2
git add --all
git commit -m "scripted backup" --author="CommMC Backup <info@k40s.net>"
git pull --rebase
git push -u origin master
echo "restarting service..."
screen -S minecraft -X stuff 'java -Xmx8G -Xms2G -jar server.jar nogui'$'\n'

Maps

What is a cool minecraft server without a Google Maps-like website that shows the available worlds?

You may already have heard of overviewer. It's an application that renders the map described above. Unfortunately the setup described in their documentation was a bit too annyoing for me and also didn't fit well into my automagic backup procedure (I'd have to run the renderer myself or start it using a cronjob) so I decided to build a Docker container for it and run it using GitLab CI.

The biggest problem with this (and the reason why I'm unable to just opensource this) is that overviewer needs an original minecraft.jar whose distribution is probably not allowed according to Microsoft's EULA (please don't try to tell me that "Minecraft is still made by Mojang", it's all M$ now.)

On macOS you can find the file (for version 1.13.1) here: ~/Library/Application\ Support/minecraft/versions/1.13.1/1.13.1.jar

On Linux it should be somewhere inside ~/.minecraft.

On Windows, searching %APPDATA% might be the way to go but I don't know (and don't really want to).

The last thing you need is the Dockerfile which is simply:

FROM ubuntu:latest

RUN apt-get -qqq update && \
    apt-get -yq upgrade && \
    apt-get -yq install build-essential git python-willow python-dev python-numpy && \
    apt clean

RUN git clone git://github.com/overviewer/Minecraft-Overviewer.git /overviewer && \
    cd overviewer && \
    git checkout minecraft113 && \
    python2 setup.py build && \
    python2 setup.py install && \
    mkdir -p /root/.minecraft/versions/1.13.1/

ADD 1.13.1.jar /root/.minecraft/versions/1.13.1/

CMD bin/bash

If you put those two files inside a new folder and run docker build -t lerk/overviewer:1131, you'll have the docker image available. You can then run it using: docker run -it lerk/overviewer:1131

Additionally, you will need a configuration file for overviewer, which is called overviewer.conf. An example configuration might look like this:

worlds["CommMC"] = "world"
worlds["CommMC Nether"] = "world_nether"

renders["day"] = {
    "world": "CommMC",
    "title": "Daytime Render of CommMC World",
    "rendermode": "smooth_lighting",
    "dimension": "overworld",
}

renders["night"] = {
    "world": "CommMC",
    "title": "Nighttime Render of CommMC World",
    "rendermode": "smooth_night",
    "dimension": "overworld",
}

renders["nether"] = {
    "world": "CommMC Nether",
    "title": "Render of CommMC Nether",
    "rendermode": "nether_smooth_lighting",
    "dimension": "nether",
}

outputdir = "mcmap"

Please note that this config file will probably not work with your setup. You should consult the official documentation on this topic to get it working as you want it to.

You can then start the docker conatiner, pull the minecraft server repo (if you've put it in git) and render your first map by running the following command from inside the repo folder: overviewer.py --config=overviewer.conf

If you used either the configuration above (and it works) or used the outputdir parameter in your own config, you need to mkdir the directory before starting the render or it will fail.

GitLab

Now to wrap this whole thing up, I want the renderings to start automatically after every backup (push into git). To do this I use my GitLab instance (to host the repo itself), GitLab CI (to run the renderer) and GitLab Pages (to host the resulting map).

All of this complicated sounding stuff is as easy as writing yaml (which means it's not. At all!) and you simply need to create a .gitlab-ci.yml inside the root folder of your repo:

image: lerk/overviewer:1131

stages:
  - render
  - deploy

map:
  stage: render
  script:
    - mkdir mcmap
    - overviewer.py --config=overviewer.conf
  artifacts:
    paths:
      - mcmap/

pages:
  stage: deploy
  dependencies:
    - map
  before_script: []
  script:
    - mv mcmap/ public/
  artifacts:
    paths:
      - public
    expire_in: 30 days
  only:
    - master

The image parameter tells GitLab CI to run the build inside the overviewer contianer we've just built. Obviously you need GitLab CI Runners that are capable of running Docker.

The map job does the rendering itself. It simply runs overviewer and sets the artifacts property to tell GitLab to keep the render result.

The pages job uses the artifacts from the map job (by using the dependencies property), copies it into a path (afaik the only one) GitLab Pages uses to search for deployable stuff.

After a pipeline completed successfully you should be able to see the updated map on the Pages-URL of your repo.

If you want to see an example, you can simply join the comm.network minecraft server (if you're lucky I am currently online and will give you the ability to build stuff) or have a look at our map which is updated daily.