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)
Docker newbies, Minecraft enthusiasts, and anyone interested in seeing how Blueprints can quickly deploy applications.
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. If you don’t have a CenturyLink Cloud account yet, head over to our website and activate an account.
- 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.
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
Login to the Control Portal and select Blueprint Library from the menu drop-down.
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.
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.
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".
Click the "Docker Engine" tile to display the deployment page.
Deploy Blueprintbutton. (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.
Customize the Blueprint
On the Customize page, there are several required fields to update.
- In the Password field(s), enter a secure root password.
- For the Group drop-down, choose a group. The Default group is fine. In my case, I chose my Development group.
- In the Network drop-down, just use the first vLAN in the list, or choose a different one if you prefer.
- Under Primary DNS, select Manually Specify. The field below displays the Primary DNS address.
- Do the same for the Secondary DNS, choose Manually Specify.
- For Server Type, select Standard (default).
- For Service Level, select Standard (default).
Under Customize Server Name(s), give your server a unique 4-6 letter alias. I changed the default
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:
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:
Now click the
next: step 2button.
Review your Customizations
- On the Review page, verify that everything looks right.
- Then, click the
deploy blueprintbutton to kick off the Blueprint deployment process.
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.
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
- Navigate back to the data center and group containing your new server. In the Control Portal, click the
Dashboardlink in the breadcrumb trail.
- Then click the "chip" icon on the left navigation bar to show the list of available data centers.
- Choose your data center and group from the list. In my case, it's the Development group in
- Select your new server from the group list to display the server dashboard.
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.
Now start the VPN connection.
Once your VPN tunnel is connected successfully, you'll see a notification modal.
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
rootisn't a best practice. Please refer to this Knowledge Base article for recommendations on securing your server.
Open up your terminal (or PowerShell on Windows) and type:
# ssh root@<nn.nnn.nnn.nn>
<nn.nnn.nnn.nn>represents the internal IP address of your new server. This is displayed on the server dashboard under Server Info).*
Cut and paste the IP address starting with
Mine looked like this:
Davids-MBP-3:~ davidgardner$ ssh email@example.com 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. firstname.lastname@example.org'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 root@IL1CLABMINE01:~#
Completing the Installation
Complete the installation by setting the
DOCKER_HOSTenv var to localhost (127.0.0.1). In your terminal, type:
Now that the docker daemon is configured, validate your Docker Blueprint deployment from the command-line by typing:
# docker run hello-world
A few failure messages appear before the docker engine downloads the missing
hello-worldimage 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
- Navigate to Docker Hub and type
minecraftinto the search field in the upper-right corner. As of the time of this writing, there are over 299 Minecraft repos.
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".
Chose a repo. I chose the
kitematic/minecraftrepo, 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/minecraftlists Michael Chiang (a Docker employee) as the MAINTAINER, so I assume it will be one of the easier repos to get started with.
Type the following command to run the
kitematic/minecraftimage in attached mode.
-evariable asserts that you've accepted the End-User License Agreement.
-tmust be used together in order to allocate a TTY for the container process, given that the Minecraft server is interactive.
-pspecifies the port mapping, exposing the Minecraft container port
25565on 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
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
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 "?"
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.
Start the Minecraft Launcher and select the profile you want to use. Click the Play button to start the Minecraft Client.
Choose Multiplayer from the game types to play a network game.
Choose Add Server to specify the server info.
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:
Click Done to connect and drop into the world. Feel free to play around and explore until you are ready to move ahead.
When ready, stop the container in the terminal by pressing
Control-C. Another way to stop a container is to run
docker psin 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
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
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
docker run parameters do the following:
-druns the Minecraft container in detached mode.
-epasses a "Message of the Day" environment variable.
-vmounts the container's
/datavolume to the host server's
/datavolume is specified in the
VOLUMEparameter in the
kitematic/minecraftimage 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.,
- Start the server in Creative mode by setting:
- Create or load a new world by changing the level name:
Check out a detailed listing of Minecraft server properties here.
These articles helped to inspire and inform my understanding of Minecraft and Docker: