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

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!
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 aseula.txt
,server.properties
,whitelist.json
, and so forth. -
world
- (Directory) This should match thelevel-name
property inserver.properties
(which is created when the server first runs - will beworld
by default) and needs to be under the server root directory. Minecraft will populate the world directory with files such aslevel.dat
, etc. when it creates the world for the first time. -
backups
- (Directory) Again, I’ve placed this underrootdir
, but feel free to point this to any valid path. This is where the commandratcraft 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 GNUscreen
session. Runs in the background. -
ratcraft stop
- Stops the running server and/orscreen
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 GNUtar
to create a compressed archive of the world directory, resumes world saving.) -
ratcraft watch
- Runs GNUtail -f
on the Minecraft server log so you can see new log messages as they are written. End withCTRL+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