Running Minecraft servers from Docker header image

Running Minecraft servers from Docker


Since picking up docker last year it's completely changed how I set up projects, home network applications, and now how I deploy Minecraft servers for friends. Gone are the days of figuring out which version of JDK I need on an ubuntu box and remembering the commands to reconnect to a screen session.

In this post I'll walk you through setting up a few different Minecraft servers, running vanilla, Paper and Forge, all on the same host. In a follow up post I go through routing to these server on the same port.

Before we get started, I'd recommend having some basic command line and linux knowledge. In addition, pre-existing docker and docker-compose experience is a bonus but not required.

The Environment

Docker being docker, you can run this on Windows, Linux, macOS - A spare computer, the cheapest DigitalOcean droplet, heck even a Raspberry Pi! Just make sure your systems up to date, has docker and docker-compose installed. Here's what we'll be using:

Install Docker & Docker Compose

Go ahead and follow the Docker install steps for the platform you wish to run the server on. Here's the Ubuntu guide.

Handy tip: if you're on linux, make sure you add your user to the docker group:

sudo usermod -aG docker $USER

You'll need to log out and back in again after.

Now you should be all set! To double check, run the following:

# Check the Docker version
docker -v

# Check Docker Compose version
docker compose -v

Creating our first server with Docker Compose

It's vary rarely I'll run a docker image with the docker command line, remembering the command and all the arguments I need is a faff. So lets define our first Minecraft server, a simple vanilla server, in a docker compose YAML file.

First up, we'll create a directory to contain all our files including the Minecraft servers:

# Create directory
mkdir minecraft-servers

# cd into the directory
cd minecraft-servers

# Create a docker-compose.yaml file
touch docker-compose.yaml

# Create a directory to store our vanilla server files in
mkdir -p vanilla/server

Go ahead and open the docker-compose.yaml. You can use nano, vim or even connect VS Code with SSH. In here we want to define our vanilla service:

version: "3.8"
services:
vanilla:
container_name: vanilla
image: itzg/minecraft-server
ports:
- 25565:25565
environment:
EULA: "TRUE"
MEMORY: 2G
volumes:
- ./vanilla/server:/data
restart: unless-stopped
tty: true
stdin_open: true

This is the bare minimum configuration, but there's a whole bunch of options you can define such as ops, whitelist, difficulty etc, all documented here. Now we're ready to test our first server works, so lets spin it up!

# Start the service
docker compose up

You'll should now see the server begin to start up. It'll take a little while on the first launch as it downloads the image and generates server files. You'll know it's ready when you see:

vanilla | [17:22:18] [Server thread/INFO]: Done (13.045s)! For help, type "help"

Congratulations, you can now connect to your new server using the server IP or localhost if you're running it on the same machine! To exit, you can use ctrl + c in the terminal.

If you take a look in the vanilla/server directory we created earlier, you'll see it's generated all the server files needed. You can edit the server.properties to make changes to the servers configuration, however I'd recommend adding these to the environment block in our docker-compose.yaml and simply deleting the server.properties file. This file will be recreated when you next start the server with your newly added environment options.

Adding different server types

So we have our vanilla server set up, but if we want to use some plugins to manage permissions or add prefixes to usernames in chat, we'll need to use a different server type. For a vanilla server that you can connect an unmodded client to, you have a few options: Bukkit, Spigot and Paper. We'll use Paper for the sake of this post.

First, lets create a directory to keep our plugins in:

mkdir vanilla/plugins

You can go ahead and add any plugins to this directory and we'll configure the container to mount these. You can also leave it blank for now and once we start the server you'll see bStats automatically added by Paper.

Now in our docker-compose.yaml file, we'll add a new entry to the environment block of our vanilla service, defining the server type. We'll also mount the plugins directory we just created.

version: "3.8"
services:
vanilla:
container_name: vanilla
image: itzg/minecraft-server
ports:
- 25565:25565
environment:
# Add our server type
TYPE: PAPER
EULA: "TRUE"
MEMORY: 2G
volumes:
- ./vanilla:/data
# Mount our plugins directory
- ./vanilla/plugins:/plugins
restart: unless-stopped
tty: true
stdin_open: true

Lets spin up the updated server, you'll see it download the new jar and create some new files.

# Start the service
docker-compose up

Now you should be able to edit the configuration files for the plugins you added, log in and see them working!

Proxying and Routing domains to Minecraft Servers

In a follow up post, I go through adding adding itzg/mc-router to route domains to multiple servers, all using the default 25565 port. No more ugly server addresses with different ports! You can read about that here

Closing thoughts

So in this post we've created a Vanilla server, maintained and managed through docker. I highly recommend reading through the docs for this image as there's tons of configuration available, including support for modded servers. You can add more servers to this config and use something like BungieCord or PaperMC Waterfall switch servers ingame.