Managing a Simple Family Minecraft Server

Page created: 2021-01-16
Updated: 2025-05-16

In summary, I use my ratcraft script (See below) to manage a Minecraft server.

A screenshot from our family Minecraft world

Virtual hardware

UPDATE: After a couple months, I ended up buying a cheap SFF ("small form-factor") Intel Core i7-based PC on eBay and ran the family minecraft server out of the basement. So this "virtual hardware" section only applied for a short period of time.

I have a Linode shared virtual instance with the following specs: 4 GB RAM, 2 Cores, 80 GB SSD, at $20 per month. Cheaper instances may work, but we’ve had no problem with four or more people playing on this instance.

Hosting on an in-house computer works great (and would be cheaper after about a year or so), but having the "cloud" instance is so much easier to manage and getting friends outside of the house connected is 100% easier.

The instance runs Slackware Linux and I do all server management at the command line through SSH.

I also gave it a DNS subdomain so it’s real easy to tell friends how to access it.

Minecraft Server

We’ve experimented with mods (and creating them), so we’ve run Spigot servers locally. But on the always-on family server, we run the so-called "vanilla" Minecraft server (Java Edition).

Launching the server

The server is a Java executable that needs to be kept running in a process. I’ve opted to manage the process with the GNU terminal multiplexer screen. The start command would look something like this:

# screen -S session_name -dm bash -c "cd worlddir; java -Xms512M -Xmx1024M -XX:ParallelGCThreads=1 -jar serverxxx.jar nogui"

Obviously I’m not going to type that more than once, so I wrote a little application (a wee Bash script) to manage this for me called ratcraft (see below).

It’s about 130 lines of Bash and has a handful of commands:

# Usage: ratcraft ( start | stop | status | backup | watch | cmd <command> )

Server upgrades

The Minecraft clients automatically update to the latest version as soon as it’s out.

Getting the latest server to match is as simple as grabbing the tarball link from minecraft.net/download/server and downloading it on the host:

# wget https://launcher.mojang.com/v1/objects/1b55...73ced/server.jar
# mv server.jar minecraft_server.1.16.5.jar
# ls
bin/                         minecraft_server.1.16.1.jar  minecraft_server.1.16.4.jar  old_server/
mcserver/                    minecraft_server.1.16.2.jar  minecraft_server.1.16.5.jar
minecraft_server.1.15.2.jar  minecraft_server.1.16.3.jar  minecraft_server.1.16.jar

UPDATE: I no longer manually rename the server to the version number and keep the old ones around. Instead, I let the downloaded server.jar overwrite the previous. Saves a step and I haven’t had any need to downgrade (yet).

So I no longer do this step: manually edit the ratcraft script to update the server version:

jar=~/minecraft_server.1.16.5.jar

Then I just restart (stop and start) the server with ratcraft:

# ratcraft stop
Stopping server...
[20:03:24] [Server thread/INFO]: ThreadedAnvilChunkStorage (world2): All chunks are saved
[20:03:24] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM-1): All chunks are saved
[20:03:24] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM1): All chunks are saved
Server stopped
# ratcraft start
Starting server...
To see how it's going, try: ratcraft watch

In a few moments, the server is ready for the updated clients.

It’s not a commercial-grade solution, but it’s:

  • Simple enough for me to understand when I come back to it every couple months

  • Automated enough to not be a total pain to use

Backups

The ratcraft script has a backup feature. I call it daily via a cron job in Slackware’s /etc/cron.daily directory. The script simply calls:

/theuserdir/bin/ratcraft backup

The backup tells the Minecraft server to stop saving, makes a .tgz with tar -cpvzf of the "world" directory and saves it in a "backups" directory.

The backup command also performs simple backup rotation so the server doesn’t fill up. Occasionally we might want to save an important epoch in our world, so I just rename one of the backups so it doesn’t get removed in the rotation.

Getting/Upgrading the JDK

Maybe you can just use the java executable on your system and update with a package manager. But in my universe, it’s a little more manual.

