Minecraft Server with Backups via systemd
In this post, I’ll describe how to use systemd to manage a Minecraft server. A named pipe will allow us to send commands to that server, which we’ll use in order to set up a job that periodically makes backups and uploads them to Google drive. This may sound fancy, but luckily systemd makes this a breeze to set up, once you’ve done a little Googling.
This tutorial does not assume you’ve ever set up a Minecraft server before. You
don’t need to be familiar with systemd
, but you should know how to use Ubuntu
at a basic level. I won’t cover how to set up an Ubuntu server.
Let’s get started!
Setting up a Minecraft-Server-as-a-Service
First, we must create a systemd service that runs a Minecraft server. You don’t
want to run that server as root, so create a minecraft
user and group:
$ useradd minecraft
Next, install the Java Runtime Environment:
$ sudo apt-get install openjdk-9-jre-headless
Next, let’s create a directory to run the server from. The Minecraft server JAR will go in this directory.
$ mkdir /srv/minecraft/
$ cd /srv/minecraft/
$ curl -o minecraft_server.jar https://s3.amazonaws.com/Minecraft.Download/versions/1.10.2/minecraft_server.1.10.2.jar
At this point, you could start a server by running:
$ java -jar minecraft_server.jar
Instead, we’ll create a service that does almost the same thing, but runs as the
minecraft
user we just created.
Create /etc/systemd/system/minecraft-server.service
:
[Unit]
Description=Minecraft server
[Service]
WorkingDirectory=/srv/minecraft
User=minecraft
Group=minecraft
ExecStart=/srv/minecraft/run-server.sh
[Install]
WantedBy=multi-user.target
As you can probably guess, this will invoke a file named run-server.sh
. Let’s
create this file in /srv/minecraft/run-server.sh
:
#!/bin/bash
tail -f /tmp/minecraft_server_in | java -jar minecraft_server.jar
You’ll need to chmod +x run-server.sh
.
Before this script can work, we must create a named pipe at
/tmp/minecraft_server_in
:
$ mkfifo /tmp/minecraft_server_in
And that’s it, you’ve service-ized Minecraft! You can start the server by running:
$ service minecraft-server start
You can test that the input is working by connecting to your new server, and running:
$ echo "time set day" >> /tmp/minecraft_server_in
Hopefully, dawn’s rosy-red fingers should caress your realm.
Adding a backup job
To take a backup of a Minecraft server, you need to copy the world
directory
in /srv/minecraft
. However, you can’t simply copy that directory at any time;
you need to disable “auto-saving” first. Otherwise, you may get a corrupted copy
of the data, since you might be reading a file while the server is still writing
to it.
Luckily, the Minecraft server has some commands to deal with this:
save-off
disables auto-savingsave-on
enables auto-savingsave-all
saves the server
Our approach will be to tell the server to save-off
, then save-all
. We’ll
then monitor the server’s logs, waiting for the server to output “Saved the
world”. Once that happens, we’ll copy the world
directory. When we’re
finished, we run save-on
.
Create a backup script called /srv/minecraft/backup-job.sh
:
#!/bin/bash
echo "save-off" >> /tmp/minecraft_server_in
echo "save-all" >> /tmp/minecraft_server_in
# See comment below
echo "seed" >> /tmp/minecraft_server_in
# `journalctl -f` produces an infinite stream of output from the Minecraft server.
# `sed` will quit once one of those lines matches "Saved the world". Once `sed`
# dies, `journalctl` will die the next time it logs anything -- it will receive a
# SIGPIPE. That's why we send "seed" to the server -- it will generate additional
# logs that will cause `journalctl` to die. The `stdbuf -oL` is to prevent
# buffering from delaying the SIGPIPE.
stdbuf -oL journalctl --since "3 seconds ago" -f -u minecraft-server.service | sed '/\[Server thread\/INFO\]: Saved the world/q'
# If there's a particular directory you want to put this in, look into the
# --parent option.
gdrive upload backup.zip --name "backup-$(date).zip"
rm backup.zip
echo "save-on" >> /tmp/minecraft_server_in
Run chmod +x
on this file. Before this script can work, you need to set up
gdrive
. Do so by running:
# This assumes you're using Ubuntu 64-bit. If not, you can use another download
# link from https://github.com/prasmussen/gdrive
$ curl -o /usr/local/bin/gdrive -L https://docs.google.com/uc?id=0B3X9GlR6EmbnQ0FtZmJJUXEyRTA\&export=download
As the minecraft
user (run su minecraft
), run gdrive list
and it’ll help
you with the Google API key shenanigans.
You’ll also need to add minecraft
to two groups in order to silence a warning:
$ usermod -G adm -a minecraft
$ usermod -G systemd-journal -a minecraft
$ id minecraft
uid=1000(minecraft) gid=1000(minecraft) groups=1000(minecraft),4(adm),101(systemd-journal)
At this point, you should be able to run backup-job.sh
successfully. Once it
finishes executing, you should be able to find a file called “backup-Some Date
Here.zip”.
Next, we’ll need to create a systemd service and a systemd timer. The timer will
invoke the service periodically, and our new service will run backup-job.sh
.
Create the service in /etc/systemd/system/minecraft-backup.service
:
[Unit]
Description=Minecraft Backup
[Service]
Type=oneshot
User=minecraft
Group=minecraft
WorkingDirectory=/srv/minecraft
ExecStart=/srv/minecraft/backup-job.sh
Enable this service with:
$ systemctl enable minecraft-backup.service
You can verify the service works by running:
$ service minecraft-backup start
A backup should appear in your Google Drive.
Next, create the timer in /etc/systemd/system/minecraft-backup.timer
:
[Unit]
Description=Run minecraft-backup.service daily
[Timer]
OnCalendar=*-*-* 12:00:00
[Install]
WantedBy=multi-user.target
This timer will execute minecraft-backup.service
every day at 12PM. My
server’s clock is on UTC; 12PM UTC is early in the morning in California, where
I live.
Kick off your timer by running:
$ systemctl enable minecraft-backup.timer
$ systemctl start minecraft-backup.timer
You can check on your timer with systemctl list-timers
.
And that’s it, you now have automatic backups! Now go out and have fun.