Serving Minecraft on Ubuntu

This is an old post. It may contain broken links and outdated information.

The first thing I ever saw of Minecraft was this video of an impossible waterslide which stretched up across the sky and down through footless caverns. I was entranced, and started playing shortly after that. Minecraft is primarily a sandbox game where you dig for resources and build things, all in glorious faux 8-bit graphics. Of late its creators have tried to turn it into a really bad Zelda clone, but fortunately it’s possible to ignore all the worthless stupid shit they keep adding and instead play it in the proper manner. It can be played single- and multiplayer, and while I normally hate playing with anyone online, Minecraft is my exception.

In October of 2010 I decided to download the Minecraft server application and take a stab at running my own Minecraft server, so that my buddies and I could have a private place to build things without worrying about random crazy people from the Internet kknocking down our sandcastles. There were some errors along the way, but I’ve settled on some guidelines and methods that have worked out very well for me.

The hardware

Bigdinosaur.org actually runs two Minecraft instances on a single server—a “creative” instance for having fun and testing things out, and a “survival” instance where there’s no summoning of resources and everything is mined and built manually, one block at a time (and for more info on our Minecraft worlds than you can shake a diamond pickaxe at, take a peek at the wiki). With that in mind, I’ve sized my Minecraft server to host two concurrently-running maps. Your requirements might be higher or lower.

Disks

In the bad old days, prior to Minecraft Beta 1.3, if memory serves me correctly, the hardest part about serving Minecraft was that its map file format was murder on disks. Each map’s data was stored in hundreds of thousands of tiny files nested within a complex structure of thousands of directories; maps of sufficient size could grow to have millions and millions of files. This meant that there was a tremendous amount of random small-block reads and writes constantly going on any time players moved around and changed things on the maps, and that made Minecraft an IO-intensive application. Running it on shared hosting or on a VPS was extremely difficult because even small maps sucked up so much disk IO. One obvious way to work around this was to move Minecraft into a RAM disk, but this wasn’t always practical or even possible on VPS systems with limited amounts of RAM. The other workaround, which I adopted for the Bigdinosaur Minecraft server, was to use a fast SSD to hold the map. I chose an 80 GB OCZ Vertex 2, which at the time was one of the fastest consumer SSDs available, and it’s been a solid performer for me since it went in.

The map format changed some time ago, with Minecraft adopting a much more sensible and disk-friendly layout, but Minecraft still can be tough on disks. Use the fastest SSD you can reasonably afford.

RAM

As much RAM as you’re willing to pay for is probably not quite enough.

That being said, I’m using 8 GB in the Bigdino server, with 6 GB dedicated to the Survival world and 2 GB dedicated to the much less-used Creative world. The Minecraft server application is happier with more and will cache as much as it can, so gibing 8 GB to a single map isn’t outrageous; with 8+ players in game at one time, the RAM usage on the Survival instance does begin to creep up close to maximum, and I’m sure it would use more if it were there. Don’t skimp on RAM.

CPU

Minecraft server doesn’t take advantage of multiple CPUs or CPU cores, so you don’t need a fancy six-core CPU or anything like that. I got a good deal on a quad-core AMD Phenom II, which is probably more CPU than I really need for the server. Under load with several players, Minecraft doesn’t use even an entire core.

Other stuff

No need to screw with a fancy video card, or a DVD burner, or anything else like that. Just use a motherboard with integrated video and Ethernet and you’ll be good to go.

The software

Hardware’s the easy part—getting everything up and running takes a little more effot.

Opearting system

Linux, duh. You can run Minecraft Server on Windows, but why would you do that to yourself? It’s a total waste of time and effort and computing resources. The best choice is Ubuntu Linux Server, or another Debian-based GNU/Linux distro. If you’re one of those types who prefers CentOS, then you can still follow along, but you’ll have to adapt any commands to your own distro’s way of thinking (especially the software installations). If you’re a BSD person, you probably think Minecraft is a waste of time because it doesn’t contain neckbeard crafting.

It’s also possible to host Minecraft on OS X, which I did for some time, but ultimately it’s got the same problem as doing it on Windows—why tie up resources on a consumer-level OS with a GUI when you could stick Minecraft on a server where it belongs?

Packages

Minecraft needs Java to run. This gets a little more complicated now, because Canonical has removed the offial Sun Java runtime packages from the Ubuntu repositories, and I’ve had bad performance and weird issues making Minecraft run on OpenJDK in the past.

Fortunately, at least for now, you can still get the Sun JRE from an alternate PPA:

