Configuring a Docker-Based Home Server - Setup and Dockerizing Minecraft
In running a home Minecraft and movie server in the past, I encountered a few annoying issues that made maintaining the server a chore, and caused a ton of issues when it came to reliability, when all I really wanted to do was enjoy the services I was providing.
To cure this ailment, I’m setting out to find a good low-maintenance solution to self-hosting and using it as an excuse to play around with Docker. While I have a bit of experience setting up a development environment in docker, I have not used the docker-compose
feature or tried to manage services with it, which is what I plan to do in this project.
Setting Up The Server Box and Dev Setup
My server box is a hodgepodge of my old desktop parts, which probably needs to be upgraded soon, but that’s a problem for future me. Here is a parts list:
Part | Model |
---|---|
Processor | intel i5 4670k |
RAM | Corsair LPX 16Gb (2x8) DDR3 1666 Mhz |
Motherboard | MSI B85M-E45 (m-atx, LGA 1150) |
SSD (OS) | ADATA Premier SP550 240GB |
HDD (Storage) | Seagate ST4000VN008 |
Power Supply | EVGA 100-BT-0450-K1 |
Case | Silverstone SST-SG13B-Q-USA |
The box is running Ubuntu 20.04 LTS in the 240GB SSD with the XFCE desktop. The 4TB Seagate drive will be my main storage drive for anything that doesn’t need quick access (movies, music, GitLab, other large storage). I’ve got the server hooked up to my secondary monitor over HDMI, and I’m using Synergy to share my keyboard/mouse with my desktop. Though I have the server desktop available to me, I plan on doing most of the setup over SSH. I just like having the option to do good ole’ point and click, and it’s very useful for organizing/moving/renaming files, which I’ll need to do a lot of for the movie server.
Once I’ve got Synergy setup to share my mouse and keyboard and all the software updated, we can start setting up some development stuff both on my desktop and on the server.
Using the Ubuntu Linux instructions on the Docker website, I installed both Docker and docker-compose
on my desktop (also Ubuntu Linux) and my server box. I’ll be doing the bulk of development and testing on my desktop, so I’ve set up a Git repo to keep track of the changes and provide a source of entertainment for some of the more experienced readers.
Dockerizing A Minecraft Server
Once the Git repo is created, we’ll go ahead and populate it with the usual suspects: A README I intend to spruce up but never do, a Dockerfile and docker-compose.yml
, and a scripts
.
To describe the Docker container setup we’ll need, I’ve pasted the Dockerfile below, and I’ll go through it:
FROM openjdk:16.0.1-slim
LABEL maintainer="Kevin Koffroth <ktkoffroth@gmail.com>"
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update -q && \
apt-get upgrade -yq && \
apt-get install -yq git wget
CMD ["bash"]
Since all Minecraft servers for version 1.17+ will run on Java 16, we’ll use the OpenJDK 16 Slim image from Docker Hub as our base image. After setting the usual maintainer (me!) and arguments (DEBIAN_FRONTEND=noninteractive
disables dialog boxes during the build), I update and upgrade the image.
Next, I install git
and wget
, which are needed to build the Minecraft server I’ll be using, SpigotMC. Part of the functionality I planned for this server is the ability to update to the latest version of SpigotMC semi-automatically. The Spigot community uses a build tool called, erm, BuildTools
to generate the server Jars, which requires git
to function. We’ll see how wget
is used momentarily.
Managing the Container with docker-compose
Now we enter unexplored territory for me. Having never used docker-compose
, the first place I went for guidance was the official documentation, which gave me a great overview of the purpose and features of it.
You may be thinking to yourself, “why is this guy using docker-compose
for production?”, well the answer is that I felt it was all I needed for container management. While I could spend a good chunk of time learning and setting up Kubernetes to manage my relatively small fleet of services, I felt I could learn a bit more about DevOps and containers by building my own automation on top of the admittedly limited (by comparison) feature set of docker-compose
.
Now, on to the interesting bits. Below, I’ve pasted the contents of the docker-compose.yml
file for inspection:
version: "3.9"
# Bridge Network for MC server and any related services
networks:
bridge-network:
external:
name: bridge-network
# Services definition
services:
# Minecraft server
mc:
image: "ktkoffroth/spigot-mc:0.4"
restart: always
stdin_open: true
tty: true
env_file: ./env/mc-variables.env
networks:
- bridge-network
volumes:
- /media/ktkoffroth/NAS/MC:/root/MC
- ./scripts:/root/scripts
ports:
- "25565:25565"
entrypoint: /root/scripts/entrypoint.sh
First, I’ve set up a bridge network for the docker-compose
instance by creating a regular docker network, called bridge-network
, and declaring it in the compose file using the external key. This custom network is mostly for future proofing, as it will allow me to connect containers from multiple docker-compose instances together and provide services for each other.
Next is the meat and potatoes of the file, the services
definition. Now, I have one service called mc
, which uses the latest build of the docker container we defined above, hosted on my Docker Hub account. In the future, if I would like to run a few more servers, such as ones running different mod packs, I could simply copy and modify this service definition and have it up relatively quickly.
restart
, stdin_open
, and tty
are all relatively common parameters for a service, which restart the service (always), open up stdin, and enable TTY respectively.
env_file
points to a .env
file with environment variables we’d like the container to use. In this case the file we point to, mc-variables.env
has one environment variable called VANILLA_VERSION
, which controls the version of Spigot we’d like to build and run in the container. We’ll see how this is used in a bit:
# Spigot Version
VANILLA_VERSION=1.17.1
networks
attaches the service to the network we defined earlier. Again, more future proofing.
volumes
lets us provide data on the host OS to the container. In this case, I provide the path to the Minecraft server files, on the 4 TB NAS drive mentioned earlier, and the scripts
directory from the repository.
ports
allows the user to route port-specific traffic from the docker container to the host OS. In this case, Minecraft uses port 25565, so I simply route 25565 from the container to the host.
entrypoint
specifies a script to run on container startup. The script in question is as below:
#!/bin/bash
# go to /root/MC/BuildTools directory, get latest version of spigot BuildTools,
# and run BuildTools to get latest version of spigot server .jar
cd /root/MC/BuildTools/
wget -N https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar
git config --global --unset core.autocrlf
rm -rf apache-maven-* BuildData Bukkit CraftBukkit Spigot work BuildTools.log.txt
java -jar BuildTools.jar --rev $VANILLA_VERSION
# move server .jar to server files directory, overwriting if necessary
mv -u spigot-$VANILLA_VERSION.jar ../Spigot-Vanilla/spigot-$VANILLA_VERSION.jar
# move to server files directory, edit start.sh for AutoRestart plugin, and run start command
cd ../Spigot-Vanilla
sed -i "s/^java.*/java -Xms2G -Xmx2G -XX:+UseG1GC -jar spigot-$VANILLA_VERSION.jar/" ./start.sh
java -Xms2G -Xmx2G -XX:+UseG1GC -jar spigot-$VANILLA_VERSION.jar
This script is semi-automatically updates the server for me. Whenever I run docker-compose up
, this script grabs the latest version of BuildTools
from the community site, sets up the build environment, and runs it with the requested server version as the environment variable I set earlier. It then moves the newly created server Jar to the correct location, edits the start.sh
script the server uses to periodically restart (it helps with server performance), and finally launches the server jar!
Now, all that needs to be done to upgrade the server to the latest version is to change the .env
file, kill the docker process (e.g. docker kill
) and restart docker-compose
using docker-compose up -d
!
Conclusion
This was a relatively short project and I learned a ton about docker-compose
. This setup also has tons of room for improvement. For instance, I’d like my server to automatically detect an update to the Spigot server, and apply it without having to manually change a version number. It would also be helpful if it could do the same for my server plugins. Oh well, I’ll let future me figure that stuff out.