Docker fuer Minecraft-Server: Vor- und Nachteile

Docker fuer Minecraft-Server: Vor- und Nachteile

Nehmen wir an, du betreibst drei Minecraft-Server: Lobby, Survival und Minigames. Jeder braucht seine eigene Java-Version, eigene Plugins, eigene JVM-Einstellungen. Du aktualisierst einen und der andere geht kaputt. Kommt dir bekannt vor? Docker loest genau dieses Problem. Jeder Server lebt in einem isolierten Container mit eigener Umgebung, und sie stoeren sich nicht gegenseitig.

Aber Docker ist kein Allheilmittel. Es hat echte Einschraenkungen, besonders fuer Gameserver, wo jede Millisekunde zaehlt. In diesem Artikel schauen wir uns an, wann Docker wirklich nuetzlich ist und wann es mehr Probleme schafft als es loest.

Warum Docker fuer Minecraft

Die Kernidee von Docker ist Isolation. Jeder Container enthaelt alles, was die Anwendung braucht: Betriebssystem, Java, Server-Software, Plugins. Wenn ein Container abstuerzt, laufen die anderen weiter. Wenn du Java von 17 auf 21 fuer einen Server upgraden musst, betrifft das die anderen nicht.

Reproduzierbarkeit. Ein Docker-Image enthaelt eine fixierte Umgebung. Was auf deiner Testmaschine funktioniert, funktioniert identisch in Produktion. Kein "bei mir hat es funktioniert" mehr - wenn das Image baut, laeuft es gleich auf jedem Host mit Docker.

Einfaches Deployment. Ein neuer Server ist ein docker-compose up -d. Keine manuelle Java-Installation, keine Umgebungsvariablen einrichten, keine JAR-Dateien von Hand herunterladen. Alles steht in der docker-compose.yml, und jedes Teammitglied kann eine identische Umgebung aufsetzen.

Ressourcen-Isolation. Docker nutzt cgroups zur Begrenzung von CPU und Speicher. Wenn der Minigames-Server ploetzlich allen Speicher frisst wegen eines Plugin-Lecks, reisst er nicht Lobby und Survival mit. Der OOM-Killer erwischt nur diesen einen Container.

Einfache Backups. Daten liegen in Docker Volumes oder Bind Mounts. Backup heisst Verzeichnis kopieren. Restore heisst Verzeichnis ersetzen und Container neustarten. Keine komplizierten Prozeduren.

Das itzg/minecraft-server Image

Du musst kein eigenes Dockerfile von Grund auf schreiben. Das itzg/minecraft-server Image ist der De-facto-Standard fuer MC in Docker. Es unterstuetzt Paper, Spigot, Fabric, Forge, Velocity, BungeeCord und dutzende andere Server-Plattformen.

Minimaler Start:

docker run -d \
  --name mc-server \
  -p 25565:25565 \
  -e EULA=TRUE \
  -e TYPE=PAPER \
  -e VERSION=1.21.4 \
  -v mc-data:/data \
  itzg/minecraft-server

Der Container laedt Paper 1.21.4 herunter, akzeptiert die EULA, generiert eine Welt und hoert auf Port 25565. Daten bleiben im Volume mc-data erhalten.

Nuetzliche Umgebungsvariablen:

  • MEMORY=4G - 4 GB fuer die JVM zuweisen (setzt -Xms und -Xmx)
  • TYPE=PAPER - Server-Typ (PAPER, SPIGOT, FABRIC, FORGE, VELOCITY, BUNGEECORD)
  • VERSION=1.21.4 - Minecraft-Version
  • ONLINE_MODE=FALSE - fuer Server hinter einem Proxy (Velocity/BungeeCord)
  • OPS=player1,player2 - Operator-Liste
  • DIFFICULTY=hard - Schwierigkeitsgrad
  • VIEW_DISTANCE=10 - Sichtweite
  • MAX_PLAYERS=100 - Spieler-Limit
  • JVM_OPTS=-XX:+UseG1GC -XX:+ParallelRefProcEnabled - zusaetzliche JVM-Flags

Docker Compose: einzelner Server

Fuer Produktion immer docker-compose statt nacktem docker run verwenden. Eine Konfigurationsdatei ist einfacher zu pflegen, zu versionieren und mit Kollegen zu teilen.

version: "3.8"