$ sudo aptitude install python-software-properties
$ sudo add-apt-repository ppa:ferramroberto/java 
$ sudo aptitude update
$ sudo aptitude install sun-java6-jre sun-java6-bin sun-java6-fonts

The first command installs the extremely useful add-apt-repository command, and then the next line adds the correct PPA to the sources list. The next line refreshes our package list, and then the final line fetches the Java packages we need.

We’ll need just a few other things as well:

$ sudo aptitide install screen # Used to run Minecraft headless, if not already installed
$ sudo aptitude install rsnapshot # Used for backups, if not already installed

We’re going to be using screen to run Java and the Minecraft server jar without needing to tie up a console, and rsnapshot will be used to set up our backups in a bit.

Context

Finally, we need a user to run things under. Privilege separation is one of the pillars of sane server administration, and so we’re going to create a Minecraft user. Unlike most service accounts, we want this one to have a home directory, since that’s where we’ll be keeping the Minecraft files:

$ sudo useradd -c "Minecraft user" -m mcuser

You should also set a password for the user with passwd mcuser so that you can log in as the user if you ever desire to do so.

Pulling it together

Create a directory under /home/mcuser named minecraft, and then switch to it. We’re going to be using that directory for all of our Minecraft stuff. Download the Minecraft server jar file from the download page (making sure to get the jar file, not the Windows executable file!), and place it in /home/mcuser/minecraft. When it is launched for the first time, Minecraft will create the rest of the files and directories it needs.

Now we need an init script for Minecraft, so that it gets launched automatically. There are lots of different ones floating out on the web, but this is the one I’m using. It includes the ability (commented out) to start and stop multiple Minecraft instances. Create this file as /etc/init.d/minecraft:

#!/bin/bash
# /etc/init.d/minecraft

  ### BEGIN INIT INFO
  # Provides:   minecraft
  # Required-Start: $local_fs $remote_fs
  # Required-Stop:  $local_fs $remote_fs
  # Should-Start:   $network
  # Should-Stop:    $network
  # Default-Start:  2 3 4 5
  # Default-Stop:   0 1 6
  # Short-Description:    Minecraft server
  # Description:    Starts the minecraft server
  ### END INIT INFO

#Settings
SERVICE='minecraft_server.jar'
USERNAME="mcuser"
MCPATH='/home/mcuser/minecraft'
## If you have more than one Minecraft instance to control, add additional MCPATH
## variables below
# MCPATH2='/home/mcuser/minecraft2'


ME=`whoami`
as_user() {
  if [ "$ME" == "$USERNAME" ] ; then
    bash -c "$1"
  else
    su - $USERNAME -c "$1"
  fi
}

mc_start() {
  if ps ax | grep -v grep | grep -v -i SCREEN | grep $SERVICE > /dev/null
  then
    echo "Tried to start but $SERVICE was already running!"
  else
    echo "$SERVICE was not running... starting."
    cd $MCPATH
    as_user "cd $MCPATH && screen -dmS survival-mc java -Xmx2048M -Xms2048M -jar minecraft_server.jar nogui"
    ## Add additional lines for additioanl Minecrat instances below here
    # as_user "cd $MCPATH2 && screen -dmS creative-mc java -Xmx2048M -Xms2048M -jar minecraft_server.jar nogui"
    sleep 7
    if ps ax | grep -v grep | grep -v -i SCREEN | grep $SERVICE > /dev/null
    then
      echo "$SERVICE is now running."
    else
      echo "Could not start $SERVICE."
    fi
  fi
}

mc_stop() {
        if ps ax | grep -v grep | grep -v -i SCREEN | grep $SERVICE > /dev/null
        then
                echo "$SERVICE is running... stopping."
                ## Add a line for each task for each Minecraft server you're controlling
                as_user "screen -p 0 -S survival-mc -X eval 'stuff \"say SERVER SHUTTING DOWN IN 10 SECONDS. Saving map...\"\015'"
                # as_user "screen -p 0 -S creative-mc -X eval 'stuff \"say SERVER SHUTTING DOWN IN 10 SECONDS. Saving map...\"\015'"
                as_user "screen -p 0 -S survival-mc -X eval 'stuff \"save-all\"\015'"
                # as_user "screen -p 0 -S creative-mc -X eval 'stuff \"save-all\"\015'"
                sleep 10
                as_user "screen -p 0 -S survival-mc -X eval 'stuff \"stop\"\015'"
                # as_user "screen -p 0 -S creative-mc -X eval 'stuff \"stop\"\015'"
                sleep 7
        else
                echo "$SERVICE was not running."
        fi
        if ps ax | grep -v grep | grep -v -i SCREEN | grep $SERVICE > /dev/null
        then
                echo "$SERVICE could not be shut down... still running."
        else
                echo "$SERVICE is shut down."
        fi
}

