This tutorial will explain how to quickly deploy a stateful, persistent Minecraft server using an image from the Docker Hub and a Docker Blueprint on the CenturyLink Cloud.

After seeing plenty of WordPress demos displaying stateful persistence between Docker container restarts, I thought it would be interesting to discuss another extremely popular application that uses stateful persistence - Minecraft. In Minecraft, players literally create or destroy almost anything in the game world that they inhabit. As they interact with the environment and the objects within, changes to the world state are immediately saved to the file system. Like WordPress, Minecraft can use Docker to achieve the stateful persistence required for game play.

Docker's ability to deploy a pre-configured image into a running container within milliseconds is a huge advantage over VMs. To enable applications to write to the file system, Docker uses a unification file system storage approach. By definition, images are static and composed of multiple layers. When a container is started, a writable top layer is added to the image. All writes to the container that add new data or modify existing data are stored in this writable layer, which is persisted between container stops and restarts. Although it is possible to connect to a running container and interact with the file system belonging to that specific container, a better way is to use a data volume. Data volumes exists outside of Docker's storage area, so even if a container is deleted, data stored in a data volume still remains.

(NOTE: Although pausing or stopping a container without a data volume mounted does not destroy the writable top layer, it is much easier and less tedious to manage the state using data volumes)

Target Audience

Docker newbies, Minecraft enthusiasts, and anyone interested in seeing how Blueprints can quickly deploy applications.

Prerequisites

OK. Before we get started, there are a few prerequisites. Besides being comfortable with executing Linux commands on the terminal, you will need:

  • A registered Minecraft Client. Click here to buy a license. Already own the game? Download the client.
  • A CenturyLink Cloud account for Blueprint access. Sign-up for a Free Trial.
  • A VPN client configured to securely connect to your CenturyLink Cloud server. This article provides instructions on successfully installing and configuring the Tunnelblick OpenVPN client.

NOTE: If you just signed up for an account, you must first create a server to activate Client VPN access. Follow the Blueprint deployment process below to create your first server, and then setup VPN access.

Overview

Here's an overview of the steps we'll follow to deploy a stateful Minecraft server using Docker and Blueprints.

  • Step 1: Create a New Server Using a Docker Blueprint
  • Step 2: Update Env Vars and Validate Blueprint Deployment
  • Step 3: Start Your Minecraft Server
  • Step 4: Connect Using Your Minecraft Client
  • Step 5: Persist Minecraft Server Settings Between Container Restarts
  • Step 6: Create New Worlds and Experiment

Ready? Let's get started.

Step 1: Create a New Server Using a Docker Blueprint

Find the Docker Blueprint

  1. Login to the Control Portal and select Blueprint Library from the menu drop-down. Select Blueprint Library

  2. In the upper left corner of the Blueprints Library page, select the data center where you want to provision your server. I've chosen IL1. Select Datacenter

  3. Most likely you will select your Primary data center. This tutorial assumes that if you choose to provision a server in a data center that is not your Primary data center, you also already know how to create cross-connect data center firewall policies so that your VPN Client can connect to your new server.

  4. In the keyword filter, type: docker. Look for the "Docker Engine" Blueprint tile. Mouse-over the tile to read the description: "Installs Docker Engine on Ubuntu 14 with Kernal 4.x". Filter by Docker Blueprints

  5. Click the "Docker Engine" tile to display the deployment page.

  6. Click the Deploy Blueprint button. (CenturyLink Cloud makes it easy to see your monthly costs and pause your server between Minecraft sessions). The next section will walk through the Blueprint configuration steps. Deploy Docker Blueprint

Customize the Blueprint

On the Customize page, there are several required fields to update.

  1. In the Password field(s), enter a secure root password.
  2. For the Group drop-down, choose a group. The Default group is fine. In my case, I chose my Development group.
  3. In the Network drop-down, just use the first vLAN in the list, or choose a different one if you prefer.
  4. Under Primary DNS, select Manually Specify. The field below displays the Primary DNS address.
  5. Do the same for the Secondary DNS, choose Manually Specify.
  6. For Server Type, select Standard (default).
  7. For Service Level, select Standard (default).
  8. Under Customize Server Name(s), give your server a unique 4-6 letter alias. I changed the default DOCK to MINE. The provisioning process generates a complete server hostname using the data center abbreviation, your CenturyLink Cloud account alias, your server alias, and 2-digits. The hostname of my new server is: IL1CLABMINE01.

  9. There is no need to change the Specify Credentials switches or Docker Daemon Options, so leave these settings as they are. When you are done making updates the Customize page should look something like this: Customize Blueprint deployment

  10. Now click the next: step 2 button.

Review your Customizations

  1. On the Review page, verify that everything looks right. Confirm Blueprint deployment
  2. Then, click the deploy blueprint button to kick off the Blueprint deployment process. Deploy Blueprint: Docker Engine

This would be a good time to grab a drink of water or purchase a Minecraft license if you don't already have one. Typically, it takes 3-5 minutes to provision the server and install Docker. When it finishes, you're ready to validate your deployment.

Blueprint Deployment Complete

Step 2: Update Env Vars and Validate Blueprint Deployment

Now it's time to confirm that your deployment was successful. This is also when you'll find out whether you can securely connect to your newly provisioned server.

Open the Server Dashboard

  1. Navigate back to the data center and group containing your new server. In the Control Portal, click the Dashboard link in the breadcrumb trail.
  2. Then click the "chip" icon on the left navigation bar to show the list of available data centers. Dashboard Breadcrumb
  3. Choose your data center and group from the list. In my case, it's the Development group in IL1.
  4. Select your new server from the group list to display the server dashboard. Datacenter Group Server page

Secure VPN Tunnel

Before you can SSH into your server, you must establish a secure VPN tunnel to the data center. If you just created a CenturyLink Cloud account, you will need to follow these instructions to download and configure your OpenVPN client for Mac OS or Windows. Once you've finished, continue below.

NOTE: You can also add the certificate and configuration to an existing VPN client, but that is outside the scope of this tutorial.

  1. Now start the VPN connection. Start OpenVPN Connection

  2. Once your VPN tunnel is connected successfully, you'll see a notification modal. Connected OpenVPN Connection

  3. You will need your server's IP address and root password. To view your root password, click show credentials and copy the value.

    NOTE: In the interest of time, this tutorial doesn't adhere to recommended security best practices. Accessing a server as root isn't a best practice. Please refer to this Knowledge Base article for recommendations on securing your server.

  4. Open up your terminal (or PowerShell on Windows) and type:

    # ssh root@<nn.nnn.nnn.nn>
    

    (NOTE: <nn.nnn.nnn.nn> represents the internal IP address of your new server. This is displayed on the server dashboard under Server Info).*

  5. Cut and paste the IP address starting with 10.

    Mine looked like this:

    Davids-MBP-3:~ davidgardner$ ssh [email protected]
    The authenticity of host '10.90.25.12 (10.90.25.12)' can't be established.
    ECDSA key fingerprint is SHA256:AdmPVk4MJuxZqj7GVrgQPFr1IjU2sNeSjvPOIJIh4PI.
    Are you sure you want to continue connecting (yes/no)? yes
    Warning: Permanently added '10.90.25.12' (ECDSA) to the list of known hosts.
    [email protected]'s password:
    Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 4.2.0-27-generic x86_64)
    
    * Documentation:  https://help.ubuntu.com/
    
    System information as of Thu Feb 11 05:39:35 UTC 2016
    
    System load:  0.22               Processes:              102
    Usage of /:   20.5% of 13.66GB   Users logged in:        0
    Memory usage: 3%                 IP address for eth0:    10.95.25.12
    Swap usage:   0%                 IP address for docker0: 172.18.0.1
    
    Graph this data and manage this system at:
      https://landscape.canonical.com/
    
      Last login: Thu Oct  8 19:03:53 2015
      [email protected]:~#
    

Completing the Installation

  1. Complete the installation by setting the DOCKER_HOST env var to localhost (127.0.0.1). In your terminal, type:

    export DOCKER_HOST=tcp://127.0.0.1:2376`
    
  2. Now that the docker daemon is configured, validate your Docker Blueprint deployment from the command-line by typing:

    # docker run hello-world
    
  3. A few failure messages appear before the docker engine downloads the missing hello-world image and executes it as an ephemeral Docker container, writing "Hello World" and a few more welcome messages before exiting. The entire output is shown below.

    # docker run hello-world
    Unable to find image 'hello-world:latest' locally
    latest: Pulling from library/hello-world
    03f4658f8b78: Pull complete
    a3ed95caeb02: Pull complete
    Digest: sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7
    Status: Downloaded newer image for hello-world:latest
    
    Hello from Docker
    .
    .
    .
    