The Minecraft server upgrades seem to require newer and newer versions of the JVM. I don’t keep up with the Java world at all any more, but it looks like the current best place to get the JVM is:

I don’t see where you can get just the JRE (runtime environment with JVM, which is all that should be needed to run the Minecraft server). But not a big deal. The JDK is just a bigger download since it’s a superset of JRE plus compiler and libraries, etc.

The last time I did this looked like this:

$ wget wget https://download.java.net/...linux-x64_bin.tar.gz
$ tar -xf openjdk-17.0.1_linux-x64_bin.tar.gz
$ jdk-17.0.1/bin/java --version
openjdk 17.0.1 2021-10-19
OpenJDK Runtime Environment (build 17.0.1+12-39)
OpenJDK 64-Bit Server VM (build 17.0.1+12-39, mixed mode, sharing)
$ vim bin/ratcraft
    start_cmd="<new path to java> -Xms512M blah blah..."

Hope that helps!

What follows is the ratcraft README file and bash script. These were originally hosted on Github and released under the MIT license in 2020.
I'm now releasing them as public domain. Feel free to do with them as you please!

ratcraft README

Minimal Bash Minecraft server launch/control/backup script.

The purpose of this script is to daemonize a Minecraft server (run it in the background) using GNU screen (present on most UNIX-like OSs) and allow simple control of the server. It follows the principal of "the simplest thing which could possibly work."

Features:

  • Start the server and run it in the background

  • Stop the server

  • Run arbitrary Minecraft server commands at the command line

  • Pause Minecraft world saving and create a compressed backup of the world files

Setup

There are some settings at the top of the script which you’ll likely need to change:

rootdir=~/mcserver
world=$rootdir/world
backups=$rootdir/backups
jar=~/minecraft_server.1.15.2.jar
sname=ratcraftscreen
start_cmd="java -Xms512M -Xmx1024M -XX:ParallelGCThreads=1 -jar '$jar' nogui"

In particular, the first four are likely to need some changes. These are my current actual settings.

  • rootdir - The server’s working directory. Minecraft server will populate this with files such as eula.txt, server.properties, whitelist.json, and so forth.

  • world - (Directory) This should match the level-name property in server.properties (which is created when the server first runs - will be world by default) and needs to be under the server root directory. Minecraft will populate the world directory with files such as level.dat, etc. when it creates the world for the first time.

  • backups - (Directory) Again, I’ve placed this under rootdir, but feel free to point this to any valid path. This is where the command ratcraft backup will attempt to save *.tgz backup files.

  • jar - This is the exact name of the Minecraft Java ARchive file (*.jar) you wish to run. Could be a Bukkit or Spigot server or Vanilla Minecraft server from minecraft.net. You choose the flavor!

Commands

Run ratcraft with arguments to see terse usage instructions:

root@darkstar:~# ratcraft
Usage: ratcraft ( start | stop | status | backup | watch | cmd <command> )

Let’s look at each of these sub-command options in turn:

  • ratcraft start - Starts the specified Minecraft server .jar under a GNU screen session. Runs in the background.

  • ratcraft stop - Stops the running server and/or screen session.

  • ratcraft status - Prints out a neat little summary of the settings and whether or not the server appears to be running. Example below.

  • ratcraft backup - Creates a backup of the current running world. (Halts world saving, uses GNU tar to create a compressed archive of the world directory, resumes world saving.)

  • ratcraft watch - Runs GNU tail -f on the Minecraft server log so you can see new log messages as they are written. End with CTRL+C.

  • ratcraft cmd '<your command>' - Send a command to the running Minecraft server. See the examples below.

status Example

As promised, here’s an example of running the status sub-command. Here’s my current server status:

root@darkstar:~# ratcraft status
Settings:
    rootdir:    /root/mcserver
    world:      /root/mcserver/world
    backups:    /root/mcserver/backups
    jar:        /root/minecraft_server.1.15.2.jar
    sname:      ratcraftscreen (id of screen session)
    start_cmd:  java -Xms512M -Xmx1024M -XX:ParallelGCThreads=1 -jar '/root/minecraft_server.1.15.2.jar' nogui