services:
  minecraft:
    image: itzg/minecraft-server:latest
    container_name: mc-survival
    restart: unless-stopped
    environment:
      EULA: "TRUE"
      TYPE: PAPER
      VERSION: "1.21.4"
      MEMORY: "6G"
      VIEW_DISTANCE: "12"
      MAX_PLAYERS: "50"
      DIFFICULTY: "hard"
      SPAWN_PROTECTION: "0"
      SNOOPER_ENABLED: "FALSE"
      JVM_OPTS: >-
        -XX:+UseG1GC
        -XX:+ParallelRefProcEnabled
        -XX:MaxGCPauseMillis=200
        -XX:+UnlockExperimentalVMOptions
        -XX:+DisableExplicitGC
        -XX:G1NewSizePercent=30
        -XX:G1MaxNewSizePercent=40
        -XX:G1HeapRegionSize=8M
        -XX:G1ReservePercent=20
        -XX:G1MixedGCCountTarget=4
        -XX:InitiatingHeapOccupancyPercent=15
        -XX:G1MixedGCLiveThresholdPercent=90
        -XX:SurvivorRatio=32
        -XX:+PerfDisableSharedMem
        -XX:MaxTenuringThreshold=1
    volumes:
      - ./server-data:/data
    ports:
      - "25565:25565"
    deploy:
      resources:
        limits:
          memory: 8G
          cpus: "4.0"
        reservations:
          memory: 6G
          cpus: "2.0"

Beachte den Abschnitt deploy.resources. Wir setzen harte Limits: der Container kann nicht mehr als 8 GB RAM und 4 CPU-Kerne nutzen. Gleichzeitig reservieren wir mindestens 6 GB und 2 Kerne. Das Docker-Speicherlimit muss hoeher als die JVM-MEMORY-Einstellung sein - Java verbraucht mehr als die Heap-Groesse (Metaspace, nativer Speicher, Threads).

Volumes: Welten, Plugins, Konfigurationen

Richtige Volume-Organisation ist der Schluessel zur bequemen Verwaltung. Du hast zwei Optionen: Docker Volumes und Bind Mounts.

Bind Mounts (empfohlen fuer MC) - ein bestimmtes Host-Verzeichnis in den Container mappen:

volumes:
  - ./server-data:/data

Alle Server-Dateien landen in ./server-data auf dem Host. Du kannst Konfigurationen direkt bearbeiten, Plugins per scp kopieren, Backups mit normalem rsync machen.

Docker Volumes - von Docker verwalteter Speicher:

volumes:
  - mc-data:/data

volumes:
  mc-data:

Daten liegen in /var/lib/docker/volumes/mc-data/_data. Isolierter, aber weniger praktisch fuer direkten Zugriff.

Fuer Minecraft-Server sind Bind Mounts praktischer. Typische Struktur:

./server-data/
  world/           # Oberwelt
  world_nether/    # Nether
  world_the_end/   # Ende
  plugins/         # Plugin-JARs und Konfigurationen
  server.properties
  paper-global.yml
  paper-world-defaults.yml
  ops.json
  whitelist.json

Um ein Plugin hinzuzufuegen, kopiere einfach die JAR nach ./server-data/plugins/ und starte den Container neu.

Netzwerk: Host vs. Bridge

Das ist eine kritische Entscheidung fuer Gameserver.

Bridge (Standard). Docker erstellt ein virtuelles Netzwerk. Traffic laeuft ueber NAT. Fuer die meisten Webanwendungen kein Problem. Fuer Minecraft - zusaetzliche Latenz. NAT verarbeitet jedes Paket, und bei 100 Spielern ist das spuerbar. Vorteil: Port-Forwarding, Netzwerk-Isolation.

Host Mode. Der Container nutzt den Netzwerk-Stack des Hosts direkt. Kein NAT, kein Overhead. Pakete gehen direkt zum Java-Prozess. Nachteil: keine Netzwerk-Isolation, der Container belegt Host-Ports.

services:
  minecraft:
    image: itzg/minecraft-server:latest
    network_mode: host
    environment:
      EULA: "TRUE"
      TYPE: PAPER
      SERVER_PORT: "25565"

Fuer Gameserver: Host Mode verwenden. Der 1-3 ms Latenzunterschied mag trivial klingen, aber bei 20 Ticks pro Sekunde und dutzenden Spielern summiert sich das. Bridge Mode ist sinnvoll, wenn du Netzwerk-Isolation zwischen Containern brauchst oder mehrere Server auf verschiedenen Ports auf derselben Maschine betreibst.

Performance: Java in Containern

Java laeuft gut in Docker, aber es gibt Feinheiten.

Speicher. Die JVM erkennt cgroup-Limits und bestimmt den verfuegbaren Speicher korrekt (seit Java 10+ eingebaut). Wenn du MEMORY=6G mit einem Docker-Limit von 8G setzt, funktioniert alles. Aber setze das Docker-Limit nicht gleich der Heap-Groesse - die JVM braucht Speicher ueber den Heap hinaus (200-500 MB fuer Metaspace, Threads, native Puffer).

CPU. Docker nutzt CPU Shares und CFS Quota. Standardmaessig teilen sich Container die CPU proportional. Ein hartes Limit (cpus: "4.0") kann zu Throttling fuehren - eine GC-Pause kann nicht mehr als 4 Kerne nutzen, selbst wenn sie frei sind. Fuer einen einzelnen Server ist es besser, kein hartes CPU-Limit zu setzen und stattdessen cpu_shares zur Priorisierung zu verwenden.

Festplatte. Overlay2 (Dockers Standard-Dateisystem) fuegt minimalen Lese-Overhead hinzu. Aber bei intensivem Schreiben (Weltspeicherung, Logs) kann der Unterschied spuerbar sein. Bind Mounts umgehen das Overlay - direkter Zugriff auf das Host-Dateisystem. Ein weiterer Grund, Bind Mounts fuer Daten zu verwenden.

Netzwerk. Bridge vs. Host haben wir bereits besprochen. Wichtig: Im Bridge-Modus kannst du keine Jumbo Frames im Docker-Netzwerk aktivieren - die MTU ist fest. Bei grossen Mengen an Chunk-Daten, die zum Spieler fliegen, kann das die Paketanzahl erhoehen.

Multi-Server: Velocity + Paper

Die wahre Staerke von Docker zeigt sich bei Multi-Server-Setups. Eine einzige docker-compose.yml beschreibt die gesamte Infrastruktur.

version: "3.8"

services:
  proxy:
    image: itzg/bungeecord:latest
    container_name: mc-velocity
    restart: unless-stopped
    environment:
      TYPE: VELOCITY
      MEMORY: "512M"
    volumes:
      - ./velocity-data:/server
    ports:
      - "25565:25577"
    deploy:
      resources:
        limits:
          memory: 1G

  lobby:
    image: itzg/minecraft-server:latest
    container_name: mc-lobby
    restart: unless-stopped
    environment:
      EULA: "TRUE"
      TYPE: PAPER
      VERSION: "1.21.4"
      MEMORY: "2G"
      ONLINE_MODE: "FALSE"
      SERVER_PORT: "25566"
    volumes:
      - ./lobby-data:/data
    expose:
      - "25566"
    deploy:
      resources:
        limits:
          memory: 3G

  survival:
    image: itzg/minecraft-server:latest
    container_name: mc-survival
    restart: unless-stopped
    environment:
      EULA: "TRUE"
      TYPE: PAPER
      VERSION: "1.21.4"
      MEMORY: "6G"
      ONLINE_MODE: "FALSE"
      SERVER_PORT: "25567"
    volumes:
      - ./survival-data:/data
    expose:
      - "25567"
    deploy:
      resources:
        limits:
          memory: 8G

  minigames:
    image: itzg/minecraft-server:latest
    container_name: mc-minigames
    restart: unless-stopped
    environment:
      EULA: "TRUE"
      TYPE: PAPER
      VERSION: "1.21.4"
      MEMORY: "4G"
      ONLINE_MODE: "FALSE"
      SERVER_PORT: "25568"
    volumes:
      - ./minigames-data:/data
    expose:
      - "25568"
    deploy:
      resources:
        limits:
          memory: 6G

Beachte, dass Paper-Server expose statt ports verwenden - sie sind von aussen nicht erreichbar, nur ueber Velocity. Der Proxy veroeffentlicht Port 25565, nimmt Verbindungen an und leitet sie ueber Docker DNS an interne Server weiter (Service-Namen: lobby, survival, minigames).

In velocity.toml werden Server ueber Docker-Service-Namen referenziert:

[servers]
lobby = "lobby:25566"
survival = "survival:25567"
minigames = "minigames:25568"
try = ["lobby"]

Docker loest lobby automatisch zur Container-IP auf. Wenn ein Container neustartet und eine neue IP bekommt, aktualisiert sich das DNS.

Backups von Docker-Servern

Ein Backup eines Servers in Docker bedeutet, das Bind-Mount-Verzeichnis zu sichern. Aber es gibt einen wichtigen Punkt: du solltest keine Weltdateien kopieren, waehrend der Server schreibt.

Der richtige Ansatz:

#!/bin/bash
# backup.sh

BACKUP_DIR="/backups/minecraft"
DATE=$(date +%Y%m%d_%H%M%S)

# Autosave deaktivieren und finalen Save erzwingen
docker exec mc-survival rcon-cli save-off
docker exec mc-survival rcon-cli save-all
sleep 5

# Daten kopieren
tar czf "$BACKUP_DIR/survival-$DATE.tar.gz" ./survival-data/

# Autosave wieder aktivieren
docker exec mc-survival rcon-cli save-on

echo "Backup abgeschlossen: survival-$DATE.tar.gz"

Das itzg/minecraft-server Image enthaelt rcon-cli - du kannst Serverbefehle ausfuehren, ohne dich an die Container-Konsole anzuhaengen.