#Start-Stop here
case "$1" in
  start)
    mc_start
    ;;
  stop)
    mc_stop
    ;;
  restart)
    mc_stop
    mc_start
    ;;
  status)
    if ps ax | grep -v grep | grep -v -i SCREEN | grep $SERVICE > /dev/null
    then
      echo "$SERVICE is running."
    else
      echo "$SERVICE is not running."
    fi
    ;;

  *)
  echo "Usage: /etc/init.d/minecraft {start|stop|status|restart}"
  exit 1
  ;;
esac

exit 0

The comments make a note at each point in the script where you’d add additional lines in order control more Minecraft servers.

The area where you will want to customize the script is on line 41, which contains the screen command and Java statement which actually launches the Minecraft server. The command as-is launches the Minecraft server instance with 2 GB of RAM; to change that to more or less, alter the two 2048 settngs to reflect the amount of RAM you’d prefer to allocate. For a small server with 1-2 people, 2 GB is more than sufficient; for a server with 4-5 people, consider 4 GB; for more than 5 people, give it as much RAM as you can spare. As mentioned earlier, I allocate 6 GB for my Survival server, which has had as many as ten people on at once.

Once the above script is saved and you’re happy about the state it’s in, run this command:

$ sudo update-rc.d minecraft defaults

This will set the init script you just wrote to execute at startup.

Backups

Rsnapshot, which we installed earlier, is a front-end for rsync that allows you to keep Time Machine-like backups of your files and directories. We’re going to use it to back up your Minecraft directories, but you can use it to back up anything else on the server, too. For this quick tutorial we’re going to back everything up to a local directory, but you really should consider backing up to a different device, like a NAS or other network location, because a backup isn’t a backup if it’s still on the same disk as the source! Keeping rsnapshot backups of our Minecraft directory gives us the ability to roll back any changes we don’t like, but it won’t help us in the event of a disk or system failure unless we back the files up to a different system.

Create a backup target directory at /mcbackups and chown it so that mcuser is the owner:

$ sudo midkr /mcbackups
$ sudo chown mcuser:mcuser /mcbackups

The settings for rsnapshot are stored in /etc/rsnapshot.conf, so let’s open that for editing. There’s a ton of stuff in here, so I’m only going to list the parts we wan to change. Be aware that stuff in this file must be separated with tabs, not spaces! If you make a change in this conf file and rsnapshot stops working, check to make sure you didn’t accidentally insert a space instead of a tab!

...
snapshot_root   /mcbackups/
...
cmd_cp	/bin/cp
cmd_rm	/bin/rm
cmd_rsync	/usr/bin/rsync
cmd_du	/usr/bin/du
...
#########################################
#           BACKUP INTERVALS            #
# Must be unique and in ascending order #
# i.e. hourly, daily, weekly, etc.      #
#########################################
#
## These are how many of each backup the system will automatically keep before
## it deletes them--the settings below keep six hourly backups on-hand, seven
## daily backups on hand, four weekly backups on hand, and monthly backups are
## never deleted. To only keep a certain number of monthlies, uncomment the
## last line.
interval        hourly  6
interval        daily   7
interval        weekly  4
#interval       monthly 3
...
logfile /var/log/rsnapshot
...
# Default rsync args. All rsync commands have at least these options set.
#
rsync_short_args        -aPE
rsync_long_args --delete  --force --relative --delete-excluded --safe-links
...
###############################
### BACKUP POINTS / SCRIPTS ###
###############################

# LOCALHOST

backup	/home/mcuser/	yourhostname/

This will tell rsnapshot everything it needs to know about what to back up, where to back it up, and what rsync options to use when it does so. The last section, where /home/mcuser is listed, is where you can add additional places to back up if you’d like.

Now we need to configure a system-wide cron job to actually run rsnapshot, since rsnapshot doesn’t start itself. Edit the system crontab file at /etc/crontab:

# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user	command
#
1 */4	* * *	root	/bin/bash /usr/bin/rsnapshot hourly &> /dev/null
31 4	* * *	root	/bin/bash /usr/bin/rsnapshot daily &> /dev/null
41 4	* * 6	root	/bin/bash /usr/bin/rsnapshot weekly &> /dev/null
51 4	1 * *	root	/bin/bash /usr/bin/rsnapshot monthly &> /dev/null