Server is running.

cmd Examples

You can talk to your players with say:

root@darkstar:~# ratcraft cmd 'say Hello from the server.'
...
[23:03:48] [Server thread/INFO]: [Server] Hello from the server.

Or teleport somebody:

root@darkstar:~# ratcraft cmd 'teleport Ratf -983 24 340'

The important thing to note in these examples is that the entire command needs to be quoted. Single-quotes are ideal unless you want to use variable interpolation, in which case you already know what you’re doing.

Also note that after your command is sent, the script waits a second and then displays the last three lines from the server log. Though this should show the server’s reaction to your command, it may also be confusing. This would be a great place for script improvement. Showing only new lines written after the command is sent would be ideal.

ratcraft Bash script source

#!/usr/bin/bash

# ratcraft - Ratfactor's Old School Minecraft Server Script
#
# Run without any arguments for terse usage. See README.md
# for more detail.
#
# Copyright 2020 Dave Gauer
# MIT license - see LICENSE file in this repo.
#

rootdir=~/mcserver
world=$rootdir/world
backups=$rootdir/backups
jar=~/minecraft_server.1.15.2.jar
sname=ratcraftscreen
start_cmd="java -Xms512M -Xmx1024M -XX:ParallelGCThreads=1 -jar '$jar' nogui"

is_running() {
	[[ -n "$(screen -ls | grep $sname)" ]]
	return # return status of above test
}

servercmd() {
	# Note lack of space between argument '$1' and the printf to produce
	# the return character. Trailing space after the command will cause
	# Minecraft to return 'Incorrect argument for command'.
	# -S <session_name>
	# -p <window#>
	# -X <command>
	# stuff "string" (GNU screen command to write to STDIN)
	screen -S $sname -p 0 -X stuff "$1$(printf '\r')"
	sleep 1
	tail -3 $rootdir/logs/latest.log
}

status() {
	echo "Settings:"
	echo "    rootdir:    $rootdir"
	echo "    world:      $world"
	echo "    backups:    $backups"
	echo "    jar:        $jar"
	echo "    sname:      $sname (id of screen session)"
	echo "    start_cmd:  $start_cmd"

	if is_running
	then
		echo "Server is running."
	else
		echo "Server is NOT Running."
	fi
}

backup() {
	if [ ! -d $backups ]
	then
		mkdir -p $backups
	fi

	# inform users, suspend saving, backup, resume saving
        servercmd "say Backing up world. Saving suspended..."
        servercmd "save-off"
        servercmd "save-all"
	tar -cpvzf "$backups/bak-$(date "+%Y-%m-%d").tgz" $world/*
        servercmd "save-on"
        servercmd "say Backup done. World saving resumed."

        # remove backups older than 2 weeks
        find $backups/ -mtime +14 -exec rm {} \;
	echo "Backup done."
}

start() {
	if is_running
	then
		echo "Apparently already running."
	fi

	echo "Starting server..."
	screen -S $sname -dm bash -c "cd $rootdir; $start_cmd"
	echo "To see how it's going, try: ratcraft watch"
}

stop() {
	if ! is_running
	then
		echo "Could not find screen session '$sname' to stop."
		exit 1
	fi

	echo "Stopping server..."

	servercmd "stop"
	sleep 20

	if ! is_running
	then
		echo "Server stopped"
		exit
	fi

	echo "Server hasn't stopped yet. Killing screen session..."
	screen -S $sname -p 0 -X quit

	if is_running
	then
		echo "ERROR: Screen session '$sname' still running. Sorry."
		exit 1
	fi
}

case $1 in
	backup)
		backup
		;;
	start)
		start
		;;
	stop)
		stop
		;;
	status)
		status
		;;
	watch)
		tail -f $rootdir/logs/latest.log
		;;
	cmd)
		servercmd "$2"
		;;
	*)
		echo "Usage: ratcraft ( start | stop | status | backup | watch | cmd <command> )"
		exit 1
esac