Step 3: Start Your Minecraft Server

  1. Navigate to Docker Hub and type minecraft into the search field in the upper-right corner. As of the time of this writing, there are over 299 Minecraft repos.
  2. I switched the filter to Official to check for an Official repo from Microsoft or Mojang (the developers of Minecraft), but none were listed. Instead, I set the filter to Downloads and listed the repositories in descending order -- a good proxy for "most popular". Minecraft Repositories Filtered by Downloads

  3. Chose a repo. I chose the kitematic/minecraft repo, but any repo could work. Keep in mind, since anyone can upload a repo to the publicly shared Docker Hub, some repos may not work without reviewing the instructions or carefully examining the Dockerfile. This Dockerfile for kitematic/minecraft lists Michael Chiang (a Docker employee) as the MAINTAINER, so I assume it will be one of the easier repos to get started with.

  4. Type the following command to run the kitematic/minecraft image in attached mode.

    • -e variable asserts that you've accepted the End-User License Agreement.
    • -i and -t must be used together in order to allocate a TTY for the container process, given that the Minecraft server is interactive.
    • -p specifies the port mapping, exposing the Minecraft container port 25565 as port 25565 on the host server, thus enabling external clients to connect to the Minecraft server process running on the container.
    # docker run -i -t -p=25565:25565 -e EULA=true kitematic/minecraft
    
  5. Since Docker is unable to find the image locally, it pulls it from the Docker Hub. After all the images are downloaded, Docker starts up the Minecraft server according the last instruction in the Dockerfile:

    CMD echo eula=true > /data/eula.txt && java -jar /minecraft_server.1.8.7.jar
    
  6. Docker also generates a few Java error messages as the server fails to load the banned user list, banned IP list, and a few other lists. You can ignore these messages. The server prints out a notification when it is ready:

    $ docker run -i -t -p=25565:25565 -e EULA=true kitematic/minecraft
    Unable to find image 'kitematic/minecraft:latest' locally
    latest: Pulling from kitematic/minecraft
    fdd04e6fcc43: Pull complete
    a3ed95caeb02: Pull complete
    547f96a81050: Pull complete
    71696d78950d: Pull complete
    d51bd1d06608: Pull complete
    Digest: sha256:e581df0cca46c88095aa83e249be3dcd5359be6231036a0c479925acbb6dfe5d
    Status: Downloaded newer image for kitematic/minecraft:latest
    [10:27:36] [Server thread/INFO]: Starting minecraft server version 1.8.7
    [10:27:36] [Server thread/INFO]: Loading properties
    [10:27:36] [Server thread/WARN]: server.properties does not exist
    [10:27:36] [Server thread/INFO]: Generating new properties file
    ...
    java.io.FileNotFoundException: banned-players.json (No such file or directory)
    at java.io.FileInputStream.open(Native Method) ~[?:1.7.0_79]
      at java.io.FileInputStream.<init>(FileInputStream.java:146) ~[?:1.7.0_79]
    ...
    [10:27:44] [Server thread/INFO]: Preparing spawn area: 66%
    [10:27:45] [Server thread/INFO]: Preparing spawn area: 84%
    [10:27:46] [Server thread/INFO]: Done (8.401s)! For help, type "help" or "?"
    

Note: Press Control + C on the terminal to signal the running container to stop.

Step 4: Connect Using Your Minecraft Client

The tutorial assumes that your Minecraft client is already registered. Follow these steps to connect to the Minecraft server.

  1. Start the Minecraft Launcher and select the profile you want to use. Click the Play button to start the Minecraft Client. Minecraft Launcher

  2. Choose Multiplayer from the game types to play a network game.

  3. Choose Add Server to specify the server info.

  4. Enter in a Server Name (or leave the default: "Minecraft Server"). For Server Address, enter the server's IP Address and Port <nn.nnn.nnn.nnn>:<12345>. The default Minecraft port value is: 25565. My server address is: 10.90.25.12:25565. Minecraft Edit Server Info

  5. Click Done to connect and drop into the world. Feel free to play around and explore until you are ready to move ahead. Minecraft Client Connect

    When ready, stop the container in the terminal by pressing Control-C. Another way to stop a container is to run docker ps in a separate terminal to list running containers, and run docker kill <containerID> to kill a specific container.

    # docker ps
    CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                      NAMES
    f7d4d28dbcc1        kitematic/minecraft   "/bin/sh -c 'echo eul"   10 minutes ago      Up 10 minutes       0.0.0.0:25565->25565/tcp   backstabbing_poincare
    $ docker kill f7d4d28dbcc1
    f7d4d28dbcc1
    

The docker kill <containerID> approach is helpful when your container is running as a Docker background process. Now let's add the ability to start the game in the same state as when you stopped it.

Step 5: Persist Minecraft Server Settings Between Container Restarts

The following docker run command mounts a volume so that the container can access the server's file system, and runs the Minecraft server container in "detached" mode as a background process.

# docker run -d -p=25565:25565 -e EULA=true -e 'MOTD=Hooray! Docker + Minecraft!' -v /opt/craft:/data kitematic/minecraft

The docker run parameters do the following:

  • -d runs the Minecraft container in detached mode.
  • -e passes a "Message of the Day" environment variable.
  • -v mounts the container's /data volume to the host server's /opt/craft directory. The /data volume is specified in the VOLUME parameter in the kitematic/minecraft image Dockerfile. By enabling the container with write access to the server filesystem, we can persist updates between container restarts.

You can save multiple worlds and access them later by renaming the world directory at /opt/craft/ to something else, such as my-new-world. If the world directory is missing when Minecraft starts, a new world folder is created.

Step 6: Create New Worlds and Experiment

Try creating new worlds by editing /opt/craft/server.properties and restarting the Minecraft container. When you are done exploring, rename the world directory something else (ex.: # mv /opt/craft/world /opt/craft/my-world-2), and repeat the process. Here are a few suggestions:

  • Generate a new level by making up your own level-seed (e.g., level-seed=123123234235342341)
  • Start the server in Creative mode by setting: gamemode=1
  • Create or load a new world by changing the level name: level-name=<filename-of-saved-world>

Check out a detailed listing of Minecraft server properties here.

Reference

These articles helped to inspire and inform my understanding of Minecraft and Docker: