Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Setup

The minecraft server as well as other sattelite services like Prometheus and Grafana are set up using Docker and Docker Compose.

Docker Image

The Docker image for the Minecraft server consists of a JDK environment and several utilities inside the image. It is based on the minebase image. This image is a framework foundation to build specific Minecraft servers on top of it. It contains tools like

  • rconcli to access the server console directly from inside the container via RCON,
    • healthcheck to be able monitor the service health of the Minecraft server via Docker,
  • a full backup system for your worlds based on rcon,
  • as well as a framework of scripts to manage lifetime routines and startup of the server

For the Minecraft server, we are using PaperMC. Here you can find the repository of the PaperMC Docker image.

Docker Compose Stack

To orchestrate the Minecraft server and related services, we are using Docker Compose, which allows defining configurations for Docker containers, volumes and networks with a single or multiple YAML files.

The stack comprises the following services:

  • the main PaperMC Minecraft server
  • a second “staging” instance of PaperMC used to test plugin compatibility for new versions
  • Prometheus as backend and database for monitoring
  • Grafana as frontend for monitoring
  • Portainer to have an easy web interface to manage and interact with containers on the server
  • Caddy which serves a fallback website to mc.zekro.de when the server is down for restart or maintenance

Below, you can see the Docker Compose definition for the stack. This can be outdated though, here you can find the file from the repository which is synced with the server, so it will be up to date.

As you can see, I am using traefik proxy as HTTP proxy, which is managed in another Docker Compose stack. Thats why the external core_public network is used to make the web services in this stack available to the internet.

networks:
  core_public:
    external: true
  minecraft:

volumes:
  mysql-data:

secrets:
  minecraftrclone:
    file: rclone.conf
  netrc:
    file: .netrc

services:
  spigot:
    image: "ghcr.io/zekrotja/papermc-docker:zulu-23"
    restart: unless-stopped
    environment:
      VERSION: "1.21.10"
      BUILD: "latest"
      CACHE_DOWNLOAD: "true"
      XMS: 4G
      XMX: 8G
      MAX_AGE_BACKUP_FILES: 12d
      PRE_START_BACKUP: "true"
      POST_START_BACKUP: "false"
      BACKUP_SUCCESS_SCRIPT: 'curl -X PUT --netrc-file /run/secrets/.netrc -H "Title: ✅ Minecraft Backup Successfull" "https://ntfy.zekro.de/minecraft_backups"'
      BACKUP_FAILED_SCRIPT: 'curl -X PUT --netrc-file /run/secrets/.netrc -H "Title: ❌ Minecraft Backup Failed" -d "$$MESSAGE" "https://ntfy.zekro.de/minecraft_backups"'
      DEBUG_MODE: "true"
    ports:
      - "24693:25565"
      - "25575:25575" # rcon
      - "24454:24454/udp" # voice chat
    networks:
      - "core_public"
      - "minecraft"
    volumes:
      - "./spigot/config:/etc/mcserver/config"
      - "./spigot/plugins:/etc/mcserver/plugins"
      - "./spigot/worlds:/etc/mcserver/worlds"
      - "./spigot/locals:/etc/mcserver/locals"
    secrets:
      - source: minecraftrclone
        target: rcloneconfig
      - source: netrc
        target: .netrc
    healthcheck:
      test: >
        /usr/bin/healthcheck -addr localhost:25565 -validateResponse
        && test "$(curl -o /dev/null -sLw "%{response_code}\n" http://127.0.0.1:8100)" -eq 200
      start_period: 1m
      start_interval: 5s
      interval: 30s
      timeout: 10s
      retries: "10"
    labels:
      traefik.enable: "true"
      traefik.http.routers.spigot.entrypoints: "https"
      traefik.http.routers.spigot.tls: "true"
      traefik.http.routers.spigot.tls.certresolver: "le"
      traefik.http.routers.spigot.rule: "Host(`mc.zekro.de`)"
      traefik.http.routers.spigot.priority: "100"
      traefik.http.services.spigot.loadbalancer.server.port: "8100"

  caddy:
    image: "caddy:latest"
    restart: unless-stopped
    volumes:
      - "./caddy/www:/usr/share/caddy"
    networks:
      - "core_public"
      - "minecraft"
    labels:
      com.centurylinklabs.watchtower.enable: "true"
      traefik.enable: "true"
      traefik.http.routers.caddy.entrypoints: "https"
      traefik.http.routers.caddy.tls: "true"
      traefik.http.routers.caddy.tls.certresolver: "le"
      traefik.http.routers.caddy.rule: "Host(`mc.zekro.de`)"
      traefik.http.routers.caddy.priority: "10"

  spigot_staging:
    image: "ghcr.io/zekrotja/papermc-docker:zulu-23"
    restart: unless-stopped
    environment:
      VERSION: "1.21.11"
      BUILD: "latest"
      CACHE_DOWNLOAD: "true"
      DEBUG_MODE: "true"
      XMS: 2G
      XMX: 4G
      MAX_AGE_BACKUP_FILES: 90d
      BACKUP_SUCCESS_SCRIPT: 'curl -X PUT --netrc-file /run/secrets/.netrc -H "Title: ✅ STAGING Minecraft Backup Successfull" "https://ntfy.zekro.de/minecraft_backups"'
      BACKUP_FAILED_SCRIPT: 'curl -X PUT --netrc-file /run/secrets/.netrc -H "Title: ❌ STAGING Minecraft Backup Failed" -d "$$MESSAGE" "https://ntfy.zekro.de/minecraft_backups"'
    ports:
      - "24694:25565"
    networks:
      - "core_public"
      - "minecraft"
    volumes:
      - "./spigot_staging/config:/etc/mcserver/config"
      - "./spigot_staging/plugins:/etc/mcserver/plugins"
      - "./spigot_staging/worlds:/etc/mcserver/worlds"
      - "./spigot_staging/locals:/etc/mcserver/locals"
    secrets:
      - source: minecraftrclone
        target: rcloneconfig
      - source: netrc
        target: .netrc
    healthcheck:
      test: >
        /usr/bin/healthcheck -addr localhost:25565 -validateResponse
        && test "$(curl -o /dev/null -sLw "%{response_code}\n" http://127.0.0.1:8100)" -eq 200
      start_period: 1m
      start_interval: 5s
      interval: 30s
      timeout: 10s
      retries: "10"
    labels:
      com.centurylinklabs.watchtower.enable: "true"
      traefik.enable: "true"
      traefik.http.routers.spigot_staging.entrypoints: "https"
      traefik.http.routers.spigot_staging.tls: "true"
      traefik.http.routers.spigot_staging.tls.certresolver: "le"
      traefik.http.routers.spigot_staging.rule: "Host(`staging.mc.zekro.de`)"
      traefik.http.routers.spigot_staging.priority: "100"
      traefik.http.services.spigot_staging.loadbalancer.server.port: "8100"

  prometheus:
    image: "prom/prometheus:latest"
    command: "--web.enable-admin-api --config.file=/etc/prometheus/prometheus.yml"
    expose:
      - "9090"
    volumes:
      - "./prometheus/config:/etc/prometheus"
    restart: always
    networks:
      - "minecraft"
    labels:
      com.centurylinklabs.watchtower.enable: "true"

  portainer:
    image: "portainer/portainer-ce:latest"
    volumes:
      - "./portainer:/data"
      - "/var/run/docker.sock:/var/run/docker.sock"
    restart: always
    networks:
      - "core_public"
    labels:
      com.centurylinklabs.watchtower.enable: "true"
      traefik.enable: "true"
      traefik.http.routers.portainer.entrypoints: "https"
      traefik.http.routers.portainer.tls: "true"
      traefik.http.routers.portainer.tls.certresolver: "le"
      traefik.http.routers.portainer.rule: "Host(`docker.mc.zekro.de`)"
      traefik.http.services.portainer.loadbalancer.server.port: "9000"

  grafana:
    image: "grafana/grafana"
    restart: unless-stopped
    user: "root"
    environment:
      GF_SECURITY_ALLOW_EMBEDDING: "true"
    volumes:
      - "./grafana:/var/lib/grafana"
    networks:
      - "core_public"
      - "minecraft"
    labels:
      com.centurylinklabs.watchtower.enable: "true"
      traefik.enable: "true"
      traefik.http.routers.grafana.entrypoints: "https"
      traefik.http.routers.grafana.tls.certresolver: "le"
      traefik.http.routers.grafana.tls: "true"
      traefik.http.routers.grafana.rule: "Host(`grafana.mc.zekro.de`)"
Last change: , commit: dab7fd0