Using the above settings, the rsnapshot hourly command is triggered every four hour at one minute past the hour (and since we set rsnapshot to keep six hourly backups, we’re maintaining a full day); rsnapshot daily is triggered every day at 04:31. rsnapshot weekly is triggered on the sixth day of every week at 04:41; and rsnapshot monthly is triggered on the first day of every month at 04:51.

There’s no need to restart cron or anything after editing the system crontab—rsnapshot is now fully configured.

Playing and managing

Make sure the Minecraft jar file is owned by the Minecraft user, and then start the Minecraft service!

$ sudo chown -R mcuser:mcuser /home/mcuser
$ sudo /etc/init.d/minecraft start

You should see an acknowledgement that Minecraft is starting, and if everything starts corretly, Minecraft will generate some terrain and then become ready to receive connections from players. You can check to see if it’s listening by taking a peek into its log file once you’re back at the prompt:

$ cat /home/mcuser/minecraft/server.log

You should see output that looks something like this:

 [INFO] Starting minecraft server version 1.0.1
 [INFO] Loading properties
 [INFO] Starting Minecraft server on *:25565
 [INFO] Preparing level "world"
 [INFO] Default game type: 0
 [INFO] Preparing start region for level 0
 [INFO] Preparing spawn area: 65%
 [INFO] Done (726138348ns)! For help, type "help" or "?"

Congratulations, that means it worked! Let’s shut the server down so that we can tweak the config file:

$ sudo /etc/init.d/minecraft stop

Check the log file again to see if the server stopped correctly, and once it’s down, edit /home/mcuser/minecraft/server.properties. For a complete list of settings that can be modified, check the Minecraft wiki, but we’re going to change a few of them right now. Add or modify the following:

level-name="Your Amazing World" #The name of your server!
white-list=true #Prevents unknown accounts from connecting

Whitelisting

White listing prevents users whose Minecraft account names aren’t listed in the white-list.txt file from logging on to your server. You should have this setting enabled, so that random crazy Internet people don’t log onto your server and destroy everything you’ve built. You can add names to the whitelist in-game using the /whitelist command, or you can directly edit the whitelist file. Add your name now, along with the name of any of your Minecraft-playing friends:

yourname
friendname1
friendname2
...
</code>

Passing commands

Start the server again, and tail the server log file with this command:

$ sudo tail -f /home/mcuser/minecraft/server.log

Open another terminal window and connect to the Minecraft server, then launch an instance of bash as root with sudo /bin/bash. Once that’s up, cut and past the following line at the prompt:

su - mcuser -c "screen -p 0 -S survival-mc -X eval 'stuff \"say Hello there!\"\015'"

You should see your “HELLO THERE!” message immediately appear in the server log window…but what have we done?

If you were running the Minecraft server jar directly in your terminal window, without using screen or a launch script or anything, you’d be able to type commands directly into the Minecraft console and interact with the server—summon items, talk to players, teleport things, change game modes, whatever. However, remember that we’re using the screen app to run the Minecraft server attached to a virtual screen, so that it’s not taking up our console window. Because of this, the only way to actually “type” commands into the Minecraft server console is by using the screen application. You can man screen to get an explanation of what the command arguments are, but the important parts are the -S survival-mc, which tells screen which virtual screen you want to send commands to (and in this case it’s called “survival-mc” because that’s what we named it way back up in the minecraft init script), and then the part enclosed in the inner double-quotes. This is where you put the command you want executed on the Minecraft console. To talk to users as the console, you use the command just as it’s entered above; if you wanted to switch a user into creative mode, you’d do this:

su - mcuser -c "screen -p 0 -S survival-mc -X eval 'stuff \"gamemode Dracula 1\"\015'"

This sets user “Dracula” to creative mode. This may or may not be a wise move!

Any command that you’d issue by typing into the console directly, you can insert into that command line. If you have multiple Minecraft server instances running, substitute the screen name of the instance to which you want to send the command in plac of the “survival-mc” portion.

One last thing

This has gone on for some time, and I’m ready to be done with it, but the final thing you should do is take a quick rsnapshot of everything before you decide to do anything wild and crazy. Since the server is in a pristine state, let’s fire a quick rsnapshot off:

$ sudo rsnapshot hourly

Keep an eye on /var/log/rsnapshot and when it’s done, you’re ready to play!

I’ve got a lot more Minecraft-related stuff to talk about, so look for more Minecraft blog postings soon.