Zur Automatisierung - cron:

0 */4 * * * /opt/minecraft/backup.sh >> /var/log/mc-backup.log 2>&1

Backups alle 4 Stunden. Fuer groessere Server ziehe inkrementelle Backups mit borgbackup in Betracht.

Sicherheit

Docker-Container laufen standardmaessig als root innerhalb des Containers. Fuer einen Minecraft-Server ist das nicht kritisch (der Container ist isoliert), aber Vorsicht ist besser.

Nicht-Root-Benutzer. Das itzg/minecraft-server Image unterstuetzt UID/GID:

environment:
  UID: 1000
  GID: 1000

Read-Only-Dateisystem. Du kannst das Root-Dateisystem des Containers schreibgeschuetzt machen und Schreibzugriff nur fuer Daten erlauben:

services:
  minecraft:
    image: itzg/minecraft-server:latest
    read_only: true
    tmpfs:
      - /tmp
    volumes:
      - ./server-data:/data

Capabilities entfernen. Unnoetige Linux Capabilities entfernen:

services:
  minecraft:
    image: itzg/minecraft-server:latest
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - SETUID
      - SETGID

Niemals --privileged verwenden. Niemals. Das gibt dem Container vollen Zugriff auf den Host.

Images aktuell halten. docker-compose pull && docker-compose up -d aktualisiert Images und erstellt Container mit neuen Versionen neu.

Wann Docker nicht die richtige Wahl ist

Docker ist keine universelle Loesung. Hier sind Situationen, in denen es Komplexitaet ohne Nutzen hinzufuegt:

Einzelner Server auf dedizierter Maschine. Wenn du einen VPS mit einem Minecraft-Server hast, bietet Docker minimale Vorteile. Java ist bereits in der JVM isoliert, und Docker fuegt eine Abstraktionsschicht hinzu. Es ist einfacher, Java direkt zu installieren und ueber systemd zu starten.

Performance-kritische Setups. Wenn du jeden Tick aus einem Server mit 200+ Spielern herausquetschst, zaehlt jeder Overhead. Docker mit Bridge-Netzwerk fuegt Latenz hinzu. Overlay FS fuegt Schreibverzoegerung hinzu. cgroups fuegen CPU-Scheduling-Overhead hinzu. Mit Host Mode und Bind Mounts ist der Unterschied minimal, aber er existiert.

Keine DevOps-Erfahrung. Wenn du mit Docker nicht vertraut bist, wird das Debuggen von Problemen im Container schwieriger als auf einer nackten Maschine. Logs sind an einem anderen Ort, das Dateisystem ist geschichtet, das Netzwerk ist virtuell. Lerne Docker zuerst an weniger kritischen Projekten.

Shared Hosting. Panels wie Pterodactyl verwenden Docker bereits unter der Haube. Eine weitere Docker-Schicht darueber zu legen ergibt keinen Sinn.

Nuetzliche Befehle

Schnellreferenz fuer den taeglichen Betrieb:

# Container-Status
docker-compose ps

# Server-Logs (letzte 100 Zeilen, Echtzeit)
docker-compose logs -f --tail=100 survival

# Server-Konsole ueber rcon
docker exec mc-survival rcon-cli

# Einen Server neustarten ohne die anderen zu stoppen
docker-compose restart survival

# Images aktualisieren
docker-compose pull
docker-compose up -d

# Ressourcenverbrauch
docker stats

# Plugin in den Server kopieren
cp my-plugin.jar ./survival-data/plugins/
docker-compose restart survival

Zusammenfassung

Docker eignet sich hervorragend fuer Multi-Server-Setups, Testumgebungen und Situationen, in denen Reproduzierbarkeit wichtig ist. Das itzg/minecraft-server Image deckt 95% der Anwendungsfaelle ab. Host Networking entfernt den Netzwerk-Overhead, Bind Mounts geben direkten Dateizugriff, und cgroups schuetzen vor Speicherlecks in Plugins.

Aber wenn du einen Server auf einer Maschine hast - ein einfacher systemd-Service ist einfacher und etwas schneller. Docker loest Skalierungs- und Reproduzierbarkeitsprobleme. Wenn du diese Probleme nicht hast, sucht die Loesung nach einem Problem.

Starte mit docker-compose fuer einen Testserver. Lass ihn eine Woche laufen, beobachte die Performance, gewoehne dich an den Workflow. Wenn alles passt - migriere die Produktion. Wenn nicht - du verlierst nichts, Weltdateien lassen sich in ein paar Minuten zurueck auf einen nackten Server verschieben.


Schützen Sie Ihren Server vor DDoS-Angriffen

Kostenloser Schutz mit 5-Minuten-Einrichtung. 1 TB Traffic inklusive.

Kostenlos testen


Weitere Artikel