Running minecraft server in a Docker container

Table of contents

Minecraft is the most-sold video game of all time, but for some reason running your own minecraft server with sensible defaults and resource limits involves a lot of research and confusing errors. Here is a quick guide to running your own minecraft server in a docker container by building spigot from scratch using BuildTools so you can automate updates and build the versions you want.

Building the container image

This step is rather simple. I assume you have a recent version of docker and the docker-compose plugin installed already. To build our container image, we first create a file called Dockerfile:

FROM debian:stable as builder
RUN mkdir /build
WORKDIR /build
RUN apt update
RUN apt install bash ca-certificates wget git -y # install first to avoid openjdk install bug
RUN apt install openjdk-17-jre-headless -y
RUN wget -O BuildTools.jar https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar
RUN git config --global --unset core.autocrlf || exit 0
RUN java -jar BuildTools.jar --rev latest #change to specific version if you don't want latest STABLE release
RUN mv spigot-*.jar spigot.jar

FROM debian:stable
RUN apt update
RUN apt install openjdk-17-jre-headless bash -y
RUN mkdir /server /minecraft
RUN adduser --system --shell /bin/bash --group minecraft
COPY --from=builder --chown=minecraft:minecraft /build/spigot.jar /server
RUN chown -R minecraft:minecraft /server
RUN chown -R minecraft:minecraft /minecraft
USER minecraft
WORKDIR /minecraft
CMD ["java", "-Xms4G", "-Xmx4G", "-XX:+UseG1GC", "-jar", "/server/spigot.jar", "nogui"]

This file will build the latest stable version of the spigot server. When minecraft releases new updates, it may take some time before the new server version becomes stable. If you want to build a different version, you can change the --rev latest in line 9 to for example --rev 1.20 to build a server for minecraft version 1.20 specifically, even if that is not the current latest stable version. A list of all versions available can be found here.

Automating with docker-compose

To ensure we don't have to do everything manually, we create a file called docker-compose.yml in the same directory as our Dockerfile:

version: "3.8"
services:
  server:
    image: minecraft_server
    build: .
    container_name: minecraft-spigot
    restart: on-failure
    stdin_open: true
    tty: true
    ports:
      - 25565:25565
    volumes:
      - $PWD/server_data:/minecraft
    deploy:
      resources:
        limits:
          cpus: "3.0"
          memory: 6G

This contains all the information about the state we want our server to have, such as being accessible on port 25565 (default minecraft port), where it should store it's data and enabling automatic restarts after crashes.

Setting up the server

Now that these two files are in place, we can prepare our server. Don't worry, you'll only do this once, no need to run this again later when you update to new versions.

First, create a directory called server_data next to the docker-compose.yml file. Inside it, create a file called eula.txt with contents eula=true inside. Lastly, ensure the server_data directory and all files inside are writable by all users.

On linux, this can be done quickly from the terminal:

mkdir server_data
echo "eula=true" > server_data/eula.txt
chmod -R 777 server_data

As a last step, build the container image:

docker compose build

This may take a while as it builds the image for our minecraft server container. Luckily, it is fully automatic so you don't have to stick around to watch it if you have better things to do.

Starting and stopping the server

Starting can be done by simply calling

docker compose up -d

The first time you start the server will take a little longer than normal because it has to generate the world. Later starts will be faster. Once the server has started for the first time, you can adjust server settings like name, player limit and whitelist settings in server_data/server.properties.

To stop it again, run

docker compose down -v

If you want to access the server directly, for example to run commands or read the logs, use

docker attach minecraft-spigot

Important: To detach from your server again press ctrl+p followed by ctrl+q. If you press ctrl+c instead, the server will shutdown!

Adjusting resource limits

The sample config above runs the server with 4gb of memory and 3 cpu cores, with a hard memory limit of 6gb for the entire container. These values are considered sane minimums for recent minecraft servers. To change the amount of cpu available to the server, change the line cpus: 3.0 in docker-compose.yml to the desired value. This setting is relative, so even values like 0.5 for "half the power of one cpu core" can be configured.

To change the memory limit, you need to change two files. First, set the total limit you want the container to have by adjusting the line memory: 6g in docker-compose.yml. This is the limit your server will never physically exceed (if it does, it will crash and restart instead). Next, change the values for "Xms4G" and "Xmx4G" in the last line of Dockerfile. The number in those values represents the amount of gb memory available to the minecraft server process (so "Xmx6g" would give it 6gb memory for example). Make sure to change both Xms4G and Xmx4G and give them the same value. Note that this should be 1-2gb BELOW the total limit you configured in docker-compose.yml, because the jvm inside the container may exceed the limit temporarily.

Updating to a new Minecraft version

First, build a docker image for the new minecraft version:

docker compose build

This may take a while. Your old server can stay running while you do this.

Once this process is done, simply restart your server:

docker compose down -v
docker compose up -d

Making backups

If you want to make a backup of your minecraft server, simply stop it and copy the entire server_data directory somewhere safe (or put it in a zip/rar/7z archive). Make sure the server is not running while you do this!

More articles

Javascript type coercion, truthy/falsy values and strict/loose equality

For when your expressions evaluate to "sort of true" and true thinks it's a number

Use let instead of var in javascript

And how it prevents hoisting from shooting you in the foot

You may not need jQuery

But it's not a clear cut case

Modern linux networking basics

Getting started with systemd-networkd, NetworkManager and the iproute2 suite

Understanding how RAID 5 works

Striking a balance between fault tolerance, speed and usable disk space

A practical guide to filesystems

Picking the best option to format your next drive