[
  {
    "path": ".dockerignore",
    "content": "data\ntestdata\nexamples\nk8s-examples\n.idea\n.git\n"
  },
  {
    "path": ".editorconfig",
    "content": "[start-*]\nindent_size = 2\nindent_style = space\nend_of_line = lf\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: itzg\ncustom:\n  - https://www.buymeacoffee.com/itzg\n  - https://paypal.me/itzg\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.yml",
    "content": "name: Bug Report\ndescription: File a bug report\nlabels:\n  - bug\n  - status/needs triage\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to report a bug.\n        \n        Please double check some things first:\n        1. Do you just have a question about something? If so, asking in the [Q&A Discussions](https://github.com/itzg/docker-minecraft-server/discussions/categories/q-a) or asking on [the Discord server](https://discord.gg/DXfKpjB) would be best.\n        2. Did you **re-pull the newest image** and confirmed the issue after that? Run `docker pull <image>` or if using a compose file, it's as easy as running `docker compose pull`. If using Kubernetes, add `imagePullPolicy: Always` to the container. \n        4. Is this bug happening after the `[init]` prefixed logs and after the log that says \"Starting the Minecraft server\"? If so, please report the bug with Mojang or the respective server provider.\n        5. Are you seeing a performance problem? If so, that is typically outside the scope of this image. Ask a question as above or contact the respective server provider.\n  - type: textarea\n    id: problem\n    attributes:\n      label: Describe the problem\n    validations:\n      required: true\n  - type: textarea\n    id: container\n    attributes:\n      label: Container definition\n      description: Please provide the compose file or run command used to create the container\n      value: |\n        ```\n        Paste run command or compose file here\n        ```\n  - type: textarea\n    id: logs\n    attributes:\n      label: Container logs\n      description: |\n        Please provide container logs from the start of the container, which will be the ones prefixed with `[init]`. It is even better if you can set the variable `DEBUG` to \"true\" and provide those debug container logs.\n      value: |\n        ```\n        Paste logs here\n        ```\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: Ask a question in discussions\n    url: https://github.com/itzg/docker-minecraft-server/discussions\n    about: Please ask questions here\n  - name: Ask a question on Discord\n    url: https://discord.gg/DXfKpjB\n    about: Please ask questions here"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/enhancement.yml",
    "content": "name: Enhancement Request\ndescription: Request an enhancement\nlabels:\n  - enhancement\n  - status/needs triage\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to request an enhancement.\n        \n        Even if you plan on submitting a pull request with a contributed enhancement it is best to describe the enhancement here first. Somebody might already be working on a similar thing and could use your help.\n  - type: dropdown\n    id: type\n    attributes:\n      label: Enhancement Type\n      options:\n        - Improve an existing feature\n        - A completely new feature\n        - New server type\n        - Not sure\n    validations:\n      required: true\n  - type: textarea\n    id: description\n    attributes:\n      label: Describe the enhancement\n    validations:\n      required: true"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      updates:\n        patterns:\n          - \"*\"\n        update-types:\n          - patch\n          - minor\n  - package-ecosystem: pip\n    directory: \"/docs\"\n    schedule:\n      interval: weekly\n    groups:\n      patches:\n        patterns:\n          - \"*\"\n        update-types:\n          - patch\n          - minor\n"
  },
  {
    "path": ".github/release.yml",
    "content": "changelog:\n  exclude:\n    authors:\n      - dependabot\n      - renovate\n  categories:\n    - title: Enhancements\n      labels:\n        - enhancement\n    - title: Bug Fixes\n      labels:\n        - bug\n    - title: Documentation\n      labels:\n        - documentation\n    - title: Other Changes\n      labels:\n        - \"*\"\n"
  },
  {
    "path": ".github/workflows/auto-release.yml",
    "content": "name: Auto release\non:\n  schedule:\n    - cron: \"0 14 * * *\"\n  workflow_dispatch:\njobs:\n  auto-release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: zenengeo/github-auto-release-action@main\n        with:\n          stable-duration: 3d\n          force-duration: 14d\n          token: '${{ secrets.GH_TOKEN }}'\n  \n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build and Publish\non:\n  push:\n    branches:\n      - master\n      - test/**\n    tags:\n      - \"[0-9]+.[0-9]+.[0-9]+\"\n    paths-ignore:\n      - \"*.md\"\n      - \"docs/**\"\n      - \"examples/**\"\n      - \"notes/**\"\n      - \"kustomize/**\"\n      - \"docker-compose.yml\"\n      - \"images.json\"\n      - \"compose-docs.yml\"\n      - \"zensical.toml\"\n      - \".readthedocs.yaml\"\n      - \"renovate.json5\"\n\njobs:\n  build:\n    strategy:\n      fail-fast: false\n      matrix:\n        # NOTE: the \"latest\" variant is identified in the Docker meta step's 'latest' config\n        variant:\n          - java25\n          - java25-alpine\n          - java25-jdk\n          - java21\n          - java21-alpine\n          - java21-jdk\n          - java17\n          - java16\n          - java11\n          - java8\n          - java8-jdk\n        include:\n        # JAVA 25\n          - variant: java25\n            baseImage: eclipse-temurin:25-jre\n            platforms: linux/amd64,linux/arm64\n            mcVersion: latest\n          - variant: java25-alpine\n            baseImage: eclipse-temurin:25-jre-alpine\n            platforms: linux/amd64,linux/arm64\n            mcVersion: latest\n          - variant: java25-jdk\n            baseImage: eclipse-temurin:25\n            platforms: linux/amd64,linux/arm64\n            mcVersion: latest\n        # JAVA 21:\n          - variant: java21\n            baseImage: eclipse-temurin:21-jre\n            platforms: linux/amd64,linux/arm64\n            mcVersion: latest\n          - variant: java21-jdk\n            baseImage: eclipse-temurin:21\n            platforms: linux/amd64,linux/arm64\n            mcVersion: latest\n          - variant: java21-alpine\n            baseImage: eclipse-temurin:21-jre-alpine\n            platforms: linux/amd64,linux/arm64\n            mcVersion: latest\n        # JAVA 17:\n          - variant: java17\n            # jammy doesn't work until minecraft updates to https://github.com/netty/netty/issues/12343\n            baseImage: eclipse-temurin:17-jre-focal\n            platforms: linux/amd64,linux/arm/v7,linux/arm64\n            mcVersion: 1.20.4\n        # JAVA 16\n          - variant: java16\n            baseImage: adoptopenjdk:16-jre-hotspot\n            platforms: linux/amd64,linux/arm/v7,linux/arm64\n            mcVersion: 1.16.5\n        # JAVA 11\n          - variant: java11\n            baseImage: adoptopenjdk:11-jre-hotspot\n            platforms: linux/amd64,linux/arm/v7,linux/arm64\n            mcVersion: 1.16.4\n        # JAVA 8: NOTE: Unable to go past 8u312 because of Forge dependencies\n          - variant: java8\n            baseImage: eclipse-temurin:8u312-b07-jre-focal\n            platforms: linux/amd64,linux/arm/v7,linux/arm64\n            mcVersion: 1.12.2\n            # Pin version for Java 8\n            mcHelperVersion: 1.51.1\n          - variant: java8-jdk\n            baseImage: eclipse-temurin:8u312-b07-jdk-focal\n            platforms: linux/amd64,linux/arm64\n            mcVersion: 1.12.2\n            # Pin version for Java 8\n            mcHelperVersion: 1.51.1\n    env:\n      IMAGE_TO_TEST: \"${{ github.repository_owner }}/minecraft-server:test-${{ matrix.variant }}-${{ github.run_id }}\"\n      HAS_IMAGE_REPO_ACCESS: ${{ secrets.DOCKER_USER != '' && secrets.DOCKER_PASSWORD != '' }}\n      MAIN_VARIANT: java25\n    runs-on: ubuntu-22.04\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6.0.2\n        with:\n          # for build-files step\n          fetch-depth: 0\n\n      - name: Docker meta\n        id: meta\n        uses: docker/metadata-action@v5.10.0\n        with:\n          # NOTE for forks: if your Docker Hub organization doesn't match your Github repo's,\n          # then the use of ${{ github.repository_owner }} will need to be replaced.\n          images: |\n            ${{ github.repository_owner }}/minecraft-server\n            ghcr.io/${{ github.repository_owner }}/minecraft-server\n          tags: |\n            # Apply the variant as a moving tag for most recent commit per variant\n            type=raw,value=${{ matrix.variant }},enable=${{ github.ref_name == github.event.repository.default_branch }}            \n            # For the \"main\" variant, it gets the tag as-is\n            type=pep440,pattern={{version}},enable=${{ matrix.variant == env.MAIN_VARIANT }}\n            # ...and all variants (including main one) get the tag with the variant suffix, such as 2023.1.1-java17\n            type=pep440,pattern={{version}},suffix=-${{ matrix.variant }}\n            # latest tag gets a moving 'stable' image tag applied to the main variant\n            type=pep440,pattern=stable,enable=${{ matrix.variant == env.MAIN_VARIANT }}\n            # ...and qualified stable for all variants\n            type=pep440,pattern=stable,suffix=-${{ matrix.variant }}\n            # for building test/* branch images\n            type=ref,event=branch,suffix=-${{ matrix.variant }},enable=${{ github.ref_name != github.event.repository.default_branch }}\n            # for backward compatibility with users referencing java8-multiarch, this will set an extra label on java8\n            type=raw,value=java8-multiarch,enable=${{ matrix.variant == 'java8' && github.ref_name == github.event.repository.default_branch }}\n          # NOTE this identifies which variant will be published as \"latest\", which isn't\n          # necessarily the newest version of Java\n          flavor: |\n            latest=${{ matrix.variant == env.MAIN_VARIANT && github.ref_name == github.event.repository.default_branch }}\n          labels: |\n            org.opencontainers.image.authors=Geoff Bourne <itzgeoff@gmail.com>\n\n      - name: Setup Docker Buildx\n        uses: docker/setup-buildx-action@v3.12.0\n\n      - name: Set up QEMU\n        uses: docker/setup-qemu-action@v3.7.0\n\n      - name: Build for test\n        uses: docker/build-push-action@v6.19.2\n        with:\n          platforms: linux/amd64\n          tags: ${{ env.IMAGE_TO_TEST }}\n          # ensure latest base image is used\n          pull: true\n          # load into daemon for test usage in next step\n          load: true\n          push: false\n          build-args: |\n            BASE_IMAGE=${{ matrix.baseImage }}\n            ${{ matrix.mcHelperVersion && format('MC_HELPER_VERSION={0}', matrix.mcHelperVersion) }}\n          cache-from: type=gha,scope=${{ matrix.variant }}\n          # no cache-to to avoid cross-cache update from next build step\n\n      - name: Run tests\n        env:\n          MINECRAFT_VERSION: ${{ matrix.mcVersion }}\n          VARIANT: ${{ matrix.variant }}\n          CF_API_KEY: ${{ secrets.CF_API_KEY }}\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          tests/test.sh\n\n      - name: Login to DockerHub\n        uses: docker/login-action@v3.7.0\n        if: env.HAS_IMAGE_REPO_ACCESS\n        with:\n          username: ${{ secrets.DOCKER_USER }}\n          password: ${{ secrets.DOCKER_PASSWORD }}\n\n      - name: Login to GHCR\n        uses: docker/login-action@v3.7.0\n        if: env.HAS_IMAGE_REPO_ACCESS\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ github.token }}\n\n      - name: Build and push\n        uses: docker/build-push-action@v6.19.2\n        if: github.actor == github.repository_owner\n        with:\n          platforms: ${{ matrix.platforms }}\n          push: >\n            ${{ \n            github.ref_type == 'tag' \n            || github.ref_name == github.event.repository.default_branch\n            || startsWith(github.ref_name, 'test/')\n            || ( github.event_name == 'pull_request' \n                 && env.HAS_IMAGE_REPO_ACCESS \n                 && contains(github.event.pull_request.labels.*.name, 'ci/push-image') \n               )\n            }}\n          tags: ${{ steps.meta.outputs.tags }}\n          # ensure latest base image is used\n          pull: true\n          labels: ${{ steps.meta.outputs.labels }}\n          # Since some consumers, like Watchtower are broken https://github.com/containrrr/watchtower/discussions/1529\n          # Also refer to https://github.com/docker/build-push-action/releases/tag/v3.3.0\n          provenance: false\n          build-args: |\n            BASE_IMAGE=${{ matrix.baseImage }}\n            BUILD_FILES_REV=${{ steps.build-files-rev.outputs.REV }}\n            BUILDTIME=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}\n            VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}\n            REVISION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}\n            ${{ matrix.mcHelperVersion && format('MC_HELPER_VERSION={0}', matrix.mcHelperVersion) }}\n          cache-from: type=gha,scope=${{ matrix.variant }}\n          cache-to: type=gha,mode=max,scope=${{ matrix.variant }}\n"
  },
  {
    "path": ".github/workflows/discord.yml",
    "content": "name: Discord notifications\n\non:\n  release:\n    types:\n      - published\n  workflow_run:\n    workflows: [\"Build and Publish\"]\n    types:\n      - completed\n\nenv:\n  DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}\n\njobs:\n  discord:\n    runs-on: ubuntu-latest\n    timeout-minutes: 5\n    steps:\n      - name: on-publish\n        if: github.event_name == 'release' && github.event.action == 'published'\n        uses: SethCohen/github-releases-to-discord@v1.19.0\n        with:\n          webhook_url: ${{ secrets.DISCORD_WEBHOOK }}\n      - name: on-success\n        if: github.event.workflow_run.conclusion == 'success'\n        uses: Ilshidur/action-discord@master\n        with:\n          args: |\n            Github repo: ${{ github.repository }}\n            - Branch: ${{ github.event.workflow_run.head_branch }}\n            - [Link: to Actions](<${{ github.event.workflow_run.html_url }}>)\n            - Status: 🎉  ${{ github.event.workflow_run.conclusion }}  🍏\n      - name: on-failure\n        if: github.event.workflow_run.conclusion == 'failure'\n        uses: Ilshidur/action-discord@master\n        with:\n          args: |\n            Github repo: ${{ github.repository }}\n            - Branch: ${{ github.event.workflow_run.head_branch }}\n            - [Link: to Actions](<${{ github.event.workflow_run.html_url }}>)\n            - Status: 🤔  ${{ github.event.workflow_run.conclusion }}  💣💥\n"
  },
  {
    "path": ".github/workflows/issue-label.yml",
    "content": "name: Issues notify\n\non:\n  issues:\n    types:\n      - opened\n      - labeled\n      - unlabeled\n      - closed\n\njobs:\n  notify:\n    uses: itzg/github-workflows/.github/workflows/issues-notify-discord.yml@main\n    with:\n      triageLabel: 'status/needs triage'\n    secrets:\n      discordWebhook: \"${{secrets.DISCORD_ISSUES_WEBHOOK}}\"\n"
  },
  {
    "path": ".github/workflows/stale-check.yml",
    "content": "name: Stale Check\n\non:\n  schedule:\n    - cron: 0 2 * * *\n\njobs:\n  check:\n    runs-on: ubuntu-22.04\n    permissions:\n      issues: write\n      pull-requests: write\n    steps:\n      - name: Process Stale Issues\n        uses: actions/stale@v10\n        with:\n          stale-issue-label: status/stale\n          stale-pr-label: status/stale\n          stale-issue-message: >\n            This issue is stale because it has been open 30 days with no activity. \n            Please add a comment describing the reason to keep this issue open.\n          days-before-stale: 30\n          days-before-close: 5\n          exempt-issue-labels: 'enhancement,keep,status/needs triage,priority/high'\n\n"
  },
  {
    "path": ".github/workflows/verify-pr.yml",
    "content": "name: Verify PR\non:\n  pull_request:\n    branches:\n      - master\n    paths-ignore:\n      - \"*.md\"\n      - \"docs/**\"\n      - \"examples/**\"\n      - \"notes/**\"\n      - \"kustomize/**\"\n      - \"docker-compose.yml\"\n      - \"images.json\"\n      - \"compose-docs.yml\"\n      - \"zensical.toml\"\n      - \".readthedocs.yaml\"\n      - \"renovate.json5\"\n\njobs:\n  build:\n    strategy:\n      fail-fast: false\n      matrix:\n        variant:\n          - java25\n          - java25-alpine\n          - java17\n          - java8\n        include:\n        # JAVA 21/25:\n          - variant: java25\n            baseImage: eclipse-temurin:25-jre\n            platforms: linux/amd64,linux/arm64\n            mcVersion: latest\n          - variant: java25-alpine\n            baseImage: eclipse-temurin:25-jre-alpine\n            platforms: linux/amd64,linux/arm64\n            mcVersion: latest\n        # JAVA 17:\n          - variant: java17\n            # jammy doesn't work until minecraft updates to https://github.com/netty/netty/issues/12343\n            baseImage: eclipse-temurin:17-jre-focal\n            platforms: linux/amd64\n            mcVersion: 1.20.4\n          - variant: java8\n            baseImage: eclipse-temurin:8u312-b07-jre-focal\n            platforms: linux/amd64\n            mcVersion: 1.12.2\n            # Pin version for Java 8\n            mcHelperVersion: 1.51.1\n    env:\n      IMAGE_TO_TEST: ${{ github.repository_owner }}/minecraft-server:test-${{ matrix.variant }}-${{ github.run_id }}\n    runs-on: ubuntu-22.04\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6.0.2\n        with:\n          # for build-files step\n          fetch-depth: 0\n\n      - name: Setup Docker Buildx\n        uses: docker/setup-buildx-action@v3.12.0\n\n      - name: Confirm multi-arch build\n        uses: docker/build-push-action@v6.19.2\n        with:\n          platforms: ${{ matrix.platforms }}\n          # ensure latest base image is used\n          pull: true\n          build-args: |\n            BASE_IMAGE=${{ matrix.baseImage }}\n            ${{ matrix.mcHelperVersion && format('MC_HELPER_VERSION={0}', matrix.mcHelperVersion) }}\n          cache-from: type=gha,scope=${{ matrix.variant }}\n\n      - name: Build for test\n        uses: docker/build-push-action@v6.19.2\n        with:\n          # Only build single platform since loading multi-arch image into daemon fails with\n          # \"docker exporter does not currently support exporting manifest lists\"\n          platforms: linux/amd64\n          tags: ${{ env.IMAGE_TO_TEST }}\n          # ensure latest base image is used\n          pull: true\n          # load into daemon for test usage in next step\n          load: true\n          push: false\n          build-args: |\n            BASE_IMAGE=${{ matrix.baseImage }}\n            ${{ matrix.mcHelperVersion && format('MC_HELPER_VERSION={0}', matrix.mcHelperVersion) }}\n          cache-from: type=gha,scope=${{ matrix.variant }}\n\n      - name: Run tests\n        env:\n          MINECRAFT_VERSION: ${{ matrix.mcVersion }}\n          VARIANT: ${{ matrix.variant }}\n          CF_API_KEY: ${{ secrets.CF_API_KEY }}\n          DEBUG: ${{ runner.debug }}\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          tests/test.sh\n"
  },
  {
    "path": ".gitignore",
    "content": ".vscode\n/data/\n/.idea/\n*.iml\n/gh-md-toc\npersonal-build-and-develop.*\nsite/\n"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "version: 2\n\nbuild:\n  os: ubuntu-24.04\n  tools:\n    python: \"3.14\"\n  jobs:\n    # We recommend using a requirements file for reproducible builds.\n    # This is just a quick example to get started.\n    # https://docs.readthedocs.io/page/guides/reproducible-builds.html\n    install:\n      - pip install -r docs/requirements.txt\n    build:\n      html:\n        - zensical build\n    post_build:\n      - mkdir -p $READTHEDOCS_OUTPUT/html/\n      - cp --recursive site/* $READTHEDOCS_OUTPUT/html/\n"
  },
  {
    "path": "Dockerfile",
    "content": "ARG BASE_IMAGE=eclipse-temurin:25-jre\nFROM ${BASE_IMAGE}\n\n# hook into docker BuildKit --platform support\n# see https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope\nARG TARGETOS\nARG TARGETARCH\nARG TARGETVARIANT\n\n# The following three arg/env vars get used by the platform specific \"install-packages\" script\nARG EXTRA_DEB_PACKAGES=\"\"\nARG EXTRA_DNF_PACKAGES=\"\"\nARG EXTRA_ALPINE_PACKAGES=\"\"\nARG FORCE_INSTALL_PACKAGES=1\nRUN --mount=target=/build,source=build \\\n    TARGET=${TARGETARCH}${TARGETVARIANT} \\\n    /build/run.sh install-packages\nCOPY --from=tianon/gosu /gosu /usr/local/bin/\n\nRUN --mount=target=/build,source=build \\\n    /build/run.sh setup-user\n\nEXPOSE 25565\n\nARG APPS_REV=1\nARG GITHUB_BASEURL=https://github.com\n\n# renovate: datasource=github-releases packageName=itzg/easy-add\nARG EASY_ADD_VERSION=0.8.11\nADD ${GITHUB_BASEURL}/itzg/easy-add/releases/download/${EASY_ADD_VERSION}/easy-add_${TARGETOS}_${TARGETARCH}${TARGETVARIANT} /usr/bin/easy-add\nRUN chmod +x /usr/bin/easy-add\n\n# renovate: datasource=github-releases packageName=itzg/restify\nARG RESTIFY_VERSION=1.7.12\nRUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \\\n  --var version=${RESTIFY_VERSION} --var app=restify --file {{.app}} \\\n  --from ${GITHUB_BASEURL}/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz\n\n# renovate: datasource=github-releases packageName=itzg/rcon-cli\nARG RCON_CLI_VERSION=1.7.4\nRUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \\\n  --var version=${RCON_CLI_VERSION} --var app=rcon-cli --file {{.app}} \\\n  --from ${GITHUB_BASEURL}/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz\n\n# renovate: datasource=github-releases packageName=itzg/mc-monitor\nARG MC_MONITOR_VERSION=0.16.1\nRUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \\\n  --var version=${MC_MONITOR_VERSION} --var app=mc-monitor --file {{.app}} \\\n  --from ${GITHUB_BASEURL}/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz\n\n# renovate: datasource=github-releases packageName=itzg/mc-server-runner\nARG MC_SERVER_RUNNER_VERSION=1.14.3\nRUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \\\n  --var version=${MC_SERVER_RUNNER_VERSION} --var app=mc-server-runner --file {{.app}} \\\n  --from ${GITHUB_BASEURL}/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz\n\n# renovate: datasource=github-releases packageName=itzg/mc-image-helper versioning=loose\nARG MC_HELPER_VERSION=1.55.2\nARG MC_HELPER_BASE_URL=${GITHUB_BASEURL}/itzg/mc-image-helper/releases/download/${MC_HELPER_VERSION}\n# used for cache busting local copy of mc-image-helper\nARG MC_HELPER_REV=1\nRUN curl -fsSL ${MC_HELPER_BASE_URL}/mc-image-helper-${MC_HELPER_VERSION}.tgz \\\n  | tar -C /usr/share -zxf - \\\n    && ln -s /usr/share/mc-image-helper-${MC_HELPER_VERSION}/ /usr/share/mc-image-helper \\\n    && ln -s /usr/share/mc-image-helper/bin/mc-image-helper /usr/bin\n\nVOLUME [\"/data\"]\nWORKDIR /data\n\nSTOPSIGNAL SIGTERM\n\n# End user MUST set EULA and change RCON_PASSWORD\nENV TYPE=VANILLA VERSION=LATEST EULA=\"\" UID=1000 GID=1000 LC_ALL=en_US.UTF-8\n\nCOPY --chmod=755 scripts/start* /image/scripts/\n\n# Backward compatible shim for those with legacy entrypoint\nCOPY --chmod=755 <<EOF /start\n#!/bin/bash\nexec /image/scripts/start\nEOF\n\nCOPY --chmod=755 scripts/auto/* /image/scripts/auto/\nCOPY --chmod=755 scripts/shims/* /image/scripts/shims/\nRUN ln -s /image/scripts/shims/* /usr/local/bin/\nCOPY --chmod=755 files/* /image/\n\nRUN curl -fsSL -o /image/Log4jPatcher.jar https://github.com/CreeperHost/Log4jPatcher/releases/download/v1.0.1/Log4jPatcher-1.0.1.jar\n\nRUN dos2unix /image/scripts/start* /image/scripts/auto/*\n\nENTRYPOINT [ \"/image/scripts/start\" ]\nHEALTHCHECK --start-period=2m --retries=2 --interval=30s CMD mc-health\n\nARG BUILDTIME=local\nARG VERSION=local\nARG REVISION=local\nCOPY <<EOF /etc/image.properties\nbuildtime=${BUILDTIME}\nversion=${VERSION}\nrevision=${REVISION}\nEOF\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "README.md",
    "content": "[![Docker Pulls](https://img.shields.io/docker/pulls/itzg/minecraft-server.svg?logo=docker)](https://hub.docker.com/r/itzg/minecraft-server/)\n[![Docker Stars](https://img.shields.io/docker/stars/itzg/minecraft-server.svg?logo=docker)](https://hub.docker.com/r/itzg/minecraft-server/)\n[![GitHub Issues](https://img.shields.io/github/issues-raw/itzg/docker-minecraft-server.svg)](https://github.com/itzg/docker-minecraft-server/issues)\n[![Discord](https://img.shields.io/discord/660567679458869252?label=Discord&logo=discord)](https://discord.gg/DXfKpjB)\n[![Build and Publish](https://github.com/itzg/docker-minecraft-server/actions/workflows/build.yml/badge.svg)](https://github.com/itzg/docker-minecraft-server/actions/workflows/build.yml)\n[![](https://img.shields.io/badge/Donate-Buy%20me%20a%20coffee-orange.svg)](https://www.buymeacoffee.com/itzg)\n[![Documentation Status](https://readthedocs.org/projects/docker-minecraft-server/badge/?version=latest)](https://docker-minecraft-server.readthedocs.io/en/latest/?badge=latest)\n\n [![Read the docs](docs/img/docs-banner.png)](https://docker-minecraft-server.readthedocs.io/)\n\nThere you will find things like\n- [Quick start with Docker Compose](https://docker-minecraft-server.readthedocs.io/en/latest/#using-docker-compose)\n- Running [different versions of Minecraft](https://docker-minecraft-server.readthedocs.io/en/latest/versions/minecraft/) and using [various server types](https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/) for Java Edition\n- [Setting server properties via container environment variables](https://docker-minecraft-server.readthedocs.io/en/latest/configuration/server-properties/)\n- [Managing mods and plugins with automated downloads and cleanup](https://docker-minecraft-server.readthedocs.io/en/latest/mods-and-plugins/)\n- [Using various modpack providers/platforms](https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/)\n- ...and much more\n\nThere are also many examples located in [the examples directory](examples) of this repo.\n\nThis image only supports Java edition natively; however, if looking for a server that is compatible with Bedrock edition, then use [itzg/minecraft-bedrock-server](https://github.com/itzg/docker-minecraft-bedrock-server) or [refer to this section](https://docker-minecraft-server.readthedocs.io/en/latest/misc/examples/#bedrock-compatible-server) to add Bedrock compatibility to a Java edition server.\n"
  },
  {
    "path": "build/alpine/install-packages.sh",
    "content": "#!/bin/sh\n\nset -e\nset -o pipefail\n\n# Install necessary packages\n# shellcheck disable=SC2086\napk add --no-cache -U \\\n    openssl \\\n    imagemagick \\\n    file \\\n    lsof \\\n    coreutils \\\n    findutils \\\n    procps \\\n    shadow \\\n    bash \\\n    curl \\\n    iputils \\\n    git \\\n    jq \\\n    mysql-client \\\n    tzdata \\\n    rsync \\\n    nano \\\n    ncurses \\\n    sudo \\\n    tar \\\n    zstd \\\n    nfs-utils \\\n    libpcap \\\n    libwebp \\\n    libcap \\\n    numactl \\\n    ${EXTRA_ALPINE_PACKAGES}\n\n# Download and install patched knockd\ncurl -fsSL -o /tmp/knock.tar.gz https://github.com/Metalcape/knock/releases/download/0.8.1/knock-0.8.1-alpine-amd64.tar.gz\ntar -xf /tmp/knock.tar.gz -C /usr/local/ && rm /tmp/knock.tar.gz\nln -s /usr/local/sbin/knockd /usr/sbin/knockd\nsetcap cap_net_raw=ep /usr/local/sbin/knockd\n\n# Set Git credentials globally\ncat <<EOF >> /etc/gitconfig\n[user]\n\tname = Minecraft Server on Docker\n\temail = server@example.com\nEOF"
  },
  {
    "path": "build/alpine/setup-user.sh",
    "content": "#!/bin/sh\n\nset -e\n\naddgroup -g 1000 minecraft\nadduser -Ss /bin/false -u 1000 -G minecraft -h /home/minecraft minecraft\n"
  },
  {
    "path": "build/ol/install-packages.sh",
    "content": "#!/bin/bash\n\nexport TARGET\n\nset -euo pipefail\n\nos_major_version=$(awk -F'[= .\"]+' '/^VERSION_ID=/{ print $2 }' /etc/os-release)\n\n# Install and configure dnf\nmicrodnf install dnf -y\ndnf install 'dnf-command(config-manager)' -y\ndnf config-manager --set-enabled ol${os_major_version}_codeready_builder\n\n# Add EPEL repository\ntee /etc/yum.repos.d/ol${os_major_version}-epel.repo <<EOF\n[ol${os_major_version}_developer_EPEL]\nname=Oracle Linux \\$releasever EPEL (\\$basearch)\nbaseurl=https://yum.oracle.com/repo/OracleLinux/OL${os_major_version}$([ \"$os_major_version\" -ge 10 ] && echo '/0' || echo '')/developer/EPEL/\\$basearch/\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-oracle\ngpgcheck=1\nenabled=1\nEOF\n\n# Update system\ndnf update -y\n\n# Install necessary packages\n# shellcheck disable=SC2086\n# shellcheck disable=SC2046\ndnf install -y \\\n  ImageMagick \\\n  file \\\n  sudo \\\n  net-tools \\\n  iputils \\\n  curl \\\n  git \\\n  jq \\\n  dos2unix \\\n  $([ \"$os_major_version\" -ge 10 ] && echo 'mysql8.4' || echo 'mysql') \\\n  procps-ng \\\n  tzdata \\\n  rsync \\\n  nano \\\n  unzip \\\n  zstd \\\n  $([ \"$os_major_version\" -ge 10 ] && echo 'bzip2' || echo 'lbzip2') \\\n  libpcap \\\n  libwebp \\\n  findutils \\\n  which \\\n  glibc-langpack-en \\\n  $([ \"$os_major_version\" -ge 10 ] && echo 'git-lfs' || echo '') \\\n  $([ \"$os_major_version\" -ge 10 ] && echo 'openssl' || echo '') \\\n  ${EXTRA_DNF_PACKAGES}\n\n# Install Git LFS through third party repository for older OL releases\nif [ \"$os_major_version\" -lt 10 ]; then\n  curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.rpm.sh | sudo bash\n  dnf update -y\n  dnf install -y git-lfs\nfi\n\n# Clean up DNF when done\ndnf clean all\n\ncat <<EOF > /usr/local/sbin/knockd\n#!/bin/sh\n\necho \"Auto-pause (using knockd) is currently unavailable on graalvm image variants\"\necho \"Consider using a different image variant https://docker-minecraft-server.readthedocs.io/en/latest/versions/java/\"\necho \"or mc-router's auto scale up/down feature https://github.com/itzg/mc-router#docker-auto-scale-updown\"\nexit 2\nEOF\nchmod 755 /usr/local/sbin/knockd\n# TODO restore retrieval from https://github.com/Metalcape/knock when tar's \"Cannot open: Invalid argument\" is solved\n\n# Set git credentials globally\ncat <<EOF >> /etc/gitconfig\n[user]\n\tname = Minecraft Server on Docker\n\temail = server@example.com\nEOF\n"
  },
  {
    "path": "build/ol/setup-user.sh",
    "content": "groupadd --gid 1000 minecraft\nuseradd --system --shell /bin/false --uid 1000 -g minecraft --home /data minecraft\n"
  },
  {
    "path": "build/run.sh",
    "content": "#!/bin/sh\n\nset -e\n\ndistro=$(cat /etc/os-release | grep -E \"^ID=\" | cut -d= -f2 | sed -e 's/\"//g')\n\n\"$(dirname \"$0\")/${distro}/$1\".sh"
  },
  {
    "path": "build/ubuntu/install-packages.sh",
    "content": "#!/bin/bash\n\nexport TARGET\n\nset -euo pipefail\n\n# Update and install packages\napt-get update\n# shellcheck disable=SC2086\nDEBIAN_FRONTEND=noninteractive \\\napt-get install -y \\\n  imagemagick \\\n  file \\\n  sudo \\\n  net-tools \\\n  iputils-ping \\\n  curl \\\n  git \\\n  jq \\\n  dos2unix \\\n  mysql-client \\\n  tzdata \\\n  rsync \\\n  nano \\\n  unzip \\\n  zstd \\\n  lbzip2 \\\n  nfs-common \\\n  libpcap0.8 \\\n  libnuma1 \\\n  ${EXTRA_DEB_PACKAGES}\n\n# Install Git LFS\ncurl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash\napt-get update\napt-get install -y git-lfs\n\n# Clean up APT when done\napt-get clean\n\n# Download and install patched knockd\ncurl -fsSL -o /tmp/knock.tar.gz https://github.com/Metalcape/knock/releases/download/0.8.1/knock-0.8.1-$TARGET.tar.gz\ntar -xf /tmp/knock.tar.gz -C /usr/local/ && rm /tmp/knock.tar.gz\nln -s /usr/local/sbin/knockd /usr/sbin/knockd\nsetcap cap_net_raw=ep /usr/local/sbin/knockd\nfind /usr/lib -name 'libpcap.so.0.8' -execdir cp '{}' libpcap.so.1 \\;\n\n# Set git credentials globally\ncat <<EOF >> /etc/gitconfig\n[user]\n\tname = Minecraft Server on Docker\n\temail = server@example.com\nEOF\n"
  },
  {
    "path": "build/ubuntu/setup-user.sh",
    "content": "#!/bin/sh\n\nset -e\n\nif id ubuntu > /dev/null 2>&1; then\n  deluser ubuntu\nfi\n\naddgroup --gid 1000 minecraft\nadduser --system --shell /bin/false --uid 1000 --ingroup minecraft --home /data minecraft"
  },
  {
    "path": "compose-dev.yml",
    "content": "services:\n  mc-dev:\n    build:\n      context: .\n    environment:\n      EULA: true\n    volumes:\n      - ./scripts:/image/scripts:ro\n      - data:/data\n    ports:\n      - \"25565:25565\"\n    stdin_open: true\n    tty: true\nvolumes:\n  data:"
  },
  {
    "path": "compose-docs.yml",
    "content": "# This composition can be used to serve up the rendered mkdocs for local authoring.\n#\n# docker compose -f compose-docs.yml -p zensical up\n#\n# and then access http://localhost:8000\n\nservices:\n  zensical:\n    build:\n      context: .\n      dockerfile: docs/Dockerfile\n    volumes:\n      - ./zensical.toml:/docs/zensical.toml\n      - ./docs:/docs/docs\n    ports:\n      - \"8000:8000\"\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    environment:\r\n      EULA: \"true\"\r\n    ports:\r\n      - \"25565:25565\"\r\n    volumes:\r\n      - data:/data\r\n    stdin_open: true\r\n    tty: true\r\n    restart: unless-stopped\r\nvolumes:\r\n  data: {}"
  },
  {
    "path": "docs/Dockerfile",
    "content": "FROM zensical/zensical\n\nRUN --mount=target=/build/requirements.txt,source=docs/requirements.txt \\\n    pip install -r /build/requirements.txt\n"
  },
  {
    "path": "docs/configuration/auto-rcon-commands.md",
    "content": "\r\n# Auto-execute RCON commands\r\n\r\nRCON commands can be configured to execute when the server starts, a client connects, or a client disconnects.\r\n\r\n!!! note\r\n\r\n    When declaring several commands within a compose file environment variable, it's easiest to use YAML's `|-` [block style indicator](https://yaml-multiline.info/).\r\n\r\n**On Server Start:**\r\n\r\n``` yaml\r\n      RCON_CMDS_STARTUP:  |-\r\n        gamerule doFireTick false\r\n        pregen start 200\r\n```\r\n\r\n**On Client Connection:**\r\n\r\n``` yaml\r\n      RCON_CMDS_ON_CONNECT:  |-\r\n        team join New @a[team=]\r\n```\r\n\r\n**Note:**\r\n* On client connect we only know there was a connection, and not who connected. RCON commands will need to be used for that.\r\n\r\n**On Client Disconnect:**\r\n\r\n``` yaml\r\n      RCON_CMDS_ON_DISCONNECT:  |-\r\n        gamerule doFireTick true\r\n```\r\n\r\n**On First Client Connect**\r\n\r\n``` yaml\r\n      RCON_CMDS_FIRST_CONNECT: |-\r\n        pregen stop\r\n```\r\n\r\n**On Last Client Disconnect**\r\n\r\n``` yaml\r\n      RCON_CMDS_LAST_DISCONNECT: |-\r\n        kill @e[type=minecraft:boat]\r\n        pregen start 200\r\n\r\n```\r\n\r\n**Example of rules for new players**\r\n\r\nUses team NEW and team OLD to track players on the server. So move player with no team to NEW, run a command, move them to team OLD.\r\n[Reference Article](https://www.minecraftforum.net/forums/minecraft-java-edition/redstone-discussion-and/2213523-detect-players-first-join)\r\n\r\n``` yaml\r\n      RCON_CMDS_STARTUP:  |-\r\n        /pregen start 200\r\n        /gamerule doFireTick false\r\n        /team add New\r\n        /team add Old\r\n      RCON_CMDS_ON_CONNECT: |-\r\n        /team join New @a[team=]\r\n        /give @a[team=New] birch_boat\r\n        /team join Old @a[team=New]\r\n      RCON_CMDS_FIRST_CONNECT: |-\r\n        /pregen stop\r\n      RCON_CMDS_LAST_DISCONNECT: |-\r\n        /kill @e[type=minecraft:boat]\r\n        /pregen start 200\r\n```\r\n"
  },
  {
    "path": "docs/configuration/interpolating.md",
    "content": "---\r\ntitle: Modifying config files\r\n---\r\n\r\n## Replacing variables inside configs\r\n\r\nSometimes you have mods or plugins that require configuration information that is only available at deploy-time. For example if you need to configure a plugin to connect to a database, you don't want to include this information in your Git repository or Docker image.\r\nOr maybe you have some runtime information like the server name that needs to be set in your config files after the container starts.\r\n\r\nFor those cases there is the option to replace defined variables inside your configs with environment variables defined at container runtime.\r\n\r\nWhen the environment variable `REPLACE_ENV_IN_PLACE` is set to `true` (the default), the startup script will go through all files inside the container's `/data` path and replace variables that match the container's environment variables. Variables can instead (or in addition to) be replaced in files sync'ed from `/plugins`, `/mods`, and `/config` by setting `REPLACE_ENV_DURING_SYNC` to `true` (defaults to `false`).\r\n\r\nVariables that you want to replace need to be declared inside curly brackets and prefixed with a dollar sign, such as  `${CFG_YOUR_VARIABLE}`, which is same as many scripting languages.\r\n\r\nYou can also change `REPLACE_ENV_VARIABLE_PREFIX`, which defaults to \"CFG_\", to limit which environment variables are allowed to be used. For example, with \"CFG_\" as the prefix, the variable `${CFG_DB_HOST}` would be substituted, but not `${DB_HOST}`. The prefix can be set to an empty string to allow for matching any variable name.\r\n\r\nIf you want to use a file's content for value, such as when using secrets mounted as files, declare the placeholder named like normal in the file and declare an environment variable named the same but with the suffix `_FILE`.\r\n\r\nFor example, a `my.cnf` file could contain:\r\n\r\n```toml\r\n[client]\r\npassword = ${CFG_DB_PASSWORD}\r\n```\r\n\r\n...a secret declared in the compose file with:\r\n```yaml\r\nsecrets:\r\n  db_password:\r\n    external: true\r\n```\r\n\r\n...and finally the environment variable would be named with a `_FILE` suffix and point to the mounted secret:\r\n```yaml\r\n    environment:\r\n      CFG_DB_PASSWORD_FILE: /run/secrets/db_password\r\n```\r\n\r\nVariables will be replaced in files with the following extensions:\r\n`.yml`, `.yaml`, `.txt`, `.cfg`, `.conf`, `.properties`.\r\n\r\nSpecific files can be excluded by listing their name (without path) in the variable `REPLACE_ENV_VARIABLES_EXCLUDES`.\r\n\r\nPaths can be excluded by listing them in the variable `REPLACE_ENV_VARIABLES_EXCLUDE_PATHS`. Path\r\nexcludes are recursive. Here is an example:\r\n```\r\nREPLACE_ENV_VARIABLES_EXCLUDE_PATHS=\"/data/plugins/Essentials/userdata /data/plugins/MyPlugin\"\r\n```\r\n\r\nHere is a full example where we want to replace values inside a `database.yml`.\r\n\r\n```yaml\r\n---\r\ndatabase:\r\n  host: ${CFG_DB_HOST}\r\n  name: ${CFG_DB_NAME}\r\n  password: ${CFG_DB_PASSWORD}\r\n```\r\n\r\nThis is how your `compose.yaml` file could look like:\r\n\r\n```yaml title=\"compose.yaml\"\r\n# Other docker compose examples in /examples\r\n\r\nservices:\r\n  minecraft:\r\n    image: itzg/minecraft-server\r\n    ports:\r\n      - \"25565:25565\"\r\n    volumes:\r\n      - \"mc:/data\"\r\n    environment:\r\n      EULA: \"TRUE\"\r\n      ENABLE_RCON: \"true\"\r\n      RCON_PASSWORD: \"testing\"\r\n      RCON_PORT: 28016\r\n      # enable env variable replacement\r\n      REPLACE_ENV_VARIABLES: \"TRUE\"\r\n      # define an optional prefix for your env variables you want to replace\r\n      ENV_VARIABLE_PREFIX: \"CFG_\"\r\n      # and here are the actual variables\r\n      CFG_DB_HOST: \"http://localhost:3306\"\r\n      CFG_DB_NAME: \"minecraft\"\r\n      CFG_DB_PASSWORD_FILE: \"/run/secrets/db_password\"\r\n\r\nvolumes:\r\n  mc:\r\n  rcon:\r\n\r\nsecrets:\r\n  db_password:\r\n    file: ./db_password\r\n```\r\n\r\n## Patching existing files\r\n\r\nJSON path based patches can be applied to one or more existing files by setting the variable `PATCH_DEFINITIONS` to the path of a directory that contains one or more [patch definition json files](https://github.com/itzg/mc-image-helper#patchdefinition) or a [patch set json file](https://github.com/itzg/mc-image-helper#patchset). \r\n\r\nThe `file` and `value` fields of the patch definitions may contain `${...}` variable placeholders. The allowed environment variables in placeholders can be restricted by setting `REPLACE_ENV_VARIABLE_PREFIX`, which defaults to \"CFG_\".\r\n\r\nThe following example shows a patch-set file where various fields in the `paper.yaml` configuration file can be modified and added:\r\n\r\n```json\r\n{\r\n  \"patches\": [\r\n    {\r\n      \"file\": \"/data/paper.yml\",\r\n      \"ops\": [\r\n        {\r\n          \"$set\": {\r\n            \"path\": \"$.verbose\",\r\n            \"value\": true\r\n          }\r\n        },\r\n        {\r\n          \"$set\": {\r\n            \"path\": \"$.settings['velocity-support'].enabled\",\r\n            \"value\": \"${CFG_VELOCITY_ENABLED}\",\r\n            \"value-type\": \"bool\"\r\n          }\r\n        },\r\n        {\r\n          \"$put\": {\r\n            \"path\": \"$.settings\",\r\n            \"key\": \"my-test-setting\",\r\n            \"value\": \"testing\"\r\n          }\r\n        }\r\n      ]\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\nSupports the file formats:\r\n- JSON\r\n- JSON5\r\n- Yaml\r\n- TOML, but processed output is not pretty\r\n"
  },
  {
    "path": "docs/configuration/jvm-options.md",
    "content": "# JVM Options\n\n## Memory Limit\n\nBy default, the image declares an initial and maximum Java memory-heap limit of 1 GB. There are several ways to adjust the memory settings:\n\n- `MEMORY`: \"1G\" by default, can be used to adjust both initial (`Xms`) and max (`Xmx`) memory heap settings of the JVM\n- `INIT_MEMORY`: independently sets the initial heap size\n- `MAX_MEMORY`: independently sets the max heap size\n\nThe values of all three are passed directly to the JVM and support format/units as `<size>[g|G|m|M|k|K]`.\n\nTo have control over heap size, without relying on absolute memory sizes percentages are also supported using `<size>%`.\n\n!!! info \"RAMPercentage parameters\"\n    Percentage based heap sizing is enabled using `-XX:InitialRAMPercentage` for `INIT_MEMORY` and `-XX:MaxRAMPercentage` for `MAX_MEMORY`.\n    For details on the function of these parameters look [here](https://www.baeldung.com/java-jvm-parameters-rampercentage).\n\n!!! example \"Using docker run\"\n\n    ```\n        -e MEMORY=2G\n    ```\n\n    or to use init and max memory:\n\n    ```\n        -e INIT_MEMORY=1G -e MAX_MEMORY=4G\n    ```\n\n!!! example \"Using compose file\"\n\n    ```\n        environment:\n          MEMORY: 2G\n    ```\n\n    or to use init and max memory:\n\n    ```\n        environment:\n          INIT_MEMORY: 1G\n          MAX_MEMORY: 4G\n    ```\n\nTo let the JVM calculate the heap size from the container declared memory limit, unset `MEMORY` with an empty value, such as `-e MEMORY=\"\"`. By default, the JVM will use 25% of the container memory limit as the heap limit.\n\n!!! important\n    The settings above only set the Java **heap** limits. Memory resource requests and limits on the overall container should also account for non-heap memory usage. An extra 25% is [a general best practice](https://dzone.com/articles/best-practices-java-memory-arguments-for-container).\n\n## Extra JVM Options\n\nGeneral JVM options can be passed to the Minecraft Server invocation by passing a `JVM_OPTS`\nenvironment variable. If needing to set `-XX` options, then pass those with the `JVM_XX_OPTS` variable instead. Both variables accept space-delimited, raw JVM arguments.\n\n```\ndocker run ... -e JVM_OPTS=\"-XsomeJVMOption -DpropName=value\" ...\n```\n\n**NOTE** When declaring `JVM_OPTS` in a compose file's `environment` section with list syntax, **do not** include the quotes:\n\n```yaml\n    environment:\n      - EULA=true\n      - JVM_OPTS=-XsomeJVMOption -DpropName=value\n```\n\nUsing object syntax is recommended and more intuitive:\n\n```yaml\n    environment:\n      EULA: \"true\"\n      JVM_OPTS: \"-XsomeJVMOption -DpropName=value\"\n# or without quotes\n#     JVM_OPTS: -XsomeJVMOption -DpropName=value\n```\n\nAs a shorthand for passing several system properties as `-D` arguments, you can instead pass a comma separated list of `name=value` or `name:value` pairs with `JVM_DD_OPTS`. (The colon syntax is provided for management platforms like Plesk that don't allow `=` inside a value.)\n\nFor example, instead of passing\n\n```yaml\n  JVM_OPTS: -Dfml.queryResult=confirm -Dname=value\n```\n\nyou can use\n\n```yaml\n  JVM_DD_OPTS: fml.queryResult=confirm,name=value\n```\n\n## Enable Remote JMX for Profiling\n\nTo enable remote JMX, such as for profiling with VisualVM or JMC, set the environment variable `ENABLE_JMX` to \"true\", set `JMX_HOST` to the IP/host running the Docker container, and add a port forwarding of TCP port 7091, such as:\n\n!!! example\n\n    With `docker run`\n\n    ```\n    -e ENABLE_JMX=true -e JMX_HOST=$HOSTNAME -p 7091:7091\n    ```\n\nIf needing to map to a different port, then also set the environment variable `JMX_PORT` to the desired host port.\n\n!!! example\n\n    With a compose file:\n\n    ```yaml\n    environment:\n      ENABLE_JMX: true\n      JMX_HOST: ${HOSTNAME}\n      JMX_PORT: \"7092\"\n    ports:\n      - \"7092:7092\"\n    ```\n\n## Enable Aikar's Flags\n\n[Aikar has done some research](https://aikar.co/2018/07/02/tuning-the-jvm-g1gc-garbage-collector-flags-for-minecraft/) into finding the optimal JVM flags for GC tuning, which becomes more important as more users are connected concurrently. [PaperMC also has an explanation](https://docs.papermc.io/paper/aikars-flags) of what the JVM flags are doing.\n\nThe set of flags documented there can be added using\n\n    -e USE_AIKAR_FLAGS=true\n\nWhen `MEMORY` is greater than or equal to 12G, then the Aikar flags will be adjusted according to the article.\n\n## Enable MeowIce's Flags\n\n[MeowIce has created an updated set of JVM flags](https://github.com/MeowIce/meowice-flags?tab=readme-ov-file#why-would-i-have-to-switch-) based on Aikar's flags but with support for optimizations for Java 17 and above\n\nThe set of flags documented there can be added by setting the environment variable `USE_MEOWICE_FLAGS` to `true`. There is an optional `USE_MEOWICE_GRAALVM_FLAGS` variable to enable GraalVM specific optimizations, defaults to `FALSE`.\n"
  },
  {
    "path": "docs/configuration/misc-options.md",
    "content": "## Running with a custom server JAR\n\nIf you would like to run a custom server JAR, set `-e TYPE=CUSTOM` and pass the custom server\nJAR via `CUSTOM_SERVER`. It can either be a URL or a container path to an existing JAR file.\n\nIf it is a URL, it will only be downloaded into the `/data` directory if it wasn't already. As\nsuch, if you need to upgrade or re-download the JAR, then you will need to stop the container,\nremove the file from the container's `/data` directory, and start again.\n\n## Force re-download of the server file\n\nFor VANILLA, FORGE, BUKKIT, SPIGOT, PAPER, CURSEFORGE, SPONGEVANILLA server types, set\n`$FORCE_REDOWNLOAD` to some value (e.g. 'true) to force a re-download of the server file for\nthe particular server type. by adding a `-e FORCE_REDOWNLOAD=true` to your command-line.\n\nFor example, with Paper, it would look something like this:\n\n```\ndocker run -d --pull=always \\\n    -v /path/on/host:/data \\\n    -e TYPE=PAPER -e FORCE_REDOWNLOAD=true \\\n    -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server\n```\n\n## Running as alternate user/group ID\n\nBy default, the container will switch to and run the Minecraft server as user ID 1000 and group ID 1000; however, that can be changed by setting the environment variables `UID` and `GID`.\n\nThe startup will also skip user switching if the `--user`/`-u` argument is passed to `docker run` or `user` is set on the compose service.\n\n## Extra Arguments\n\nArguments that would usually be passed to the jar file (those which are written after the filename) can be passed via the `EXTRA_ARGS` environment variable.\n\nSee [Custom worlds directory path](../misc/world-data.md#custom-worlds-directory-path) for an example.\n\n## Interactive and Color Console\n\nWhen RCON is enabled, which is the default, and [TTY](https://docs.docker.com/compose/compose-file/05-services/#tty) is enabled on the container, then some server types will output colorized logs and provide a fully interactive console. To access the interactive console, use [`docker attach`](https://docs.docker.com/engine/reference/commandline/container_attach/) (not `exec`). When finished, make sure to use the sequence Control-P, Control-Q to detach without stopping the container.\n\nIf this behavior interferes with the log content, then disable TTY or remove the setting entirely since the default is disabled. In a compose file, set the service's `tty` parameter to `false`. On the `docker run` command-line remove the `-t` argument.\n\n## Server Shutdown Options\n\nTo allow time for players to finish what they're doing during a graceful server shutdown, set `STOP_SERVER_ANNOUNCE_DELAY` to a number of seconds to delay after an announcement is posted by the server.\n\n!!! warning \"Increase stop grace period\"\n\n    The Docker stop grace period must be increased to a value longer than the announce delay. The value to use that is longer than announce delay will vary based upon the amount of time it takes for final world data saving. If the container exits with exit code 137, then that indicates a longer grace period is needed. \n    \n    The grace period can be increased using [the -t option on `docker compose down`](https://docs.docker.com/compose/reference/down/) or set the [stop_grace_period](https://docs.docker.com/compose/compose-file/05-services/#stop_grace_period) in the compose file.\n\nThe `STOP_SERVER_ANNOUNCE_DELAY` can be bypassed by sending a `SIGUSR1` signal to the `mc-server-runner` process.\n\n`docker`:\n\n        docker stop --signal SIGUSR1 mc\n\n`docker compose`:\n\n        docker compose kill --signal SIGUSR1\n\n## Configuration Options for Minecraft Server Health Monitoring\n\nThe image tags include specific variables to simplify configuration for monitoring the health of a Minecraft server:\n\n- `-e SERVER_HOST=localhost` : This variable sets the host address of the Minecraft server to be monitored. By default, it is set to `localhost`, but you can replace it with the actual hostname or IP address of your Minecraft server.\n\n- `-e SERVER_PORT=25565` : This variable sets the port number on which the Minecraft server is running. By default, Minecraft servers run on port 25565, but if your server is configured to use a different port, you should replace `25565` with the correct port number. This helps the monitoring system to accurately check the health status of the Minecraft server on the specified port.\n\n## OpenJ9 Specific Options\n\nThe openj9 image tags include specific variables to simplify configuration:\n\n- `-e TUNE_VIRTUALIZED=TRUE` : enables the option to\n  [optimize for virtualized environments](https://www.eclipse.org/openj9/docs/xtunevirtualized/)\n- `-e TUNE_NURSERY_SIZES=TRUE` : configures nursery sizes where the initial size is 50%\n  of the `MAX_MEMORY` and the max size is 80%.\n\n## Customize Minecraft server logging\n\nThe image can use a templated log4j2 configuration based on PaperMC's logging setup, which is automatically applied for versions that don't require Log4j security patches. This configuration provides rolling logs and advanced logging features by default.\n\nSet the environment variable `GENERATE_LOG4J2_CONFIG` to \"true\" to enable the following features.\n\n!!! note\n\n    This configures the server logging only. To diagnose container startup, set the `DEBUG` environment variable to \"true\".\n\n### Customization via environment variables\n\nYou can customize various aspects of the logging behavior using environment variables:\n\n- `LOG_LEVEL` : Root logger level (default: `info`)\n  ```\n  -e LOG_LEVEL=debug\n  ```\n\n- `ROLLING_LOG_FILE_PATTERN` : Pattern for rolled log file names (default: `logs/%d{yyyy-MM-dd}-%i.log.gz`)\n  ```\n  -e ROLLING_LOG_FILE_PATTERN=\"logs/archive/%d{yyyy-MM-dd}-%i.log.gz\"\n  ```\n\n- `ROLLING_LOG_MAX_FILES` : Maximum number of archived log files to keep (default: `1000`)\n  ```\n  -e ROLLING_LOG_MAX_FILES=30\n  ```\n\n### Customizing log message formats\n\nFor full control over how log messages are formatted, you can customize the Log4j2 pattern layouts using these variables. These use [Log4j2 Pattern Layout syntax](https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout):\n\n- `LOG_CONSOLE_FORMAT` : Format for console output (what you see in `docker logs`)\n  Default: `[%d{HH:mm:ss}] [%t/%level]: %msg%n`\n\n- `LOG_FILE_FORMAT` : Format for file logs (written to `logs/latest.log`)\n  Default: `[%d{HH:mm:ss}] [%t/%level]: %msg%n`\n\n- `LOG_TERMINAL_FORMAT` : Format for interactive terminal console (used with `docker attach`)\n  Default: `[%d{HH:mm:ss} %level]: %msg%n`\n\n### Example configurations\n\nSimple timestamp customization (most common use case):\n```yaml\nenvironment:\n  # What you see in docker logs\n  LOG_CONSOLE_FORMAT: \"[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%t/%level]: %msg%n\"\n  # What gets written to logs/latest.log\n  LOG_FILE_FORMAT: \"[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%t/%level]: %msg%n\"\n```\n\nAdvanced customization:\n```yaml\nenvironment:\n  LOG_LEVEL: debug\n  # Custom ISO8601 format with logger names\n  LOG_CONSOLE_FORMAT: \"%d{ISO8601} %-5level [%t] %logger{36} - %msg%n\"\n  LOG_FILE_FORMAT: \"%d{ISO8601} %-5level [%t] %logger{36} - %msg%n\"\n  ROLLING_LOG_MAX_FILES: 50\n```\n\n### Legacy ENABLE_ROLLING_LOGS option\n\nThe `ENABLE_ROLLING_LOGS` environment variable is no longer needed for most use cases, as rolling logs are now enabled by default through the templated configuration. This option is maintained for backward compatibility but is only checked for error reporting when rolling logs cannot be used due to Log4j security patches.\n\n> **NOTE** The templated log4j2 configuration may interfere with interactive/color consoles [as described in the section above](#interactive-and-color-console)\n\n## Timezone Configuration\n\nYou can configure the timezone to match yours by setting the `TZ` environment variable:\n\n        -e TZ=Europe/London\n\nsuch as:\n\n        docker run -d -it --pull=always -e TZ=Europe/London -p 25565:25565 --name mc itzg/minecraft-server\n\nOr mounting `/etc/timezone` as readonly (not supported on Windows):\n\n        -v /etc/timezone:/etc/timezone:ro\n\nsuch as:\n\n        docker run -d -it --pull=always -v /etc/timezone:/etc/timezone:ro -p 25565:25565 --name mc itzg/minecraft-server\n\n## HTTP Proxy\n\nYou may configure the use of an HTTP/HTTPS proxy by passing the proxy's \"host:port\" via the environment variable `PROXY`. In [the example compose file](https://github.com/itzg/docker-minecraft-server/blob/master/examples/proxied/compose.yml) it references a Squid proxy. The host and port can be separately passed via the environment variables `PROXY_HOST` and `PROXY_PORT`. A `|` delimited list of hosts to exclude from proxying can be passed via `PROXY_NON_PROXY_HOSTS`.\n\n## Using \"noconsole\" option\n\nSome older versions (pre-1.14) of Spigot required `--noconsole` to be passed when detaching stdin, which can be done by setting `-e CONSOLE=FALSE`.\n\n## Explicitly disable GUI\n\nSome older servers get confused and think that the GUI interface is enabled. You can explicitly\ndisable that by passing `-e GUI=FALSE`.\n\n## Stop Duration\n\nWhen the container is signaled to stop, the Minecraft process wrapper will attempt to send a \"stop\" command via RCON or console and waits for the process to gracefully finish. By default, it waits 60 seconds, but that duration can be configured by setting the environment variable `STOP_DURATION` to the number of seconds.\n\nBe sure to also increase the shutdown timeout described [here for docker compose down](https://docs.docker.com/reference/cli/docker/compose/down/#options) and [here for docker stop](https://docs.docker.com/reference/cli/docker/container/stop/#options).\n\n## Setup only\n\nIf you are using a host-attached data directory, then you can have the image setup the Minecraft server files and stop prior to launching the server process by setting `SETUP_ONLY` to `true`. \n    \n## Enable Flare Flags\n    \nTo enable the JVM flags required to fully support the [Flare profiling suite](https://blog.airplane.gg/flare), set the following variable:\n    \n    -e USE_FLARE_FLAGS=true\n    \nFlare is built-in to Pufferfish/Purpur, and is available in [plugin form](https://github.com/TECHNOVE/FlarePlugin) for other server types.\n\n## Enable support for optimized SIMD operations\n\nTo enable support for optimized SIMD operations, the JVM flag can be set with the following variable:\n\n    -e USE_SIMD_FLAGS=true\n\nSIMD optimized operations are supported by Pufferfish and Purpur.\n\nFor example, to download configuration files for plugins:\n\n```yaml\nenvironment:\n  DOWNLOAD_EXTRA_CONFIGS: |\n    plugins/WorldEdit<https://raw.githubusercontent.com/EngineHub/WorldEdit/refs/heads/version/7.3.x/worldedit-bukkit/src/main/resources/defaults/config.yml\n    plugins/EssentialsX<https://raw.githubusercontent.com/EssentialsX/Essentials/refs/heads/2.x/Essentials/src/main/resources/config.yml\n```\n\nOr as a single line:\n\n```yaml\nenvironment:\n  DOWNLOAD_EXTRA_CONFIGS: \"plugins/WorldEdit<https://example.com/worldedit.yml,config<https://example.com/another.yml\"\n```\n\nThe files will be downloaded to `/data/` relative paths, so `plugins/WorldEdit` will be saved as `/data/plugins/WorldEdit/config.yml`.\n\n!!! note \n    The downloaded files can be further processed using [environment variable replacement](interpolating.md) or [patch definitions](interpolating.md#patching-existing-files)\n\n## Enable timestamps in init logs\n\nBefore the container starts the Minecraft Server its output is prefixed with `[init]`, such as\n\n```\n[init] Starting the Minecraft server...\n```\n\nTo also include the timestamp with each log, set `LOG_TIMESTAMP` to \"true\". The log output will then look like:\n\n```\n[init] 2022-02-05 16:58:33+00:00 Starting the Minecraft server...\n```\n"
  },
  {
    "path": "docs/configuration/server-properties.md",
    "content": "To unify management of the Minecraft server container, all known [`server.properties`](https://minecraft.wiki/w/Server.properties) entries can be managed by the environment variables described in the sections below. Some mappings provide additional functionality above and beyond the properties file and will be described in the sections below.\r\n\r\n!!! warning \"Version compatibility\"\r\n\r\n    Not all server properties are supported by all versions of Minecraft. Since this image supports a wide range of versions, please consult the [server properties documentation](https://minecraft.wiki/w/Server.properties) for the version you are using.\r\n\r\nIf you prefer to manually manage the `server.properties` file, set `OVERRIDE_SERVER_PROPERTIES` to \"false\". Similarly, you can entirely skip the startup script's creation of `server.properties` by setting `SKIP_SERVER_PROPERTIES` to \"true\".\r\n\r\n!!! note\r\n  \r\n    To clear a server property, set the variable to an empty string, such as `-e RESOURCE_PACK=\"\"`. An unset variable is ignored and the existing server property is left unchanged.\r\n\r\nTo see what `server.properties` will get used by the server, set the environment variable `DUMP_SERVER_PROPERTIES` to \"true\" and the contents of `server.properties` will get output before the server starts.\r\n\r\n## Placeholders\r\n\r\nWhen declaring a server properties via container environment variables, those values may contain placeholders that are processed when the `server.properties` file is updated. \r\n\r\nThe syntax of placeholders is DOS-style, `%VAR%`, to avoid being processed by Docker or the shell and the following options are available:\r\n\r\n`%VAR%` or `%env:VAR%`\r\n\r\n: Replaced with the resolved value or the environment variable `VAR`\r\n\r\n`%date:FMT%`\r\n\r\n: Formats the current date/time with the given `FMT` string processed by [Java's DateTimeFormatter](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html).\r\n\r\nAny declared or resolved environment variable may be referenced, such as `VERSION` and `TYPE`. Additionally, [Modrinth](../types-and-platforms/mod-platforms/modrinth-modpacks.md) and [Auto CurseForge](../types-and-platforms/mod-platforms/auto-curseforge.md) modpacks will expose the environment variables `MODPACK_NAME` and `MODPACK_VERSION`. The originally declared version, such as \"LATEST\" or \"SNAPSHOT\", is available in the variable `DECLARED_VERSION` \r\n\r\n!!! example\r\n\r\n    As a compose file environment entry:\r\n\r\n    ```yaml\r\n        MOTD: Running %MODPACK_NAME% version %env:MODPACK_VERSION%\r\n        LEVEL: world-%date:yyyy-MM-dd%\r\n    ```\r\n\r\n!!! warning\r\n\r\n    Placeholders are not supported when manually managing `server.properties`\r\n\r\n## Properties\r\n\r\n### Message of the Day\r\n\r\nThe message of the day, shown below each server entry in the client UI, can be changed with the `MOTD` environment variable or a default is computed from the server type and version, such as\r\n\r\n    A Paper Minecraft Server powered by Docker\r\n\r\nThat way you can easily differentiate between several server types you may have started.\r\n\r\nThe section symbol (§) and other unicode characters are automatically converted to allow [formatting codes](https://minecraft.wiki/w/Formatting_codes) to be used consistently with all server versions. For Minecraft versions less than 1.20, unicode characters in `server.properties` will be escaped as `\\uXXXX`, by default. That behavior can be altered by setting `SERVER_PROPERTIES_ESCAPE_UNICODE` to \"true\" or \"false\".\r\n\r\n!!! example\r\n\r\n    With `docker run`\r\n    \r\n         -e MOTD=\"A §l§cMinecraft§r §nserver\"\r\n    \r\n    or within a compose file\r\n    \r\n        environment:\r\n          MOTD: \"A §l§cMinecraft§r §nserver\"\r\n\r\n    renders\r\n    \r\n    ![](../img/motd-example.png)\r\n\r\nTo produce a multi-line MOTD, embed a newline character as `\\n` in the string, such as the following example.\r\n\r\n!!! example \"Multi-line MOTD\"\r\n\r\n    With `docker run`\r\n    \r\n    ```\r\n    -e MOTD=\"Line one\\nLine two\"\r\n    ```\r\n    \r\n    or within a compose file\r\n    \r\n    ```yaml\r\n          MOTD: |\r\n            line one\r\n            line two\r\n    # or\r\n    #      MOTD: \"line one\\nline two\"\r\n    ```\r\n\r\nThe following example combines a multi-line MOTD with [placeholders](#placeholders) from the latest version of the installed modpack:\r\n\r\n!!! example\r\n\r\n    ```yaml\r\n    MOD_PLATFORM: AUTO_CURSEFORGE\r\n    CF_SLUG: craftoria\r\n    MOTD: |\r\n      A %TYPE% server on %VERSION%\r\n      running %MODPACK_NAME% %MODPACK_VERSION%\r\n    ```\r\n    \r\n    ![](../img/motd-with-placeholders.png)\r\n\r\n### Difficulty\r\n\r\nThe difficulty level (default: `easy`) can be set like:\r\n\r\n```\r\ndocker run -d -e DIFFICULTY=hard ...\r\n```\r\n\r\nValid values are: `peaceful`, `easy`, `normal`, and `hard`, and an\r\nerror message will be output in the logs if it's not one of these\r\nvalues.\r\n\r\nRefer to [the Minecraft wiki](https://minecraft.wiki/w/Difficulty)\r\n\r\n### Whitelist Players\r\n\r\n!!! warning \"For public servers\" \r\n    \r\n    It is very important to consider setting a whitelist of expected players.\r\n\r\nTo whitelist players for your Minecraft server, you can:  \r\n\r\n- Provide a list of usernames and/or UUIDs separated by commas or newlines via the `WHITELIST` environment variable\r\n- Provide the URL or container path to a whitelist file via `WHITELIST_FILE` that will be retrieved/copied into the standard location\r\n\r\n!!! example \r\n\r\n    In a compose file, a text block can be used to improve maintainability, such as\r\n\r\n    ```yaml\r\n          WHITELIST: |\r\n            user1\r\n            user2\r\n            user3\r\n    ```\r\n\r\nWhen either is set, [whitelisting of connecting users](https://minecraft.wiki/w/Server.properties#white-list) is enabled.\r\n\r\nTo change the behavior when the whitelist file already exists, set the variable `EXISTING_WHITELIST_FILE` to one of the following options:\r\n\r\n`SKIP`\r\n: Skip processing of the whitelist file when one is already present. This is the same as setting the legacy variable `OVERRIDE_WHITELIST` to \"false\".\r\n\r\n`SYNCHRONIZE`\r\n: Synchronize the list of users in the file with the `WHITELIST` or `WHITELIST_FILE` provided. When using both, `WHITELIST` will take precedence. This is the same as setting the legacy variable `OVERRIDE_WHITELIST` to \"true\".\r\n\r\n`MERGE`\r\n: Merge the list of users from `WHITELIST` into the existing file. `WHITELIST_FILE` cannot be used with this option.\r\n\r\n`SYNC_FILE_MERGE_LIST` (default)\r\n: When `WHITELIST_FILE` is provided it will overwrite an existing whitelist file. Also, if `WHITELIST` is provided, then those users will be merged into the newly copied file.\r\n\r\n!!! note \r\n\r\n    For versions prior to 1.7.3, `white-list.txt` will be maintained instead. Only usernames are supported for those versions.\r\n\r\nTo [enforce the whitelist changes immediately](https://minecraft.wiki/w/Server.properties#enforce-whitelist) when whitelist commands are used , set `ENFORCE_WHITELIST` to \"true\". If managing the whitelist file manually, `ENABLE_WHITELIST` can be set to \"true\" to set the `white-list` property.\r\n\r\n!!! tip \"Changing user API provider\"\r\n\r\n    The usernames provided for whitelist and ops processing are resolved using either [PlayerDB](https://playerdb.co/) or [Mojang's API](https://wiki.vg/Mojang_API#Username_to_UUID). The default uses PlayerDB, but can be changed by setting the environment variable `USER_API_PROVIDER` to \"mojang\". Sometimes one or the other service can become overloaded, which is why there is the ability to switch providers.\r\n\r\n\r\n### Op/Administrator Players\r\n\r\nSimilar to the whitelist, users can be provisioned as operators (aka administrators) to your Minecraft server by\r\n\r\n- Providing a list of usernames and/or UUIDs separated by commas or newlines via the `OPS` environment variable\r\n- Providing the URL or container path to an ops file via `OPS_FILE` that will be retrieved/copied into the standard location\r\n\r\n!!! example\r\n\r\n    In a compose file, a text block can be used to improve maintainability, such as\r\n\r\n    ```yaml\r\n          OPS: |\r\n            user1\r\n            user2\r\n            user3\r\n    ```\r\n\r\nTo change the behavior when the ops file already exists, set the variable `EXISTING_OPS_FILE` to one of the following options:\r\n\r\n`SKIP`\r\n: Skip processing of the ops file when one is already present. This is the same as setting the legacy variable `OVERRIDE_OPS` to \"false\".\r\n\r\n`SYNCHRONIZE`\r\n: Synchronize the list of users in the file with the `OPS` or `OPS_FILE` provided. When using both, `OPS` will take precedence. The `level` and `bypassesPlayerLimit` will be retained from previous entries. This is the same as setting the legacy variable `OVERRIDE_OPS` to \"true\".\r\n\r\n`MERGE`\r\n: Merge the list of users from `OPS` into the existing file. `OPS_FILE` cannot be used with this option.\r\n\r\n`SYNC_FILE_MERGE_LIST` (default)\r\n: When `OPS_FILE` is provided it will overwrite an existing ops file. Also, if `OPS` is provided, then those users will be merged into the newly copied file.\r\n\r\n!!! note\r\n\r\n    For versions prior to 1.7.3, `ops.txt` will be maintained instead. Only usernames are supported for those versions.\r\n\r\n### Enable/disable initial selection of datapacks\r\n\r\nNew to [22W42A](https://www.minecraft.net/en-us/article/minecraft-snapshot-22w42a), datapacks with feature flags can be controlled before initial world creation by setting these to a comma separated list of packs:\r\n\r\n- `INITIAL_ENABLED_PACKS`\r\n  such as \"update_1_20,bundle\"\r\n- `INITIAL_DISABLED_PACKS`\r\n\r\n### Server icon\r\n\r\nA server icon can be configured by setting the `ICON` variable to a URL to download or a container path. The image will be automatically downloaded (if a URL), scaled, and converted from any other image format:\r\n\r\n!!! example\r\n\r\n    Using `docker run`:\r\n    \r\n    ```\r\n    docker run -d -e ICON=http://..../some/image.png ...\r\n    ```\r\n    \r\n    In compose file:\r\n    \r\n    ```yaml\r\n    environment:\r\n      ICON: http://..../some/image.png\r\n    ```\r\n    \r\n    Using a file from host filesystem:\r\n    \r\n    ```yaml\r\n    environment:\r\n      ICON: /icon.png\r\n      OVERRIDE_ICON: true\r\n    volumes:\r\n      ./icon.png:/icon.png\r\n    ```\r\n\r\nBy default an existing `server-icon.png` file will not be replaced, that can be changed by setting `OVERRIDE_ICON` to \"true\".\r\n\r\n### RCON\r\n\r\nRCON is **enabled by default** to allow for graceful shut down of the server and coordination of save state during backups. RCON can be disabled by setting `ENABLE_RCON` to \"false\".\r\n\r\n!!! warning\r\n\r\n    Disabling RCON will remove and limit some features, such as interactive and color console support.\r\n\r\n#### RCON Password\r\n\r\nThe default password is randomly generated on each startup. However, you can specify a password using one of the following environment variables:\r\n\r\n* Set `RCON_PASSWORD` to your desired password.\r\n* Set `RCON_PASSWORD_FILE` to the path of a file containing the password.\r\n\r\nUsing `RCON_PASSWORD_FILE` is the recommended method for managing sensitive data, as it allows full support for [Docker Secrets](https://docs.docker.com/compose/how-tos/use-secrets/).\r\n\r\n??? example\r\n    ```yaml title=\"compose.yaml\"\r\n    services:\r\n      mc:\r\n        image: itzg/minecraft-server:latest\r\n        pull_policy: daily\r\n        tty: true\r\n        stdin_open: true\r\n        ports:\r\n          - \"25565:25565\"\r\n        environment:\r\n          EULA: \"TRUE\"\r\n          RCON_PASSWORD_FILE: /run/secrets/rcon_pass # Points to the path where the secret is mounted\r\n        volumes:\r\n          # attach the relative directory 'data' to the container's /data path\r\n          - ./data:/data\r\n        secrets:\r\n          - rcon_pass\r\n    \r\n    secrets:\r\n      rcon_pass:\r\n        file: ./rcon_password # local file containing the password\r\n    ```\r\n!!! warning\r\n    **BE CAUTIOUS OF MAPPING THE RCON PORT EXTERNALLY** unless you are aware of all the consequences and have set a **secure password**.\r\n\r\n!!! info \r\n\r\n    Mapping ports (`-p` command line or `ports` in compose) outside the container and docker networking needs to be a purposeful choice. Most production Docker deployments do not need any of the Minecraft ports mapped externally from the server itself.\r\n\r\nBy default, the server listens for RCON on port 25575 within the container. It can be changed with `RCON_PORT` but only do this if you have a very good reason. **DO NOT change `rcon.port` via `server.properties`** or integrations will break.\r\n\r\n### Query\r\n\r\nSet the environment variable `ENABLE_QUERY` to \"true\" to enable the gamespy query protocol. Maps to the server property [enable-query](https://minecraft.wiki/w/Server.properties#enable-query). By default, the query port will be `25565` (UDP) but can be changed with the `QUERY_PORT` environment variable.\r\n\r\n### Level Seed\r\n\r\nIf you want to create the Minecraft level with a specific seed, set the environment variable `SEED`, which maps to the [level-seed](https://minecraft.wiki/w/Server.properties#level-seed) property.\r\n\r\nIf using a negative value for the seed, make sure to quote the value such as:\r\n\r\n!!! example \"Using docker run\"\r\n\r\n    ``` \r\n    -e SEED=\"-1785852800490497919\"\r\n    ```\r\n\r\n!!! example \"Using compose\"\r\n\r\n    ```yaml\r\n    environment:\r\n      SEED: \"-1785852800490497919\"\r\n    ```\r\n\r\n### Game Mode\r\n\r\nBy default, Minecraft servers are configured to run in Survival mode. You can\r\nchange the mode using `MODE` where you can either provide the [standard\r\nnumerical values](http://minecraft.wiki/Game_mode#Game_modes) or the\r\nshortcut values:\r\n\r\n- creative\r\n- survival\r\n- adventure\r\n- spectator (only for Minecraft 1.8 or later)\r\n\r\nFor example:\r\n\r\n    docker run -d -e MODE=creative ...\r\n\r\n### Level Type and Generator Settings\r\n\r\nBy default, a standard world is generated with hills, valleys, water, etc. A different level type can\r\nbe configured by setting `LEVEL_TYPE` to [an expected type listed here](https://minecraft.wiki/w/Server.properties#level-type).\r\n\r\nFor some of the level types, `GENERATOR_SETTINGS` can be used to further customize the world generation.\r\n\r\nTo configure the `GENERATOR_SETTINGS` you need to add the appropriate `GeneratorOptions` JSON configuration. In the case of a superflat world, you may omit the `flat_world_options`.\r\n\r\nThe layers are applied from -64 and up and are added in the order of the list\r\n\r\nExample for a superflat world:\r\n\r\n- 1x bedrock\r\n- 2x stone\r\n- 15x sandstone\r\n- Desert biome\r\n\r\n```yaml\r\nenvironment:\r\n  LEVEL_TYPE: FLAT\r\n  GENERATOR_SETTINGS: >-\r\n      {\r\n          \"layers\": [\r\n              {\r\n                  \"block\": \"minecraft:bedrock\",\r\n                  \"height\": 1\r\n              },\r\n              {\r\n                  \"block\": \"minecraft:stone\",\r\n                  \"height\": 2\r\n              },\r\n              {\r\n                  \"block\": \"minecraft:sandstone\",\r\n                  \"height\": 15\r\n              }\r\n          ],\r\n          \"biome\": \"minecraft:desert\"\r\n      }\r\n```\r\nFor more details, refer to the Minecraft Wiki sections for [Superflat Multiplayer](https://minecraft.wiki/w/Superflat#Multiplayer) and [generator options tag format](https://minecraft.wiki/w/Java_Edition_level_format#generatorOptions_tag_format).\r\n\r\n### Custom Server Resource Pack\r\n\r\nYou can set a link to a custom resource pack and set it's checksum using the `RESOURCE_PACK` and `RESOURCE_PACK_SHA1` options respectively, the default is blank:\r\n\r\n    docker run -d -e 'RESOURCE_PACK=http://link.com/to/pack.zip?=1' -e 'RESOURCE_PACK_SHA1=d5db29cd03a2ed055086cef9c31c252b4587d6d0'\r\n\r\nYou can enforce the resource pack on clients by setting `RESOURCE_PACK_ENFORCE` to `TRUE` (default: `FALSE`).\r\n\r\n### Level / World Save Name\r\n\r\nYou can either switch between world saves or run multiple containers with different saves by using the `LEVEL` option,\r\nwhere the default is \"world\":\r\n\r\n    docker run -d -e LEVEL=bonus ...\r\n\r\n> **NOTE:** if running multiple containers be sure to either specify a different `-v` host directory for each\r\n`LEVEL` in use or don't use `-v` and the container's filesystem will keep things encapsulated.\r\n\r\n> **INFO** Refer to the [data directory](../data-directory.md) section for a visual description of where the `$LEVEL` directory is situated.\r\n\r\n### Server port\r\n\r\n> **WARNING:** only change this value if you know what you're doing. It only needs to be changed when using host-networking and it is rare that host networking should be used. Use `-p` port mappings instead.\r\n\r\nIf you must, the server port can be set like:\r\n\r\n!!! example \"Using docker run\"\r\n\r\n    ```\r\n    docker run -d -e SERVER_PORT=25566 ...\r\n    ```\r\n\r\n!!! example \"Using compose\"\r\n\r\n    ```yaml\r\n    environment:\r\n      SERVER_PORT: 25566\r\n    ```\r\n\r\n**however**, be sure to change your port mapping accordingly and be prepared for some features to break.\r\n\r\n### Custom server properties\r\n\r\nSome mods/plugins utilize custom `server.properties` entries which can be declared via the `CUSTOM_SERVER_PROPERTIES` environment variable. The contents must be newline delimited `name=value` pairs.\r\n\r\nWithin a compose file, newline delimited entries can be declared as shown here:\r\n\r\n```yaml\r\n      CUSTOM_SERVER_PROPERTIES: |\r\n        custom1=value1\r\n        defaultworldgenerator-port=f8c04631-f744-11ec-b260-f02f74b094e0\r\n```\r\n\r\nWhen using `docker run` from a bash shell, the entries must be quoted with the `$'` syntax, such as\r\n\r\n```\r\n-e CUSTOM_SERVER_PROPERTIES=$'k1=v1\\nk2=v2'\r\n```\r\n\r\n### Other server property mappings\r\n\r\n!!! warning \"Version compatibility\"\r\n\r\n    Not all server properties are supported by all versions of Minecraft. Since this image supports a wide range of versions, please consult the [server properties documentation](https://minecraft.wiki/w/Server.properties) for the version you are using.\r\n\r\n| Environment Variable                    | Server Property                                                                                                               |\r\n|-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|\r\n| ACCEPTS_TRANSFERS                       | [accepts-transfers](https://minecraft.wiki/w/Server.properties#accepts-transfers)                                             |\r\n| ALLOW_FLIGHT                            | [allow-flight](https://minecraft.wiki/w/Server.properties#allow-flight)                                                       |\r\n| ALLOW_NETHER                            | [allow-nether](https://minecraft.wiki/w/Server.properties#allow-nether)                                                       |\r\n| ANNOUNCE_PLAYER_ACHIEVEMENTS            | [announce-player-achievements](https://minecraft.wiki/w/Server.properties#announce-player-achievements)                       |\r\n| BROADCAST_CONSOLE_TO_OPS                | [broadcast-console-to-ops](https://minecraft.wiki/w/Server.properties#broadcast-console-to-ops)                               |\r\n| BROADCAST_RCON_TO_OPS                   | [broadcast-rcon-to-ops](https://minecraft.wiki/w/Server.properties#broadcast-rcon-to-ops)                                     |\r\n| BUG_REPORT_LINK                         | [bug-report-link](https://minecraft.wiki/w/Server.properties#bug-report-link)                                                 |\r\n| ENABLE_COMMAND_BLOCK                    | [enable-command-block](https://minecraft.wiki/w/Server.properties#enable-command-block)                                       |\r\n| ENABLE_STATUS                           | [enable-status](https://minecraft.wiki/w/Server.properties#enable-status)                                                     |\r\n| ENFORCE_SECURE_PROFILE                  | [enforce-secure-profile](https://minecraft.wiki/w/Server.properties#enforce-secure-profile)                                   |\r\n| ENTITY_BROADCAST_RANGE_PERCENTAGE       | [entity-broadcast-range-percentage](https://minecraft.wiki/w/Server.properties#entity-broadcast-range-percentage)             |\r\n| FORCE_GAMEMODE                          | [force-gamemode](https://minecraft.wiki/w/Server.properties#force-gamemode)                                                   |\r\n| FUNCTION_PERMISSION_LEVEL               | [function-permission-level](https://minecraft.wiki/w/Server.properties#function-permission-level)                             |\r\n| GENERATE_STRUCTURES                     | [generate-structures](https://minecraft.wiki/w/Server.properties#generate-structures)                                         |\r\n| GENERATOR_SETTINGS                      | [generator-settings](https://minecraft.wiki/w/Server.properties#generator-settings)                                           |                                                                                                                    \r\n| HARDCORE                                | [hardcore](https://minecraft.wiki/w/Server.properties#hardcore)                                                               |\r\n| HIDE_ONLINE_PLAYERS                     | [hide-online-players](https://minecraft.wiki/w/Server.properties#hide-online-players)                                         |\r\n| LOG_IPS                                 | [log-ips](https://minecraft.wiki/w/Server.properties#log-ips)                                                                 |\r\n| MANAGEMENT_SERVER_ALLOWED_ORIGINS       | [management-server-allowed-origins](https://minecraft.wiki/w/Server.properties#management-server-allowed-origins)             |\r\n| MANAGEMENT_SERVER_ENABLED               | [management-server-enabled](https://minecraft.wiki/w/Server.properties#management-server-enabled)                             |\r\n| MANAGEMENT_SERVER_HOST                  | [management-server-host](https://minecraft.wiki/w/Server.properties#management-server-host)                                   |\r\n| MANAGEMENT_SERVER_PORT                  | [management-server-port](https://minecraft.wiki/w/Server.properties#management-server-port)                                   |\r\n| MANAGEMENT_SERVER_SECRET                | [management-server-secret](https://minecraft.wiki/w/Server.properties#management-server-secret)                               |\r\n| MANAGEMENT_SERVER_TLS_ENABLED           | [management-server-tls-enabled](https://minecraft.wiki/w/Server.properties#management-server-tls-enabled)                     |\r\n| MANAGEMENT_SERVER_TLS_KEYSTORE          | [management-server-tls-keystore](https://minecraft.wiki/w/Server.properties#management-server-tls-keystore)                   |\r\n| MANAGEMENT_SERVER_TLS_KEYSTORE_PASSWORD | [management-server-tls-keystore-password](https://minecraft.wiki/w/Server.properties#management-server-tls-keystore-password) |\r\n| MAX_BUILD_HEIGHT                        | [max-build-height](https://minecraft.wiki/w/Server.properties#max-build-height)                                               |\r\n| MAX_COMMAND_CHAIN_LENGTH                | [max-command-chain-length](https://minecraft.wiki/w/Server.properties#max-command-chain-length)                               |\r\n| MAX_ENTITY_CRAMMING                     | [max-entity-cramming](https://minecraft.wiki/w/Server.properties#max-entity-cramming)                                         |\r\n| MAX_ENTITY_COLLISION_RADIUS             | [max-entity-collision-radius](https://minecraft.wiki/w/Server.properties#max-entity-collision-radius)                         |\r\n| MAX_FUNCTION_CHAIN_DEPTH                | [max-function-chain-depth](https://minecraft.wiki/w/Server.properties#max-function-chain-depth)                               |\r\n| MAX_NEIGHBORS                           | [max-neighbors](https://minecraft.wiki/w/Server.properties#max-neighbors)                                                     |\r\n| MAX_CHAINED_NEIGHBOR_UPDATES            | [max-chained-neighbor-updates](https://minecraft.wiki/w/Server.properties#max-chained-neighbor-updates)                       |\r\n| MAX_PLAYERS                             | [max-players](https://minecraft.wiki/w/Server.properties#max-players)                                                         |\r\n| MAX_TICK_TIME                           | [max-tick-time](https://minecraft.wiki/w/Server.properties#max-tick-time)                                                     |\r\n| MAX_WORLD_SIZE                          | [max-world-size](https://minecraft.wiki/w/Server.properties#max-world-size)                                                   |\r\n| NETWORK_COMPRESSION_THRESHOLD           | [network-compression-threshold](https://minecraft.wiki/w/Server.properties#network-compression-threshold)                     |\r\n| ONLINE_MODE                             | [online-mode](https://minecraft.wiki/w/Server.properties#online-mode)                                                         |\r\n| OP_PERMISSION_LEVEL                     | [op-permission-level](https://minecraft.wiki/w/Server.properties#op-permission-level)                                         |\r\n| PAUSE_WHEN_EMPTY_SECONDS                | [pause-when-empty-seconds](https://minecraft.wiki/w/Server.properties#pause-when-empty-seconds)                               |\r\n| PLAYER_IDLE_TIMEOUT                     | [player-idle-timeout](https://minecraft.wiki/w/Server.properties#player-idle-timeout)                                         |\r\n| PREVENT_PROXY_CONNECTIONS               | [prevent-proxy-connections](https://minecraft.wiki/w/Server.properties#prevent-proxy-connections)                             |\r\n| PVP                                     | [pvp](https://minecraft.wiki/w/Server.properties#pvp)                                                                         |\r\n| RATE_LIMIT                              | [rate-limit](https://minecraft.wiki/w/Server.properties#rate-limit)                                                           |\r\n| REGION_FILE_COMPRESSION                 | [region-file-compression](https://minecraft.wiki/w/Server.properties#region-file-compression)                                 |\r\n| RESOURCE_PACK_ID                        | [resource-pack-id](https://minecraft.wiki/w/Server.properties#resource-pack-id)                                               |\r\n| RESOURCE_PACK_PROMPT                    | [resource-pack-prompt](https://minecraft.wiki/w/Server.properties#resource-pack-prompt)                                       |\r\n| SERVER_NAME                             | [server-name](https://minecraft.wiki/w/Server.properties#server-name)                                                         |\r\n| SIMULATION_DISTANCE                     | [simulation-distance](https://minecraft.wiki/w/Server.properties#simulation-distance)                                         |\r\n| SNOOPER_ENABLED                         | [snooper-enabled](https://minecraft.wiki/w/Server.properties#snooper-enabled)                                                 |\r\n| SPAWN_ANIMALS                           | [spawn-animals](https://minecraft.wiki/w/Server.properties#spawn-animals)                                                     |\r\n| SPAWN_MONSTERS                          | [spawn-monsters](https://minecraft.wiki/w/Server.properties#spawn-monsters)                                                   |\r\n| SPAWN_NPCS                              | [spawn-npcs](https://minecraft.wiki/w/Server.properties#spawn-npcs)                                                           |\r\n| SPAWN_PROTECTION                        | [spawn-protection](https://minecraft.wiki/w/Server.properties#spawn-protection)                                               |\r\n| STATUS_HEARTBEAT_INTERVAL               | [status-heartbeat-interval](https://minecraft.wiki/w/Server.properties#status-heartbeat-interval)                             |\r\n| SYNC_CHUNK_WRITES                       | [sync-chunk-writes](https://minecraft.wiki/w/Server.properties#sync-chunk-writes)                                             |\r\n| USE_NATIVE_TRANSPORT                    | [use-native-transport](https://minecraft.wiki/w/Server.properties#use-native-transport)                                       |\r\n| VIEW_DISTANCE                           | [view-distance](https://minecraft.wiki/w/Server.properties#view-distance)                                                     |\r\n"
  },
  {
    "path": "docs/css/extra.css",
    "content": ""
  },
  {
    "path": "docs/data-directory.md",
    "content": "Everything the container manages is located under the **container's** `/data` path, as shown here:\r\n\r\n![](img/level-vs-world.drawio.png)\r\n\r\n!!! note\r\n\r\n    The container path `/data` is pre-declared as a volume, so if you do nothing then it will be allocated as an anonymous volume. As such, it is subject to removal when the container is removed. \r\n\r\n### Attaching data directory to host filesystem\r\n\r\nIn most cases the easiest way to persist and work with the minecraft data files is to use [bind mounts](https://docs.docker.com/storage/bind-mounts/) with the `-v` argument to map a directory on your host machine to the container's `/data` directory. In the following example, the path `/home/user/minecraft-data` **must be** a directory on your host machine:\r\n\r\n    -v /home/user/minecraft-data:/data\r\n       ------------------------- -----\r\n        |                         |\r\n        |                         +-- must always be /data\r\n        |\r\n        +-- replace with a directory on your host machine\r\n\r\nWhen attached in this way you can stop the server, edit the configuration under your attached directory and start the server again to pick up the new configuration.\r\n\r\n!!! important \"Rootless, Podman, SELinux, AppArmor usage\"\r\n    When running rootless containers, such as with Podman, or using SELinux / AppArmor on your system, append \":Z\" to the volume mapping. For example:\r\n\r\n    ```\r\n    /home/user/minecraft-data:/data:Z\r\n    ```\r\n\r\n    There might be a safer/better way to accommodate these systems. Please post an issue or PR if you have more information.\r\n    \r\nWith Docker Compose, setting up a host attached directory is even easier since relative paths can be configured. For example, with the following `compose.yaml` Docker will automatically create/attach the relative directory `minecraft-data` to the container.\r\n\r\n```yaml title=\"compose.yaml\"\r\n\r\nservices:\r\n  mc:\r\n    image: itzg/minecraft-server:latest\r\n    pull_policy: daily\r\n    ports:\r\n      - 25565:25565\r\n    environment:\r\n      EULA: \"TRUE\"\r\n    tty: true\r\n    stdin_open: true\r\n    restart: unless-stopped\r\n    volumes:\r\n      # attach a directory relative to the directory containing this compose file\r\n      - ./minecraft-data:/data\r\n```\r\n\r\n### Converting anonymous `/data` volume to named volume\r\n\r\nIf you had used the commands in the first section, without the `-v` volume attachment, then an anonymous data volume was created by Docker. You can later bring over that content to a named or host attached volume using the following procedure.\r\n\r\n!!! note \r\n\r\n    In this example, it is assumed the original container was given a `--name` of \"mc\", so change the container identifier accordingly.\r\n    \r\n    You can also locate the Docker-managed directory from the `Source` field obtained from `docker inspect <container id or name> -f \"{{json .Mounts}}\"`\r\n\r\nFirst, stop the existing container:\r\n\r\n``` shell\r\ndocker stop mc\r\n```\r\n\r\nUse a temporary container to copy over the anonymous volume's content into a named volume, \"mc\" in this case:\r\n\r\n``` shell\r\ndocker run --rm --volumes-from mc -v mc:/new alpine cp -avT /data /new\r\n```\r\n\r\nNow you can recreate the container with any environment variable changes, etc by attaching the named volume created from the previous step:\r\n\r\n``` shell\r\ndocker run -d -it --name mc-new -v mc:/data -p 25565:25565 -e EULA=TRUE -e MEMORY=2G itzg/minecraft-server\r\n```\r\n\r\n### Locating filesystem path of anonymous volume\r\n\r\nThe `Source` field from the output of this command will show where the anonymous volume is mounted from:\r\n\r\n``` shell\r\ndocker inspect -f \"{{json .Mounts}}\" CONTAINER_NAME_OR_ID\r\n```\r\n\r\n!!! note\r\n\r\n    On Windows with WSL the volumes path is `\\\\wsl$\\docker-desktop-data\\data\\docker\\volumes`\r\n"
  },
  {
    "path": "docs/img/.gitignore",
    "content": "/.*.bkp"
  },
  {
    "path": "docs/img/Minecraft server containment.xml",
    "content": "<mxfile userAgent=\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36\" version=\"6.4.4\" editor=\"www.draw.io\" type=\"github\"><diagram name=\"Page-1\">1VVNc5swEP01HDMDUiDOMbGd5pK2Ux96VkGAxgJRIRvcX9+VWD40ODOZ1j0Uz9jS2w+t3ts1Ad1W/SfNmvJNZVwGJMz6gO4CQjZRCN8WuAxA8vgwAIUW2QBFM3AQvziCGFecRMZbz9EoJY1ofDBVdc1T42FMa9X5brmS/qkNK/gKOKRMrtHvIjMlolEYzoZXLooSj97EaPjB0mOh1anG8wJCc/cM5oqNudC/LVmmugVE9wHdaqXMsKr6LZeW2pG2Ie7lHetUt+a1+UgAGQLOTJ74WHEiIfQ5E2dbn7kgJ8nPky3q2fDe3DEpijqgT+AheW5mK6wK/HVZ2obVI7ZT6ZFrCHlVrRkdoLSlzwJ2BYwo8WohXSkMPzQstfsOWhCcSlNJ2EX21JFVu8mFlFsllXahdJ/YD+Bnro0AyZ+Gu+yMslnwZjt3LVd/KurCpgptLlWbA5aBXNs0vH+X/mgSFWaFq4obfQEXDKDYBhd/2809RzeIlYt2mxwZ9nkxZZ61hgXKfV16upK+EjBMmuXmruX67KSCATMMYL2SwDU5z5DkvxAE52NNvdHqyBeOoXs8UWIbplP8A3ES3UCVja/K41oVEl5RJYpuoMr9SpU34B+Qr0rD0ITD0JE4TuI/nopwLcKLexYi6OFet6CT3Pt8TjwtCI3IFUJv0eXxis9v2y+fr/D58P/yGf87PmE7v4ycbfHCp/vf</diagram></mxfile>"
  },
  {
    "path": "docs/img/docs-banner.txt",
    "content": "Generated at https://textcraft.net/\r\n\r\nLine 1:\r\n\"Click here for\"\r\nSmall, none, Minecraftia\r\n\r\nLine 2:\r\n\"Documentation\"\r\nLarge, non, Minecrafter\r\n\r\nOverall settings:\r\n- drop shadow"
  },
  {
    "path": "docs/index.md",
    "content": "# Intro\r\n\r\n[![Docker Pulls](https://img.shields.io/docker/pulls/itzg/minecraft-server.svg)](https://hub.docker.com/r/itzg/minecraft-server/)\r\n[![Docker Stars](https://img.shields.io/docker/stars/itzg/minecraft-server.svg?maxAge=2592000)](https://hub.docker.com/r/itzg/minecraft-server/)\r\n[![GitHub Issues](https://img.shields.io/github/issues-raw/itzg/docker-minecraft-server.svg)](https://github.com/itzg/docker-minecraft-server/issues)\r\n[![Discord](https://img.shields.io/discord/660567679458869252?label=Discord&logo=discord)](https://discord.gg/DXfKpjB)\r\n[![Build and Publish](https://github.com/itzg/docker-minecraft-server/workflows/Build%20and%20Publish/badge.svg)](https://github.com/itzg/docker-minecraft-server/actions)\r\n[![](https://img.shields.io/badge/Donate-Buy%20me%20a%20coffee-orange.svg)](https://www.buymeacoffee.com/itzg)\r\n\r\nThis docker image provides a Minecraft Server that will automatically download the latest stable\r\nversion at startup. You can also run/upgrade to any specific version or the\r\nlatest snapshot. See the _Versions_ section below for more information.\r\n\r\nTo simply use the latest stable version, run\r\n\r\n    docker run -d -it -p 25565:25565 -e EULA=TRUE itzg/minecraft-server\r\n\r\nwhere, in this case, the standard server port 25565 will be exposed on your host machine.\r\n\r\n!!! important \"Persistent Data\"\r\n\r\n    The Minecraft server will store its data in the container's `/data` directory. This directory can be [mounted](https://docs.docker.com/storage/volumes/) from the host machine or a managed volume.\r\n\r\n    Using `docker run` add a `-v` option somewhere before the image name:\r\n    \r\n    ```\r\n    ... -v /path/on/host:/data itzg/minecraft-server\r\n    ```\r\n    \r\n    Using docker compose, add a `volumes` section to the service definition:\r\n    \r\n    ```yaml\r\n    services:\r\n      mc:\r\n        # ... image and environment section\r\n        volumes:\r\n          # attach the relative directory 'data' to the container's /data path\r\n          - ./data:data\r\n    ```\r\n\r\n!!! note\r\n\r\n    If you plan on running a server for a longer amount of time it is highly recommended using a management layer such as [Docker Compose](#using-docker-compose) or [Kubernetes](misc/deployment/index.md#on-kubernetes) to allow for incremental reconfiguration and image upgrades.\r\n\r\n!!! info \r\n\r\n    Be sure to always include `-e EULA=TRUE` in your commands and container definitions, as Mojang/Microsoft requires EULA acceptance.\r\n\r\nBy default, the container will download the latest version of the \"vanilla\" [Minecraft: Java Edition server](https://www.minecraft.net/en-us/download/server) provided by Mojang. The [`VERSION`](versions/minecraft.md) and the [`TYPE`](types-and-platforms/index.md) can be configured to create many variations of desired Minecraft server. \r\n\r\n## Using [Docker Compose](https://docs.docker.com/compose/)\r\n\r\n1. Create a new directory\r\n2. Put the contents of the file below in a file called `compose.yaml`\r\n3. Run `docker compose up -d` in that directory\r\n4. Done! Point your client at your host's name/IP address and port 25565.\r\n\r\n```yaml title=\"compose.yaml\"\r\n\r\nservices:\r\n  mc:\r\n    image: itzg/minecraft-server:latest\r\n    pull_policy: daily\r\n    tty: true\r\n    stdin_open: true\r\n    ports:\r\n      - \"25565:25565\"\r\n    environment:\r\n      EULA: \"TRUE\"\r\n    volumes:\r\n      # attach the relative directory 'data' to the container's /data path\r\n      - ./data:/data\r\n```\r\n\r\nTo apply changes made to the compose file, just run `docker compose up -d` again.\r\n\r\nFollow the logs of the container using `docker compose logs -f`, check on the status with `docker compose ps`, and stop the container using `docker compose stop`.\r\n\r\n!!! note \"Configurator Tool\"\r\n    If you prefer to use an interactive tool to create or edit a Docker Compose file for this image, you can check out [setupmc.com's configurator](https://setupmc.com/java-server/). It provides a form that supports most of the image variables and generates the `compose.yaml` file in real time.\r\n\r\n!!! note \"More Compose Examples\"\r\n    There are more [examples located in the Github repo](https://github.com/itzg/docker-minecraft-server/tree/master/examples).\r\n\r\n!!! note \"Deployment Examples\"\r\n    The [deployments page](misc/deployment/index.md) provides more examples of deployment with and beyond Docker Compose.\r\n\r\n"
  },
  {
    "path": "docs/misc/autopause-autostop/autopause.md",
    "content": "# Auto-Pause\r\n\r\n!!! important\r\n\r\n    As of [1.21.2](https://minecraft.wiki/w/Java_Edition_1.21.2) it is not recommend to use this feature since Minecraft server natively auto-pauses when the server is empty. That is configured via the enivironment variable `PAUSE_WHEN_EMPTY_SECONDS`, which maps to the `pause-when-empty-seconds` server property. \r\n\r\nAn auto-pause functionality is provided that monitors whether clients are connected to the server. If a client is not connected for a specified time, the Java process is put into a pause state. When a client attempts to connect while the process is paused, then process will be restored to a running state. The experience for the client does not change. This feature can be enabled by setting the environment variable `ENABLE_AUTOPAUSE` to \"true\".\r\n\r\n!!! important\r\n\r\n**You must greatly increase or disable max-tick-time watchdog functionality.** From the server's point of view, the pausing causes a single tick to take as long as the process is stopped, so the server watchdog might intervene after the process is continued, possibly forcing a container restart. To prevent this, ensure that the `max-tick-time` in the `server.properties` file is set to a very large value or -1 to disable it entirely, which is highly recommended. That can be set with `MAX_TICK_TIME` as described in [server properties](../../configuration/server-properties.md).\r\n\r\n    Non-vanilla versions might have their own configuration file, you might have to disable their watchdogs separately. For PaperMC servers, you need to send the JVM flag `-Ddisable.watchdog=true`, this can be done with the docker env variable `-e JVM_DD_OPTS=disable.watchdog:true`\r\n\r\n    On startup the `server.properties` file is checked and, if applicable, a warning is printed to the terminal. When the server is created (no data available in the persistent directory), the properties file is created with the Watchdog disabled.\r\n\r\nThe utility used to wake the server (`knock(d)`) works at network interface level. So the correct interface has to be set using the `AUTOPAUSE_KNOCK_INTERFACE` variable when using non-default networking environments (e.g. host-networking, Portainer oder NAS solutions). See the description of the variable below.\r\n\r\nA file called `.paused` is created in `/data` directory when the server is paused and removed when the server is resumed. Other services may check for this file's existence before waking the server.\r\n\r\nA `.skip-pause` file can be created in the `/data` directory to make the server skip autopausing, for as long as the file is present. The autopause timer will also be reset.\r\n\r\nA starting, example compose file has been provided in [the examples](https://github.com/itzg/docker-minecraft-server/blob/master/examples/autopause/compose.yml).\r\n\r\n!!! note \r\n\r\n    When configuring kubernetes readiness/liveness health checks with auto-pause enabled, be sure to reference the `mc-health` wrapper script rather than `mc-status` directly.\r\n\r\n## Additional configuration\r\n\r\nThe following environment variables define the behaviour of auto-pausing:\r\n\r\n- `AUTOPAUSE_TIMEOUT_EST`, default `3600` (seconds)\r\n  describes the time between the last client disconnect and the pausing of the process (read as timeout established)\r\n- `AUTOPAUSE_TIMEOUT_INIT`, default `600` (seconds)\r\n  describes the time between server start and the pausing of the process, when no client connects inbetween (read as timeout initialized)\r\n- `AUTOPAUSE_TIMEOUT_KN`, default `120` (seconds)\r\n  describes the time between knocking of the port (e.g. by the main menu ping) and the pausing of the process, when no client connects inbetween (read as timeout knocked)\r\n- `AUTOPAUSE_PERIOD`, default `10` (seconds)\r\n  describes period of the daemonized state machine, that handles the pausing of the process (resuming is done independently)\r\n- `AUTOPAUSE_KNOCK_INTERFACE`, default `eth0`\r\n  <br>Describes the interface passed to the `knockd` daemon. If the default interface does not work, run the `ifconfig` command inside the container and derive the interface receiving the incoming connection from its output. The passed interface must exist inside the container. Using the loopback interface (`lo`) does likely not yield the desired results.\r\n- `AUTOPAUSE_STATUS_RETRY_LIMIT`, default 10\r\n- `AUTOPAUSE_STATUS_RETRY_INTERVAL`, default 2s\r\n\r\n!!! tip\r\n\r\n    To troubleshoot, add `DEBUG_AUTOPAUSE=true` to see additional output\r\n\r\n## Rootless Auto-Pause\r\n\r\nIf you're running the container as rootless, then it is necessary to add the `CAP_NET_RAW` capability to the container, such as using [the `cap_add` service field](https://docs.docker.com/compose/compose-file/05-services/#cap_add) in a compose file or [`--cap-add` docker run argument](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities). It may also be necessary to set the environment variable `SKIP_SUDO` to \"true\". \r\n\r\nYou might need to set change the default port forwarder from RootlessKit to slirp4netns.\r\n\r\nFor Docker, see the following for setup:\r\n\r\n- https://docs.docker.com/engine/security/rootless/#networking-errors\r\n- https://rootlesscontaine.rs/getting-started/docker/#changing-the-port-forwarder\r\n\r\nFor Podman, see the following for setup:\r\n- https://rootlesscontaine.rs/getting-started/podman/#changing-the-port-forwarder\r\n\r\n\r\n!!! example \"Using docker run\"\r\n\r\n    -e AUTOPAUSE_KNOCK_INTERFACE=tap0 --cap-add=CAP_NET_RAW --network slirp4netns:port_handler=slirp4netns\r\n\r\n"
  },
  {
    "path": "docs/misc/autopause-autostop/autostop.md",
    "content": "# Auto-Stop\r\n\r\nAn option to stop the server after a specified time has been added for niche applications (e.g. billing saving on AWS Fargate). The function is incompatible with the Autopause functionality, as they basically cancel out each other.\r\n\r\n!!! note\r\n\r\n    the docker container variables have to be set accordingly (restart policy set to \"no\") and that the container has to be manually restarted.\r\n\r\nA `.skip-stop` file can be created in the `/data` directory to make the server skip autostopping, for as long as the file is present. The autostop timer will also be reset.\r\n\r\nA starting, example compose file has been provided in [examples/docker-compose-autostop.yml](https://github.com/itzg/docker-minecraft-server/blob/master/examples/docker-compose-autostop.yml).\r\n\r\nEnable the Autostop functionality by setting:\r\n\r\n```\r\n-e ENABLE_AUTOSTOP=TRUE\r\n```\r\n\r\nThe following environment variables define the behavior of auto-stopping:\r\n- `AUTOSTOP_TIMEOUT_EST`, default `3600` (seconds)\r\n  describes the time between the last client disconnect and the stopping of the server (read as timeout established)\r\n- `AUTOSTOP_TIMEOUT_INIT`, default `1800` (seconds)\r\n  describes the time between server start and the stopping of the server, when no client connects in-between (read as timeout initialized)\r\n- `AUTOSTOP_PERIOD`, default `10` (seconds)\r\n  describes period of the daemonized state machine, that handles the stopping of the server\r\n- `AUTOPAUSE_STATUS_RETRY_LIMIT`, default 10\r\n- `AUTOPAUSE_STATUS_RETRY_INTERVAL`, default 2s\r\n\r\n> To troubleshoot, add `DEBUG_AUTOSTOP=true` to see additional output\r\n\r\n## Proxy Support\r\nIf you make use of PROXY Protocol, i.e. through something like HAProxy or Fly.io, you will need to enable it in your variety of server's configuration, and then set the `USES_PROXY_PROTOCOL` envar to `true`. This lets Autostop monitor the server, where it otherwise wouldn't\r\n"
  },
  {
    "path": "docs/misc/autoscale/autoscale.md",
    "content": "\n# Autoscaling (sleep when idle)\n\nAutoscaling (sometimes called *sleeping*, *scale to zero* or *wake on join*) is the pattern of stopping a Minecraft server when nobody is playing and starting it again when someone tries to connect with the intention of saving resources when the server is not in use (e.g., on a VPS with limited CPU/RAM or a home server).\n\n## mc-router\n\n[mc-router](https://github.com/itzg/mc-router) is a Minecraft-aware router/multiplexer.\n\n- Routes players by the hostname they connect with (useful for multiple servers behind one port)\n- Can auto-start a backend container on join and stop it again after an idle timeout\n\nExamples:\n\n- See the mc-router section in [Examples](../examples.md#mc-router-with-auto-scale)\n\n## Lazymc\n\n[Lazymc](https://github.com/timvisee/lazymc) can keep a server “asleep” until a player connects. With Docker it’s commonly used via [lazymc-docker-proxy](https://github.com/joesturge/lazymc-docker-proxy).\n\n- Players connect to the proxy; the proxy starts/stops the server container\n- Usually requires a static IP for the Minecraft container on a user-defined network\n\nExample:\n\n- See the Lazymc section in [Examples](../examples.md#lazymc-put-your-minecraft-server-to-rest-when-idle)\n\n## Lazytainer\n\n[Lazytainer](https://github.com/vmorganp/Lazytainer) starts/stops containers based on network traffic.\n\n- Uses packet thresholds + inactivity timeouts (not Minecraft hostname aware)\n- Can be triggered by port scans/pings in noisy environments\n\nExample:\n\n- See the Lazytainer section in [Examples](../examples.md#lazytainer-stop-minecraft-container-based-on-traffic)\n\n\n"
  },
  {
    "path": "docs/misc/building.md",
    "content": "\n## Simple image additions\n\nYou can easily build upon the base image using an inline Dockerfile.\n\n```yaml title=\"compose.yaml\"\nservices:\n  mc:\n    build:\n      context: .\n      dockerfile_inline: |\n        FROM itzg/minecraft-server:latest\n\n        RUN apt-get update && apt-get install -y \\\n            webp \\\n            && rm -rf /var/lib/apt/lists/*\n      pull: true # Always pull new base image\n    pull_policy: build\n    restart: unless-stopped\n    environment:\n      EULA: true\n    ports:\n      - \"25565:25565/tcp\"\n    volumes:\n      - ./data:/data\n```\n\nHere is an example to add Nvidia GPU support for C2ME:\n\n??? Example \"C2ME GPU example\"\n    ```yaml title=\"compose.yaml\"\n    \n    services:\n      mc:\n        build:\n          context: .\n          dockerfile_inline: |\n            FROM itzg/minecraft-server:java25\n    \n            # Install OpenCL loader and NVIDIA driver capabilities\n            RUN apt-get update && apt-get install -y \\\n                ocl-icd-libopencl1 \\\n                opencl-headers \\\n                clinfo \\\n                && rm -rf /var/lib/apt/lists/*\n    \n            # 1. Create the vendor directory\n            # 2. Tell OpenCL to use the NVIDIA library\n            RUN mkdir -p /etc/OpenCL/vendors && \\\n                echo \"libnvidia-opencl.so.1\" > /etc/OpenCL/vendors/nvidia.icd\n    \n            # Tell the NVIDIA container runtime to expose all GPU capabilities (including compute/utility)\n            ENV NVIDIA_VISIBLE_DEVICES all\n            ENV NVIDIA_DRIVER_CAPABILITIES compute,utility,graphics,video\n    \n            COPY ./mods /mods\n          pull: true # Always pull new base image\n        pull_policy: build\n        restart: unless-stopped\n        deploy:\n          resources:\n            reservations:\n              devices:\n                - driver: nvidia\n                  count: 1\n                  capabilities: [gpu]\n        environment:\n          EULA: true\n          TYPE: \"FABRIC\"\n          VERSION: 1.21.10\n          MEMORY: 8G\n          MODRINTH_PROJECTS: |-\n            fabric-api\n            c2me\n        ports:\n          - \"25565:25565/tcp\"\n        volumes:\n          - ./data:/data\n    ```\n\n\n!!! tip \"For advanced use only\"\n\n    This page describes a capability that is not applicable to most users. It is only intended for rare cases when a very specific Java base image is needed or additional packages need to be installed that are not generally applicable or would bloat the image size.\n\n    Be sure to confirm that the desired [version and variant of Java isn't already provided](../versions/java.md).\n\n## Building image locally with alternate Java base\n\nAn alternate Java base image can be specified by setting the [docker build argument](https://docs.docker.com/reference/cli/docker/buildx/build/#build-arg) `BASE_IMAGE` to the desired base image. The following shows an example of using the base image `ghcr.io/graalvm/graalvm-ce:ol8-java11`:\n\n```shell\ndocker build --build-arg BASE_IMAGE=ghcr.io/graalvm/graalvm-ce:ol8-java11 -t IMG_PREFIX/minecraft-server:java11-graalvm .\n```\n\n## Building a multi-architecture image\n\nEnsure buildx/BuildKit support is enabled and run the following to build multi-architecture and push to the repository as named by the image:\n\n```shell\ndocker buildx build --platform=linux/arm64 --platform=linux/arm/v7 --platform=linux/amd64 --tag IMG_PREFIX/minecraft-server --push .\n```\n\nTo build for local images, multi-architecture is not supported, use the following with buildx to load the image into the local daemon:\n\n```shell\ndocker buildx build --tag IMG_PREFIX/minecraft-server --load .\n```\n\nor just a plain build\n\n```shell\ndocker build -t IMG_PREFIX/minecraft-server .\n```\n\n## Installing extra packages\n\nThe following build args can be set to install additional packages for the respective base image distro:\n\n- `EXTRA_DEB_PACKAGES`\n- `EXTRA_DNF_PACKAGES`\n- `EXTRA_ALPINE_PACKAGES`\n"
  },
  {
    "path": "docs/misc/contributing/development.md",
    "content": "## Adding a server type\n\nAdding a new server `TYPE` can vary due to the complexity of obtaining and configuring each type; however, the addition of any server type includes at least the following steps:\n\n1. Copy an existing \"start-deploy*\" script, such as [start-deployFabric](https://github.com/itzg/docker-minecraft-server/blob/master/scripts/start-deployFabric) and rename it accordingly making sure to retain the \"start-deploy\" prefix\n2. Modify the type-specific behavior between the \"start-utils\" preamble and the hand-off to `start-setupWorld` at the end of the script \n3. Develop and test the changes using the [iterative process described below](#iterative-script-development)\n4. Add a case-entry to the `case \"${TYPE^^}\"` in [start-configuration](https://github.com/itzg/docker-minecraft-server/blob/master/scripts/start-configuration)\n5. Add a section to the [docs](https://github.com/itzg/docker-minecraft-server/tree/master/docs). It is recommended to copy-modify an existing section to retain a similar wording and level of detail\n6. [Submit a pull request](https://github.com/itzg/docker-minecraft-server/pulls)\n\n## Iterative script development\n\nThe included `compose-dev.yml` will mount the local `scripts` code into the container and allow for iterative development. Replace `[-e key=value]` with any environment variables you wish to set for testing the modified scripts.\n\n```shell\ndocker compose -f compose-dev.yml run --rm -it [-e key=value]  mc-dev\n```\n\n!!! tip\n\n    To speed up the development cycle, it is recommended to set `SETUP_ONLY` to `true` as part of the run command above.\n\n## Using development copy of tools\n\nIn the cloned repo, such as [`mc-image-helper`](https://github.com/itzg/mc-image-helper), install the distribution locally by running:\n\n```shell\n./gradlew installDist\n```\n\nThe distribution will be installed in the project's `build/install/mc-image-helper`. Obtain the absolute path to that directory use in the next step.\n\nRefer to the instructions above to mount any locally modified image scripts or build a local copy of the image using or with alternate `BASE_IMAGE`, as described above:\n\n```shell\ndocker build -t itzg/minecraft-server .\n```\n\nMount the local mc-image-helper distribution directory as a volume in the container at the path `/usr/share/mc-image-helper`, such as\n\n```shell\ndocker run -it --rm \\\n  -v /path/to/mc-image-helper/build/install/mc-image-helper:/usr/share/mc-image-helper \\\n  -e EULA=true \\\n  itzg/minecraft-server\n```\n\nIn a compose file, include the volume mount in the `volumes` section of the container definition:\n\n```yaml\nservices:\n  mc:\n    # ... usual container definition\n    volumes:\n      - /path/to/mc-image-helper/build/install/mc-image-helper:/usr/share/mc-image-helper:ro\n```\n\nFor Go base tools, run\n\n```shell\ngoreleaser release --snapshot --clean\n```\n\nClone [itzg/github-releases-proxy](https://github.com/itzg/github-releases-proxy) and run it according to the instructions shown there.\n\nIn the Docker build, configure the following \n\n```shell\n--build-arg GITHUB_BASEURL=http://host.docker.internal:8080 \\\n--build-arg APPS_REV=1\n```\n\nand declare one or more version overrides, such as\n\n```\n--build-arg MC_HELPER_VERSION=1.8.1-SNAPSHOT\n```\n\n## Generating release notes\n\nThe following git command can be used to provide the bulk of release notes content:\n\n```shell script\ngit log --invert-grep --grep \"^ci:\" --grep \"^misc:\" --grep \"^docs:\" --pretty=\"* %s\" 1.1.0..1.2.0\n```\n"
  },
  {
    "path": "docs/misc/contributing/docs.md",
    "content": "# Site documentation\r\n\r\nThe documentation for this image/repository is written in markdown and built using [Zensical](https://zensical.org/) into a documentation website hosted at [Read the Docs](https://readthedocs.org/). [Here is general information about writing Zensical markdown](https://zensical.org/docs/authoring/markdown/).\r\n\r\n!!! note\r\n    The README.md rarely needs to be modified and only serves as a brief introduction to the project.\r\n\r\nThe documentation source is maintained in the [docs](https://github.com/itzg/docker-minecraft-server/tree/master/docs) folder and is organized into sections by directory and files. Look through the existing content to determine if an existing file should be updated or a new file/directory added.\r\n\r\nIt will be very helpful to view the rendered documentation as you're editing. To do that run the following from the top-level directory:\r\n\r\n```shell\r\ndocker compose -f compose-docs.yml -p zensical up\r\n```\r\n\r\nYou can access the live documentation rendering at <http://localhost:8000>.\r\n"
  },
  {
    "path": "docs/misc/deployment/index.md",
    "content": "# More Deployment Info \n\n## On Kubernetes\n\n### Using Helm\n\n- itzg Helm Chart:\n    - [GitHub repo](https://github.com/itzg/minecraft-server-charts)\n      - [Helm Chart repo](https://itzg.github.io/minecraft-server-charts/)\n- [mcsh/server-deployment](https://github.com/mcserverhosting-net/charts)\n\n### Using Shulker\n\n[Shulker](https://github.com/jeremylvln/Shulker) is a Kubernetes operator for managing complex and dynamic Minecraft infrastructures, including game servers and proxies. It uses the docker-minecraft-server and docker-bungeecord images under-the-hood.\n\n## With Ansible\n\n[Ansible](https://docs.ansible.com/ansible/latest/getting_started/introduction.html) is an open-source task automation tool built in [Python](https://www.python.org/). Ansible playbooks can be used to automate all kinds of tasks, including deploying remote Minecraft servers.\n\n### Using the MASH playbook\n\n[The MASH Playbook](https://github.com/mother-of-all-self-hosting/mash-playbook) is a premade playbook with the option to deploy a [wide variety of open-source services](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/supported-services.md) to your server(s), including [docker-minecraft-server](https://github.com/XHawk87/ansible-role-minecraft), making it a good option if you want all the bells and whistles alongside your Minecraft server.\n- Check out the [Installation Guide](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/README.md) for the MASH playbook to get started.\n- You can then enable [Minecraft](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/minecraft.md) in your vars.yml.\n- Enable any supporting services that you might find useful, such as [user authentication](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/authelia.md), [remote backups](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/backup-borg.md), [email relay](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/exim-relay.md), [cron monitoring](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/healthchecks.md), [audio and video conferencing](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/jitsi.md), databases ([MariaDB](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/mariadb.md), [PostgresSQL](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/postgres.md)), [push notifications](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/ntfy.md), [uptime monitoring](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/uptime-kuma.md), [a website](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/wordpress.md), as well as installing any [extra files, folders, applications, services and running commands](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/auxiliary.md) automatically on deployment.\n\n## On CloudFormation (AWS)\n\nIf you're looking for a simple way to deploy this to the Amazon Web Services Cloud, check out the [Minecraft Server Deployment (CloudFormation) repository](https://github.com/vatertime/minecraft-spot-pricing). This repository contains a CloudFormation template that will get you up and running in AWS in a matter of minutes. Optionally it uses Spot Pricing so the server is very cheap, and you can easily turn it off when not in use.\n\n## Supporting Articles\n\nBelow are supporting articles for server deployment.\n\n- \"Zero to Minecraft Server with Docker Desktop and Compose\"\n\n    https://dev.to/rela-v/zero-to-minecraft-server-with-docker-desktop-and-compose-500a\n\n    - This is a reference guide/tutorial on how to set up a vanilla Minecraft server using this project, including step-by-step instructions, and information on topics such as port-forwarding.\n"
  },
  {
    "path": "docs/misc/examples.md",
    "content": "# Examples\r\n\r\nVarious examples are [maintained in the repository](https://github.com/itzg/docker-minecraft-server/tree/master/examples). The sections below highlight a few particular ones.\r\n\r\n## Bedrock compatible server\r\n\r\nUsing the [GeyserMC plugin](https://geysermc.org/) with a Paper server (or similar) \"enables clients from Minecraft Bedrock Edition to join your Minecraft Java server\". The example also includes [Floodgate](https://wiki.geysermc.org/floodgate/) which \"allows Xbox Live authenticated Bedrock users to join without a Java Edition account\". \r\n\r\n```yaml title=\"compose.yaml\"\r\n\r\nservices:\r\n  mc:\r\n    image: itzg/minecraft-server:latest\r\n    pull_policy: daily\r\n    environment:\r\n      EULA: \"true\"\r\n      TYPE: \"PAPER\"\r\n      PLUGINS: |\r\n        https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot\r\n        https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot\r\n    ports:\r\n      - \"25565:25565\"\r\n      - \"19132:19132/udp\"\r\n    volumes:\r\n      - ./data:/data\r\n```\r\n\r\n[Source](https://github.com/itzg/docker-minecraft-server/blob/master/examples/geyser/docker-compose.yml)\r\n\r\n\r\n## mc-router with auto-scale\r\n\r\nUsing [mc-router](https://github.com/itzg/mc-router) in front of one or multiple Minecraft server containers allows you to route players based on the hostname they use to connect.\r\n\r\nWith `AUTO_SCALE_UP` and `AUTO_SCALE_DOWN` enabled, mc-router can automatically start a target server when a player connects and stop it again after a period of inactivity.\r\n\r\n```yaml title=\"compose.yaml\"\r\nservices:\r\n  router:\r\n    image: itzg/mc-router\r\n    environment:\r\n      IN_DOCKER: true\r\n      AUTO_SCALE_DOWN: true\r\n      AUTO_SCALE_UP: true\r\n      AUTO_SCALE_DOWN_AFTER: 2h\r\n      AUTO_SCALE_ASLEEP_MOTD: \"Server is asleep. Join again to wake it up!\"\r\n    ports:\r\n      - \"25565:25565\"\r\n    volumes:\r\n      - /var/run/docker.sock:/var/run/docker.sock:ro\r\n\r\n  vanilla:\r\n    image: itzg/minecraft-server\r\n    environment:\r\n      EULA: \"TRUE\"\r\n    labels:\r\n      mc-router.host: \"vanilla.example.com\"\r\n\r\n  paper:\r\n    image: itzg/minecraft-server\r\n    environment:\r\n      EULA: \"TRUE\"\r\n      TYPE: PAPER\r\n    labels:\r\n      mc-router.host: \"paper.example.com\"\r\n```\r\n\r\n[Source](https://github.com/itzg/mc-router/blob/main/examples/docker-autoscale/compose-minimal.yml)\r\n\r\n[More detailed example](https://github.com/itzg/mc-router/blob/main/examples/docker-autoscale/compose.yml)\r\n\r\n## Lazymc - Put your Minecraft server to rest when idle\r\n\r\nWith [lazymc-docker-proxy](https://github.com/joesturge/lazymc-docker-proxy) you are able to use [lazymc](https://github.com/timvisee/lazymc) with the minecraft container.\r\n\r\n```yaml title=\"compose.yaml\"\r\n# Lazymc requires that the minecraft server have a static IP.\r\n#\r\n# To ensure that our servers have a static IP we need to create\r\n# a network for our services to use.\r\n#\r\n# By default, Docker uses 172.17.0.0/16 subnet range.\r\n# So we need to create a new network in a different subnet\r\n# See the readme for more information.\r\n#\r\n# Please ensure that the subnet falls within the private CIDRs:\r\n# https://datatracker.ietf.org/doc/html/rfc1918#section-3\r\n#\r\n# And that it is not in use by anything else.\r\nnetworks:\r\n  minecraft-network:\r\n    driver: bridge    \r\n    ipam:\r\n      config:\r\n        - subnet: 172.18.0.0/16\r\n\r\nservices:\r\n  lazymc:\r\n    image: ghcr.io/joesturge/lazymc-docker-proxy:latest\r\n    # the IPs should start at .2 as .1 is reserved for the gateway\r\n    networks:\r\n      minecraft-network:\r\n        ipv4_address: 172.18.0.2\r\n    restart: unless-stopped\r\n    volumes:\r\n      # you should mount the minecraft server dir under /server, using read only.\r\n      - data:/server:ro\r\n      # you need to supply the docker socket, so that the container can run docker command\r\n      - /var/run/docker.sock:/var/run/docker.sock:ro\r\n    ports:\r\n      # lazymc-docker-proxy acts as a proxy, so there is\r\n      # no need to expose the server port on the Minecraft container\r\n      - \"25565:25565\"\r\n\r\n  # Standard Docker Minecraft server, also works with other server types\r\n  mc:\r\n    image: itzg/minecraft-server:java21\r\n    pull_policy: daily\r\n    # Assign a static IP to the server container\r\n    networks:\r\n      minecraft-network:\r\n        ipv4_address: 172.18.0.3\r\n    # We need to add a label here so that lazymc-docker-proxy knows which\r\n    # container to manage\r\n    labels:\r\n      # Set lazymc.enabled to true to enable lazymc on this container\r\n      - lazymc.enabled=true\r\n      # Required to find the container to manage it\r\n      - lazymc.group=mc\r\n      # Point to the service name of the Minecraft server\r\n      - lazymc.server.address=mc:25565\r\n    tty: true\r\n    stdin_open: true\r\n    # This container should be managed solely by the lazymc container\r\n    # so set restart to no, or else the container will start again...\r\n    restart: no\r\n    environment:\r\n      EULA: \"TRUE\"\r\n    volumes:\r\n      - data:/data\r\n\r\nvolumes:\r\n  data:\r\n```\r\n[Source](https://github.com/joesturge/lazymc-docker-proxy/blob/master/docker-compose.yaml)\r\n\r\n## Lazytainer - Stop Minecraft container based on traffic\r\nMonitors network traffic to the Minecraft containers. If there is traffic, the container runs, otherwise the container is stopped/paused.\r\n\r\nBy using [Lazytainer](https://github.com/vmorganp/Lazytainer) with the [docker-minecraft-server](https://github.com/itzg/docker-minecraft-server) a somehow similar behaviour to [Lazymc](https://github.com/timvisee/lazymc) can be archived.\r\n\r\n```yaml title=\"compose.yaml\"\r\nservices:\r\n  lazytainer:\r\n    image: ghcr.io/vmorganp/lazytainer:master\r\n    environment:\r\n      VERBOSE: false\r\n    ports:\r\n      - 25565:25565\r\n    volumes:\r\n      - /var/run/docker.sock:/var/run/docker.sock:ro\r\n    labels:\r\n      - lazytainer.group.minecraft.sleepMethod=stop\r\n      - lazytainer.group.minecraft.ports=25565\r\n      - lazytainer.group.minecraft.minPacketThreshold=2 # Start after two incomming packets\r\n      - lazytainer.group.minecraft.inactiveTimeout=600 # 10 minutes, to allow the server to bootstrap. You can probably make this lower later if you want.\r\n    restart: unless-stopped\r\n    network_mode: bridge\r\n  mc:\r\n    image: itzg/minecraft-server:latest\r\n    pull_policy: daily\r\n    environment:\r\n      EULA: TRUE\r\n      TYPE: PAPER\r\n      MEMORY: 4G\r\n    volumes:\r\n      - ./data:/data\r\n    labels:\r\n      - lazytainer.group=minecraft\r\n    depends_on:\r\n      - lazytainer\r\n    network_mode: service:lazytainer\r\n    tty: true\r\n    stdin_open: true\r\n    restart: unless-stopped\r\n```\r\n[Source](https://github.com/itzg/docker-minecraft-server/blob/master/examples/lazytainer/docker-compose.yml)\r\n"
  },
  {
    "path": "docs/misc/healthcheck.md",
    "content": "## Healthcheck\r\n\r\nThis image contains [mc-monitor](https://github.com/itzg/mc-monitor) and uses its `status` command to continually check on the container's. That can be observed from the `STATUS` column of `docker ps`\r\n\r\n```\r\nCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                    PORTS                                 NAMES\r\nb418af073764        mc                  \"/start\"            43 seconds ago      Up 41 seconds (healthy)   0.0.0.0:25565->25565/tcp, 25575/tcp   mc\r\n```\r\n\r\nYou can also query the container's health in a script-friendly way:\r\n\r\n``` shell\r\n> docker container inspect -f \"{{.State.Health.Status}}\" mc\r\nhealthy\r\n```\r\n\r\nThere's actually a wrapper script called `mc-health` that takes care of calling `mc-monitor status` with the correct arguments. If needing to customize the health checks parameters, such as in a Compose file, then use something like the following in the service declaration:\r\n\r\n``` yaml\r\nhealthcheck:\r\n  test: mc-health\r\n  start_period: 1m\r\n  interval: 5s\r\n  retries: 20\r\n```\r\n\r\nSome orchestration systems, such as Portainer, don't allow for disabling the default `HEALTHCHECK` declared by this image. In those cases you can approximate the disabling of health checks by setting the environment variable `DISABLE_HEALTHCHECK` to `true`.\r\n\r\nThe [health check in a Compose service declaration](https://docs.docker.com/reference/compose-file/services/#healthcheck) can also be disabled using:\r\n\r\n```yaml\r\n    healthcheck:\r\n      disable: true\r\n      test: [\"NONE\"]\r\n```\r\n\r\n### Health checks for older versions\r\n\r\nThis container disables health checks for Minecraft versions before b1.8 as those versions do not support any kind of server pinging. For more information see [Server List Ping](https://minecraft.wiki/w/Java_Edition_protocol/Server_List_Ping#Beta_1.8_to_1.3).\r\n"
  },
  {
    "path": "docs/misc/log4j.md",
    "content": "**Please ensure you have pulled the latest image** since [all official mitigations](https://www.minecraft.net/en-us/article/important-message--security-vulnerability-java-edition) are automatically applied by the container startup process.\r\n"
  },
  {
    "path": "docs/misc/raspberrypi.md",
    "content": "# Running on RaspberryPi\r\n\r\nTo run this image on a RaspberryPi 3 B+, 4, or newer, use any of the image tags [list in the Java version section](../versions/java.md) that specify `armv7` for the architecture, which includes `itzg/minecraft-server:latest`.\r\n\r\n!!! note\r\n   \r\n   You may need to lower the memory allocation, such as `-e MEMORY=750m`\r\n\r\n!!! note\r\n\r\n    If experiencing issues such as \"sleep: cannot read realtime clock: Operation not permitted\", ensure `libseccomp` is up to date on your host. In some cases adding `:Z` flag to the `/data` mount may be needed, [but use cautiously](https://docs.docker.com/storage/bind-mounts/#configure-the-selinux-label).\r\n"
  },
  {
    "path": "docs/misc/related-projects.md",
    "content": "### [itzg/minecraft-bedrock-server](https://github.com/itzg/docker-minecraft-bedrock-server)\r\n\r\nDocker image that runs a Minecraft Bedrock server.\r\n\r\n### [mc-router](https://github.com/itzg/mc-router)\r\n\r\nLightweight multiplexer/proxy for Minecraft Java servers. Provided as a stand-alone application and a Docker image.\r\n\r\n### [itzg/mc-proxy](https://github.com/itzg/docker-bungeecord/)\r\n\r\nDocker image that runs a proxy powered by Bungeecord, Velocity, or Waterfall\r\n\r\n### [itzg/mc-backup](https://github.com/itzg/docker-mc-backup)\r\n\r\nDocker image that runs as a side-car container to backup world data.\r\n\r\n### [rcon-cli](https://github.com/itzg/rcon-cli)\r\n\r\nA tool that is bundled with this image to provide CLI access to an RCON endpoint.\r\n\r\n### [mc-monitor](https://github.com/itzg/mc-monitor)\r\n\r\nA tool that is bundled with this image that provides health checks and metrics reporting, such as a Prometheus exporter or a telegraf data source.\r\n\r\n### [mc-image-helper](https://github.com/itzg/mc-image-helper)\r\n\r\nA tool that is bundled with this image to provide complex, re-usable preparation operations. \r\n\r\n### [itzg/rcon](https://github.com/itzg/docker-rcon-web-admin)\r\n\r\nAn image that dockerizes [rcon-web-admin](https://github.com/rcon-web-admin/rcon-web-admin).\r\n"
  },
  {
    "path": "docs/misc/troubleshooting.md",
    "content": "To troubleshoot the container initialization, such as when server files are pre-downloaded, set the environment variable `DEBUG` to `true`. The container logs will include **much more** output, and it is highly recommended including that output when reporting any [issues](https://github.com/itzg/docker-minecraft-server/issues).\r\n\r\nTo troubleshoot just the command-line used to start the Minecraft server, set the environment variable `DEBUG_EXEC` to `true`.\r\n\r\nTo troubleshoot any issues with memory allocation reported by the JVM, set the environment variable `DEBUG_MEMORY` to `true`.\r\n\r\nIf you are experiencing any issues with the \"Changing ownership of /data\" step, that can be disabled by setting `SKIP_CHOWN_DATA` to `true`.\r\n\r\nTo confirm the image version that has been pulled, use the following command, replacing `itzg/minecraft-server` as needed for specific image tags:\r\n\r\n## Image labels\r\n\r\n```shell\r\ndocker image inspect itzg/minecraft-server -f \"{{json .Config.Labels}}\"\r\n```\r\n\r\nsuch as\r\n\r\n```json\r\n{\r\n  \"org.opencontainers.image.authors\": \"... <...@gmail.com>\",\r\n  \"org.opencontainers.image.created\": \"2025-04-03T02:15:51.405Z\",\r\n  \"org.opencontainers.image.description\": \"Docker image that provides a Minecraft Server for Java Edition that automatically downloads selected version at startup\",\r\n  \"org.opencontainers.image.licenses\": \"Apache-2.0\",\r\n  \"org.opencontainers.image.ref.name\": \"ubuntu\",\r\n  \"org.opencontainers.image.revision\": \"d6897a649ecbc16b5fb2e1500e24b64ef80270a0\",\r\n  \"org.opencontainers.image.source\": \"https://github.com/itzg/docker-minecraft-server\",\r\n  \"org.opencontainers.image.title\": \"docker-minecraft-server\",\r\n  \"org.opencontainers.image.url\": \"https://github.com/itzg/docker-minecraft-server\",\r\n  \"org.opencontainers.image.version\": \"java21\"\r\n}\r\n```\r\n\r\nThe labels that are most interesting are:\r\n\r\n- `org.opencontainers.image.created` : the date/time the image was built\r\n- `org.opencontainers.image.revision` : which maps to <https://github.com/itzg/docker-minecraft-server/commit/REVISION>\r\n- `org.opencontainers.image.version` : image tag and variant [as described in this page](../versions/java.md)"
  },
  {
    "path": "docs/misc/world-data.md",
    "content": "\r\n## Downloadable world\r\n\r\nInstead of mounting the `/data` volume, you can instead specify the URL of a ZIP or compressed TAR file containing an archived world. It will be searched for a file `level.dat` and the containing subdirectory moved to the directory named by `$LEVEL`. This means that most of the archived Minecraft worlds downloadable from the Internet will already be in the correct format.\r\n\r\n    docker run -d -e WORLD=http://www.example.com/worlds/MySave.zip ...\r\n\r\n!!! note\r\n\r\n    This URL must be accessible from inside the container. Therefore, you should use an IP address or a globally resolvable FQDN, or else the name of a linked container.\r\n\r\n!!! note\r\n\r\n    If the archive contains more than one `level.dat`, then the one to select can be picked with `WORLD_INDEX`, which defaults to 1.\r\n\r\n## Cloning world from a container path\r\n\r\nThe `WORLD` option can also be used to reference a directory, zip file, or compressed tar file that will be used as a source to clone or extract the world directory.\r\n\r\nFor example, the following would initially clone the world's content from `/worlds/basic`. Also notice in the example that you should use a read-only volume attachment to ensure the clone source remains pristine.\r\n\r\n```\r\ndocker run ... -v $HOME/worlds:/worlds:ro -e WORLD=/worlds/basic\r\n```\r\n\r\nThe following diagram shows how this option can be used in a compose deployment with a relative directory:\r\n\r\n![](../img/world-copy-compose-project.drawio.png)\r\n\r\n## Overwrite world on start\r\nThe world will only be downloaded or copied if it doesn't exist already. Set `FORCE_WORLD_COPY=TRUE` to force overwrite the world on every server start.\r\n\r\n## Custom worlds directory path\r\nTo set a custom worlds directory for the Multiverse plugin on a baremetal server, you'd pass the `--world-dir` argument after the jar file.\r\nIn order to accomplish the same in a containerized server, set the `EXTRA_ARGS` environment variable in your command line or docker compose yaml to the same argument string. For example:\r\n\r\n```\r\ndocker run -d -e EXTRA_ARGS='--world-dir ./worlds/'\r\n```\r\n`--world-container`, `-W`, and `--universe` are aliases to `--world-dir` and can also be used.\r\n\r\n## Datapacks\r\n\r\nDatapacks can be installed in a similar manner to mods/plugins. There are many environment variables which function in the same way they do for [mods](../mods-and-plugins/index.md):\r\n\r\n* `DATAPACKS`: comma-separated list of zip file URL, zip file in container, or directory in container\r\n* `DATAPACKS_FILE`: a text file within the container where each line is a zip file URL, zip file in container, or directory in container\r\n* `REMOVE_OLD_DATAPACKS`: if \"true\" the datapacks directory is removed of everything matching `REMOVE_OLD_DATAPACKS_INCLUDE`, but excluding `REMOVE_OLD_DATAPACKS_EXCLUDE` no deeper than `REMOVE_OLD_DATAPACKS_DEPTH`\r\n* `REMOVE_OLD_DATAPACKS_DEPTH`: default is 16\r\n* `REMOVE_OLD_DATAPACKS_INCLUDE`: default is `*.zip`\r\n* `REMOVE_OLD_DATAPACKS_EXCLUDE`: default is empty\r\n\r\nDatapacks will be placed in `/data/$LEVEL/datapacks`\r\n\r\n## VanillaTweaks\r\n\r\n[VanillaTweaks](https://vanillatweaks.net/) datapacks, crafting tweaks, and resource packs can be installed with a share code from the website **OR** a json file to specify packs to download and install. Datapacks and crafting tweaks will be installed into the current world directory specified by `$LEVEL`. As new versions of the packs are retrieved the previous versions will automatically be cleaned up.\r\n\r\nThe share code is the part following the hash sign, as shown here:\r\n\r\n```\r\nhttps://vanillatweaks.net/share/#MGr52E\r\n                                 ------\r\n                                  |\r\n                                  +- share code MGr52E\r\n```\r\n\r\nAccepted Parameters:\r\n\r\n- `VANILLATWEAKS_FILE`: comma separated list of JSON VanillaTweak pack files accessible within the container\r\n- `VANILLATWEAKS_SHARECODE`: comma separated list of share codes\r\n\r\nExample of expected VanillaTweaks share codes:\r\n\r\n!!! note\r\n\r\n    ResourcePacks, DataPacks, and CraftingTweaks all have separate sharecodes\r\n\r\n``` yaml\r\nVANILLATWEAKS_SHARECODE: MGr52E,tF1zL2,LnEDwT\r\n```\r\n\r\n!!! note\r\n\r\n    Datapack names are all lower case. [See their spec](https://vanillatweaks.net/assets/resources/json/1.21/dpcategories.json) for a full list of 1.21 datapacks, and [their spec](https://vanillatweaks.net/assets/resources/json/1.21/ctcategories.json) for a full list of 1.21 crafting tweaks.\r\n\r\nExample of expected VanillaTweaks files:\r\n\r\n``` yaml\r\nVANILLATWEAKS_FILE: /config/vt-datapacks.json,/config/vt-craftingtweaks.json,/config/vt-resourcepacks.json\r\n```\r\n\r\n``` json title=\"DataPacks json\"\r\n{\r\n  \"type\": \"datapacks\",\r\n  \"version\": \"1.21\",\r\n  \"packs\": {\r\n    \"gameplay changes\": [\r\n      \"graves\",\r\n      \"multiplayer sleep\",\r\n      \"armored elytra\"\r\n    ],\r\n    \"teleport commands\": [\"tpa\"]\r\n  }\r\n}\r\n```\r\n\r\n``` json title=\"ResourcePacks json\"\r\n{\r\n    \"type\": \"resourcepacks\",\r\n    \"version\": \"1.21\",\r\n    \"packs\": {\r\n        \"aesthetic\": [\"CherryPicking\", \"BlackNetherBricks\", \"AlternateBlockDestruction\"]\r\n    }\r\n}\r\n```\r\n\r\n\r\n``` json title=\"CraftingTweaks Json\"\r\n{\r\n    \"type\": \"craftingtweaks\",\r\n    \"version\": \"1.21\",\r\n    \"packs\": {\r\n        \"quality of life\": [\r\n            \"dropper to dispenser\",\r\n            \"double slabs\",\r\n            \"back to blocks\"\r\n        ]\r\n    }\r\n}\r\n```\r\n"
  },
  {
    "path": "docs/mods-and-plugins/curseforge-files.md",
    "content": "# Auto-download from CurseForge\r\n\r\nMods and plugins can be auto-downloaded and upgraded from CurseForge by setting `CURSEFORGE_FILES` to a comma or space delimited list of [project-file references](#project-file-references). References removed from the declaration are automatically cleaned up and setting `CURSEFORGE_FILES` to an empty string removes all previously managed project-files.\r\n\r\nA specific file can be omitted from each reference to allow for auto-selecting the newest version of the selected mod/plugin. The resolved `VERSION` and `TYPE` will be taken into consideration for selecting the appropriate file. \r\n\r\n!!! warning \"CurseForge API key usage\"\r\n\r\n    A CurseForge API key must be allocated and set with `CF_API_KEY` (or `CF_API_KEY_FILE`) [as described here](../types-and-platforms/mod-platforms/auto-curseforge.md#api-key).\r\n\r\n## Project-file references\r\n\r\n!!! tip\r\n\r\n    Individual project files typically represent a version of the mod/plugin, but CurseForge refers to these items broadly as \"files\" rather than \"versions\". \r\n\r\nThe following formats are supported in the list of project-file references:\r\n\r\n- A project page URL, such as `https://www.curseforge.com/minecraft/mc-mods/jei`. _The newest applicable file will be automatically selected._\r\n- A file page URL, such as `https://www.curseforge.com/minecraft/mc-mods/jei/files/4593548`\r\n- Project slug, such as `jei`. _The newest applicable file will be automatically selected._\r\n- Project ID, such as `238222`. _The newest applicable file will be automatically selected._\r\n- Project slug or ID, `:`, and a file ID, such as `jei:4593548` or `238222:4593548`\r\n- Project slug or ID, `@`, and a partial filename, such as `jei@10.2.1.1005`. This option is useful to refer to a version of the mod/plugin rather than looking up the file ID.\r\n- An `@` followed by the **container path** to a listing file\r\n\r\n!!! info \"More about listing files\"\r\n\r\n    Each line in the listing file is processed as one of the references above; however, blank lines and comments that start with `#` are ignored.\r\n    \r\n    Make sure to place the listing file in a mounted directory/volume or declare an appropriate mount for it.\r\n    \r\n    For example, `CURSEFORGE_FILES` can be set to \"@/extras/cf-mods.txt\", assuming \"/extras\" has been added to `volumes` section, where the container file `/extras/cf-mods.txt` contains\r\n    \r\n    ```text\r\n    # This comment is ignored\r\n    jei:10.2.1.1005\r\n    \r\n    # This and previous blank line are ignore\r\n    geckolib\r\n    aquaculture\r\n    naturalist\r\n    ```\r\n\r\n!!! tip \"Multi-line values in Docker Compose\"\r\n\r\n    Making use of the space delimited option, compose file declarations can be organized nicely with a [multi-line string](https://yaml-multiline.info/), such as\r\n    \r\n    ```yaml\r\n          CURSEFORGE_FILES: |\r\n            geckolib\r\n            aquaculture\r\n            naturalist\r\n    ```\r\n\r\n## Dependencies\r\n\r\nThe files processing can detect if a dependency is missing from the given list, but is not able to resolve the dependencies otherwise since their metadata only gives the mod ID and not the specific file version/ID that is needed.\r\n"
  },
  {
    "path": "docs/mods-and-plugins/index.md",
    "content": "# Working with mods and plugins\n\n## Modpack platforms\n\nBy far the easiest way to work with mod and plugins, especially large numbers of them, is to utilize modpacks with [one of the supported modpack platforms](../types-and-platforms/index.md).\n\nThe following are some supported modpack platforms:\n\n- [Modrinth](../types-and-platforms/mod-platforms/modrinth-modpacks.md) \n- [CurseForge](../types-and-platforms/mod-platforms/auto-curseforge.md)\n- [Feed the Beast](../types-and-platforms/mod-platforms/ftb.md)\n\n## Download automation\n\nOn the left, there are sections describing some download automation options.\n\n## Mods vs Plugins\n\nThe terms \"mods\" and \"plugins\" can be quite confusing. Generally, the rule of thumb is that \"mods\" are used by the types that run client side to modify rendering, add new blocks, and add behaviors server, such as [Forge](../types-and-platforms/server-types/forge.md) and [Fabric](../types-and-platforms/server-types/fabric.md). \"Plugins\" are used by the types that **only run on servers** to add behaviors, commands, etc such as [Paper](../types-and-platforms/server-types/paper.md) (which derives from [Bukkit/Spigot](../types-and-platforms/server-types/bukkit-spigot.md)). There are also some types that are [hybrids](../types-and-platforms/server-types/hybrids.md), such as Magma, that use both \"mods\" and \"plugins\".\n\nTypically, mods needs to be installed in both the client and server; however, there are some cases when only the server needs a mod. Plugins only need to be installed in the server and are never needed in the client.\n\n## Optional plugins, mods, and config attach points\n\nThere are optional volume paths that can be attached to supply content to be copied into the data area:\n\n`/plugins`\n: content in this directory is synchronized into `/data/plugins` for server types that use plugins, [as described above](#mods-vs-plugins). For special cases, the source can be changed by setting `COPY_PLUGINS_SRC` and destination by setting `COPY_PLUGINS_DEST`. If using a mod-based loader, such as Forge or Fabric, but a hybrid mod like [Cardboard](https://modrinth.com/mod/cardboard), then set `USES_PLUGINS` to have the automation utilize `/plugins` mount.\n\n!!! example \"Using Cardboard plugins with Fabric\"\n\n    ```yaml\n        environment:\n          EULA: \"TRUE\"\n          TYPE: \"FABRIC\"\n          MODRINTH_PROJECTS: |\n            fabric-api\n            cardboard\n          USES_PLUGINS: true\n    ```\n\n`/mods`\n: content in this directory is synchronized into `/data/mods` for server types that use mods, [as described above](#mods-vs-plugins). For special cases, the source can be changed by setting `COPY_MODS_SRC` and destination by setting `COPY_MODS_DEST`.\n\n`/config`\n: contents are synchronized into `/data/config` by default, but can be changed with `COPY_CONFIG_DEST`. For example, `-v ./config:/config -e COPY_CONFIG_DEST=/data` will allow you to copy over files like `bukkit.yml` and so on directly into the server directory. The source can be changed by setting `COPY_CONFIG_SRC`. Set `SYNC_SKIP_NEWER_IN_DESTINATION=false` if you want files from `/config` to take precedence over newer files in `/data/config`.\n\nBy default, the [environment variable processing](../configuration/interpolating.md) is performed on synchronized files that match the expected suffixes in `REPLACE_ENV_SUFFIXES` (by default \"yml,yaml,txt,cfg,conf,properties,hjson,json,tml,toml\") and are not excluded by `REPLACE_ENV_VARIABLES_EXCLUDES` and `REPLACE_ENV_VARIABLES_EXCLUDE_PATHS`. This processing can be disabled by setting `REPLACE_ENV_DURING_SYNC` to `false`.\n\nIf you want old mods/plugins to be removed before the content is brought over from those attach points, then add `-e REMOVE_OLD_MODS=TRUE`. You can fine tune the removal process by specifying the `REMOVE_OLD_MODS_INCLUDE` and `REMOVE_OLD_MODS_EXCLUDE` variables, which are comma separated lists of file glob patterns. If a directory is excluded, then it and all of its contents are excluded. By default, only jars are removed. \n\nYou can also specify the `REMOVE_OLD_MODS_DEPTH` (default is 16) variable to only delete files up to a certain level.\n\nFor example: `-e REMOVE_OLD_MODS=TRUE -e REMOVE_OLD_MODS_INCLUDE=\"*.jar\" -e REMOVE_OLD_MODS_DEPTH=1` will remove all old jar files that are directly inside the `plugins/` or `mods/` directory.\n\nThese paths work well if you want to have a common set of modules in a separate location, but still have multiple worlds with different server requirements in either persistent volumes or a downloadable archive.\n\n!!! information \"Multiple source directories\"\n\n    `COPY_PLUGINS_SRC`, `COPY_MODS_SRC`, `COPY_CONFIG_SRC` can each be set to a comma or newline delimited list of container directories to reference.\n\n    For example, in a compose file:\n    \n    ```yaml\n        environment:\n          # ...EULA, etc\n          TYPE: PAPER\n          # matches up to volumes declared below\n          COPY_PLUGINS_SRC: /plugins-common,/plugins-local\n        volumes:\n          - mc-data:/data\n          # For example, reference a shared directory used by several projects\n          - ../plugins-common:/plugins-common:ro\n          # and add plugins unique to this project\n          - ./plugins:/plugins-local:ro\n    ```\n\n    Alternatively, you can declare other directories along with files and URLs to use in [the `MODS` / `PLUGINS` variables](#modsplugins-list).\n\n## Applying extra configuration files\n\nYou can download/copy additional configuration files or other resources before the server starts by using the `APPLY_EXTRA_FILES` environment variable. This is useful for downloading configs that you want to patch or modify during the startup process.\n\nThe format uses a `<` separator between the destination path and the source URL/path. The destination path is relative to the `/data` directory. If specifying a source path, it needs to be path mounted within the container.\n\n!!! example\n\n    With `docker run`\n    \n    ```\n    -e APPLY_EXTRA_FILES=destination<source_url[,destination2<source_url2,...]\n    ```\n    \n    With a compose file:\n    ```yaml\n    environment:\n      APPLY_EXTRA_FILES: |\n        destination<source1_url\n        destination2<source2_path\n    ```\n\n!!! tip \"Patch-able\"\n\nThe `APPLY_EXTRA_FILES` feature is processed prior to [patch processing](../configuration/interpolating.md#patching-existing-files), so this can be used as for baseline files to be patched further at runtime.\n\n## Zip file modpack\n\nLike the `WORLD` option above, you can specify the URL or container path of a \"mod pack\" to download and install into `mods` for Forge/Fabric or `plugins` for Bukkit/Spigot. To use this option pass the environment variable `MODPACK`, such as\n\n```shell\ndocker run -d -e MODPACK=http://www.example.com/mods/modpack.zip ...\n```\n\n!!! note\n    The referenced URL/file must be a zip file with one or more jar files at the\n    top level of the zip archive. Make sure the jars are compatible with the\n    particular `TYPE` of server you are running.\n\n## Generic pack files\n\nTo install all the server content (jars, mods, plugins, configs, etc.) from a zip or tgz file, then set `GENERIC_PACK` to the container path or URL of the archive file. This can also be used to apply a CurseForge modpack that is missing a server start script and/or Forge installer.\n\nIf multiple generic packs need to be applied together, set `GENERIC_PACKS` instead, with a comma separated list of archive file paths and/or URLs to files.\n\nTo avoid repetition, each entry will be prefixed by the value of `GENERIC_PACKS_PREFIX` and suffixed by the value of `GENERIC_PACKS_SUFFIX`, both of which are optional. For example, the following variables\n\n```\nGENERIC_PACKS=configs-v9.0.1,mods-v4.3.6\nGENERIC_PACKS_PREFIX=https://cdn.example.org/\nGENERIC_PACKS_SUFFIX=.zip\n```\n\nwould expand to `https://cdn.example.org/configs-v9.0.1.zip,https://cdn.example.org/mods-v4.3.6.zip`.\n\nIf applying large generic packs, the update can be time-consuming. To skip the update set `SKIP_GENERIC_PACK_UPDATE_CHECK` to \"true\". Conversely, the generic pack(s) can be forced to be applied by setting `FORCE_GENERIC_PACK_UPDATE` to \"true\".\n\nThe most time-consuming portion of the generic pack update is generating and comparing the SHA1 checksum. To skip the checksum generation, set `SKIP_GENERIC_PACK_CHECKSUM` to \"true\".\n\nTo disable specific mods, which can be useful for conflicts between multiple generic packs, you can use the `GENERIC_PACKS_DISABLE_MODS` variable to specify mods to disable.\n\nDisabling mods with docker run:\n```shell\ndocker run -d -e GENERIC_PACKS_DISABLE_MODS=\"mod1.jar mod2.jar\" ...\n```\n\nDisabling mods within docker compose files:\n```yaml\n      GENERIC_PACKS_DISABLE_MODS: |\n        mod1.jar\n        mod2.jar\n```\n\n## Mods/plugins list\n\nYou may also download or copy over individual mods/plugins using the `MODS` or `PLUGINS` environment variables. Both are a comma or newline delimited list of\n\n- URL of a jar file\n- container path to a jar file\n- container path to a directory containing jar files\n\n```shell\ndocker run -d -e MODS=https://www.example.com/mods/mod1.jar,/plugins/common,/plugins/special/mod2.jar ...\n```\n\nThe newline delimiting allows for compose file usage like:\n```yaml\n      PLUGINS: |\n        https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot\n        https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot\n```\n\n!!! note \"Auto-removal\"\n\n    Entries that are removed from the `MODS` or `PLUGINS` list will be automatically removed from the `mods` or `plugins` directory. This is useful for removing mods/plugins that are no longer needed. An empty `MODS` or `PLUGINS` list will remove all mods/plugins.\n\n!!! note \"Disable processing\"\n\n    To temporarily disable processing of the `MODS` or `PLUGINS` list, then comment out the `MODS` or `PLUGINS` environment variable.\n\n## Mod/Plugin URL Listing File \n\nAs an alternative to `MODS`/`PLUGINS`, the variable `MODS_FILE` or `PLUGINS_FILE` can be set with the container path or URL of a text file listing a mod/plugin URLs on each line. For example, the following\n\n     -e MODS_FILE=/extras/mods.txt\n\nwould load from a file mounted into the container at `/extras/mods.txt`. That file might look like:\n\n```text\nhttps://edge.forgecdn.net/files/2965/233/Bookshelf-1.15.2-5.6.40.jar\nhttps://edge.forgecdn.net/files/2926/27/ProgressiveBosses-2.1.5-mc1.15.2.jar\n# This and next line are ignored\n#https://edge.forgecdn.net/files/3248/905/goblintraders-1.3.1-1.15.2.jar\nhttps://edge.forgecdn.net/files/3272/32/jei-1.15.2-6.0.3.16.jar\nhttps://edge.forgecdn.net/files/2871/647/ToastControl-1.15.2-3.0.1.jar\n```\n\n!!! note\n\n    Blank lines and lines that start with a `#` will be ignored\n\n    [This compose file](https://github.com/itzg/docker-minecraft-server/blob/master/examples/mods-file/docker-compose.yml) shows another example of using this feature.\n\n## Remove old mods/plugins\n\nWhen the `MODPACK` option above is specified you can also instruct script to delete old mods/plugins prior to installing new ones. This behaviour is desirable in case you want to upgrade mods/plugins from downloaded zip file.\n\nTo use this option pass the environment variable `REMOVE_OLD_MODS=TRUE`, such as\n\n```shell\ndocker run -d -e REMOVE_OLD_MODS=TRUE -e MODPACK=http://www.example.com/mods/modpack.zip ...\n```\n\n!!! danger \n\n    All content of the `mods` or `plugins` directory will be deleted before unpacking new content from the MODPACK or MODS.\n"
  },
  {
    "path": "docs/mods-and-plugins/modrinth.md",
    "content": "# Auto-download from Modrinth\n\n[Modrinth](https://modrinth.com/) is an open source modding platform with a clean, easy to use website for finding [Fabric, Forge, etc mods](https://modrinth.com/mods) and [Paper, etc plugins](https://modrinth.com/plugins), and [datapacks](https://modrinth.com/datapacks). At startup, the container will automatically locate and download the newest versions of mod/plugin files that correspond to the `TYPE` and `VERSION` in use. Older file versions downloaded previously will automatically be cleaned up.\n\n## Usage\n\nTo use this feature, set the environment variable `MODRINTH_PROJECTS` to a comma or newline separated list of projects.  \n\nEach project entry can be any of the following combinations where a colon (`:`) is used to separate the different parts:\n\n```\n         Project\n         Project : Version\n         Project : Release Type\nPrefix : Project\nPrefix : Project : Version\nPrefix : Project : Release Type\n@ Listing File\n```\n\nWhere:\n\n- **Project** is the project slug or ID, see below\n- **Version** is the version ID (such as \"Oa9ZDzZq\") or number (such as \"2.21.2\"). When omitted, the latest release version will be selected. Using version ID will override Minecraft and loader compatibility checks.\n- **Release Type** is `release`, `beta`, or `alpha` indicating the latest version to select.\n- **Prefix** is `datapack`, `fabric`, `forge`, or `paper`\n    - The `datapack` prefix is optional when running a vanilla server\n    - The `fabric`, `forge`, and `paper` prefixes allow for installing mods/plugins that differ from server's `TYPE`. Using [Sinytra Connector](https://modrinth.com/mod/connector) is an example of this, where Fabric mods can be loaded into a NeoForge server.\n- **Listing file** is a container path to a file containing a list of projects\n\n!!! tip \"Project ID\"\n\n    The project ID can be copied to the clipboard from the project page menu:\n\n    ![Modrinth copy project ID](../img/modrinth-copy-project-id.png)\n\n!!! tip \"Project Slug\"\n\n    The project slug is the part of the URL that follows `/mod/`, `/plugin/`, or `/datapack/`. For example, this one is \"essentialsx\".\n\n    ![](../img/modrinth-plugin-project-slug.png)\n\n!!! tip \"Version number and ID\"\n\n    The version number and ID are located in the metadata panel on a version specific page, such as <https://modrinth.com/plugin/essentialsx/version/2.21.2>\n\n    ![Version ID](../img/modrinth-version-id.drawio.png)\n\n### Examples\n            \n| Description                     | Example projects entry                                |\n|---------------------------------|-------------------------------------------------------|\n| Select latest version           | `fabric-api`                                          |\n| Select specific version         | `fabric-api:bQZpGIz0`<br/>`fabric-api:0.119.2+1.21.4` |\n| Select latest beta version      | `fabric-api:beta`                                     |\n| Latest version using project ID | `P7dR8mSH`                                            |\n| Latest version of datapack      | `datapack:terralith`                                  |\n| Specific version of datapack    | `datapack:terralith:2.5.5`                            |\n| Mod loader override             | `fabric:fabric-api`<br/>`fabric:fabric-api:bQZpGIz0`  |\n| Projects Listing File           | `@/path/to/modrinth-mods.txt`                         |\n\n### Notes\n\n!!! info \"More about listing files\"\n\n    Each line in the listing file is processed as one of the references above; however, blank lines and comments that start with `#` are ignored.\n    \n    Make sure to place the listing file in a mounted directory/volume or declare an appropriate mount for it.\n    \n    For example, `MODRINTH_PROJECTS` can be set to \"@/extras/modrinth-mods.txt\", assuming \"/extras\" has been added to `volumes` section, where the container file `/extras/modrinth-mods.txt` contains\n    \n    ```text\n    # This comment is ignored\n    fabric-api\n    \n    # This and previous blank line are ignore\n    cloth-config\n    datapack:terralith\n    ```\n\n!!! note \"Auto-removal\"\n\n    Entries that are removed from the `MODRINTH_PROJECTS` list will be automatically removed from the `mods` or `plugins` directory. This is useful for removing mods/plugins that are no longer needed. An empty `MODRINTH_PROJECTS` list will remove all mods/plugins.\n\n!!! note \"Disable processing\"\n\n    To temporarily disable processing of the `MODRINTH_PROJECTS` list, then comment out the `MODRINTH_PROJECTS` environment variable.\n\n## Version from Projects\n\nWhen the environment variable `VERSION_FROM_MODRINTH_PROJECTS` is set to \"true\" the Minecraft [`VERSION`](../versions/minecraft.md) will be automatically determined by looking at the most recent version of Minecraft that is supported by all the projects provided in `MODRINTH_PROJECTS`.\n\n!!! example\n\n    Given the environment variables\n    \n    ```yaml\n        MODRINTH_PROJECTS: |\n          viaversion\n          viabackwards\n          griefprevention\n          discordsrv\n        VERSION_FROM_MODRINTH_PROJECTS: true\n    ```\n    \n    Let's say all are supported on Minecraft up to 1.21.8 except griefprevention, which is only supported up to 1.21.7. In that case, `VERSION` will be automatically set to 1.21.7.\n\n## Extra options\n\n`MODRINTH_DOWNLOAD_DEPENDENCIES`\n: Can be set to `none` (the default), `required`, or `optional` to download required and/or optional dependencies.\n\n`MODRINTH_PROJECTS_DEFAULT_VERSION_TYPE`\n: The version type is used to determine the newest version to use from each project. The allowed values are `release` (default), `beta`, `alpha`. Setting to `beta` will pick up both release and beta versions. Setting to `alpha` will pick up release, beta, and alpha versions.\n\n`MODRINTH_LOADER`\n: When using a custom server, set this to specify which loader type will be requested during lookups\n\n"
  },
  {
    "path": "docs/mods-and-plugins/packwiz.md",
    "content": "# Packwiz Modpacks\r\n\r\n[packwiz](https://packwiz.infra.link/) is a CLI tool for maintaining and providing modpack definitions, with support for both CurseForge and Modrinth as sources. See the [packwiz tutorial](https://packwiz.infra.link/tutorials/getting-started/) for more information.\r\n\r\nTo configure server mods using a packwiz modpack, set the `PACKWIZ_URL` environment variable to the location of your `pack.toml` modpack definition:\r\n\r\n```\r\ndocker run -d --pull=always \\\r\n    -v /path/on/host:/data -e TYPE=FABRIC \\\r\n    -e \"PACKWIZ_URL=https://example.com/modpack/pack.toml\" \\\r\n    itzg/minecraft-server\r\n```\r\n\r\npackwiz modpack definitions are processed before other mod definitions (`MODPACK`, `MODS`, etc.) to allow for additional processing/overrides you may want to perform (in case of mods not available via Modrinth/CurseForge, or you do not maintain the pack).\r\n\r\n!!! note \r\n\r\n    packwiz is pre-configured to only download server mods. If client-side mods are downloaded and cause issues, check your pack.toml configuration, and make sure any client-only mods are not set to `\"both\"`, but rather `\"client\"` for the side configuration item.\r\n"
  },
  {
    "path": "docs/mods-and-plugins/spiget.md",
    "content": "# Auto-download using Spiget\n\nThe `SPIGET_RESOURCES` variable can be set with a comma-separated list of SpigotMC resource IDs to automatically download [Spigot/Bukkit/Paper plugins](https://www.spigotmc.org/resources/) using [the spiget API](https://spiget.org/). Resources that are zip files will be expanded into the plugins directory and resources that are simply jar files will be moved there.\n\n!!! important \"SPIGET not SPIGOT\"\n    The variable is purposely spelled SPIG**E**T with an \"E\"\n\nThe **resource ID** can be located from the numerical part of the URL after the shortname/slug and a dot. For example, the ID is **28140** from\n\n    https://www.spigotmc.org/resources/luckperms.28140/\n                                                 =====\n\nFor example, the following will auto-download the [LuckPerms](https://www.spigotmc.org/resources/luckperms.28140/) and [Vault](https://www.spigotmc.org/resources/vault.34315/) plugins:\n\n    -e SPIGET_RESOURCES=28140,34315\n\n!!! note\n    Some plugins, such as EssentialsX (resource ID 9089), do not permit automated downloads via Spiget. Instead, you will need to pre-download the desired file and supply it to the container, such as using the `/plugins` mount point, described [in the main section](index.md).\n\n"
  },
  {
    "path": "docs/requirements.txt",
    "content": "click==8.3.1\ncolorama==0.4.6\ndeepmerge==2.0\nghp-import==2.1.0\ngriffe==2.0.0\nJinja2==3.1.6\nMarkdown==3.10.2\nMarkupSafe==3.0.3\nmergedeep==1.3.4\nmkdocs==1.6.1\nmkdocs-autorefs==1.4.4\nmkdocs-get-deps==0.2.2\nmkdocstrings==1.0.3\nmkdocstrings-python==2.0.3\npackaging==26.0\npathspec==1.0.4\nplatformdirs==4.9.4\nPygments==2.19.2\npymdown-extensions==10.21\npython-dateutil==2.9.0.post0\nPyYAML==6.0.3\npyyaml_env_tag==1.1\nsix==1.17.0\nwatchdog==6.0.0\nzensical==0.0.27\n"
  },
  {
    "path": "docs/sending-commands/commands.md",
    "content": "---\r\ntitle: With Docker\r\n---\r\n\r\n[RCON](http://wiki.vg/RCON) is enabled by default, so you can `exec` into the container to\r\naccess the Minecraft server console:\r\n\r\n```\r\ndocker exec -i mc rcon-cli\r\n```\r\n\r\nNote: The `-i` is required for interactive use of rcon-cli.\r\n\r\nTo run a simple, one-shot command, such as stopping a Minecraft server, pass the command as arguments to `rcon-cli`, such as:\r\n\r\n```\r\ndocker exec mc rcon-cli stop\r\n```\r\n\r\n_The `-i` is not needed in this case._\r\n\r\n## When RCON is disabled\r\n\r\nIf rcon is disabled you can send commands by passing them as arguments to the packaged `mc-send-to-console` script after setting the env var `CREATE_CONSOLE_IN_PIPE` to \"true\". For example, a player can be op'ed in the container `mc` with: \r\n\r\n```shell\r\ndocker exec --user 1000 mc mc-send-to-console op player\r\n                        |                     |\r\n                        +- container name     +- Minecraft commands start here\r\n```\r\n\r\n## Enabling interactive console\r\n\r\nIn order to attach and interact with the Minecraft server make sure to enable TTY and keep stdin open.\r\n\r\n!!! example\r\n\r\n    With `docker run` use the `-it` arguments:\r\n\r\n    ```shell\r\n    docker run -d -it -p 25565:25565 --name mc itzg/minecraft-server\r\n    ```\r\n\r\n    or with a compose file:\r\n\r\n    ```yaml\r\n    services:\r\n      minecraft:\r\n        stdin_open: true\r\n        tty: true\r\n    ```\r\n\r\nWith that you can attach and interact at any time using the following, replacing the `{...}` placeholders.\r\n\r\n...when container is created with `docker run`\r\n```\r\ndocker attach {container name or ID}\r\n```\r\n\r\n...or when declared using a compose file\r\n```\r\ndocker compose attach {service name}\r\n```\r\n\r\nand then Control-p Control-q to **detach**.\r\n\r\n!!! info \"RCON is required for fully interactive, color console\"\r\n\r\n    RCON must be enabled, which is the default, in order to use a fully interactive console with auto-completion and colorized log output.\r\n"
  },
  {
    "path": "docs/sending-commands/ssh.md",
    "content": "---\ntitle: Over SSH\n---\n\nThe container can host an SSH console. It is enabled by setting `ENABLE_SSH` to `true`.\nThe SSH server only supports password based authentication. The password is the same as the RCON password.\n\n!!! question\n    See [the RCON password](../configuration/server-properties.md/#rcon-password) section under configuration/server-properties for more information on how to set an RCON password.\n\nThe SSH server runs on port `2222` inside the container.\n\n??? tip \"Tip: Exposing the SSH port\"\n\n    !!! warning \"Security Implications\"\n        By default, publishing ports in Docker binds them to all network interfaces (`0.0.0.0`), making the SSH console accessible to any device that can reach your host machine.\n        \n        Since the SSH console grants **full administrative access** to your server, it is critical to use a strong [RCON password](../configuration/server-properties.md/#rcon-password). \n        \n        If you wish to restrict access to the local machine only, refer to the [Docker documentation](https://docs.docker.com/engine/network/port-publishing/#publishing-ports) on binding to specific IP addresses (e.g., `127.0.0.1:2222:2222`).\n      \n        If SSH access is only intended for inter-container connections, consider **NOT** forwarding the port to the host machine, and putting the containers in a shared [Docker network](https://docs.docker.com/engine/network/#user-defined-networks).\n\n    ```yaml title=\"compose.yaml\"\n    services:\n      mc:\n        ports:\n          - '25565:25565'\n          - '2222:2222'\n    ```\n\n## Connecting\n\nConnecting should be as simple as running\n```bash\nssh anyuser@127.0.0.1 -p 2222\n```\nand typing in the RCON password.\n\n## Environment variables\n\n| Environment Variable | Usage                     | Default |\n| -------------------- | ------------------------- | ------- |\n| `ENABLE_SSH`   | Enable remote SSH console | `false` |\n\n\n## Example\n\n```yaml title=\"compose.yaml\"\nservices:\n  mc:\n    image: itzg/minecraft-server:latest\n    pull_policy: daily\n    tty: true\n    stdin_open: true\n    ports:\n      - \"25565:25565\"\n      - \"2222:2222\"\n    environment:\n      EULA: \"TRUE\"\n      ENABLE_SSH: true\n      RCON_PASSWORD_FILE: /run/secrets/rcon_pass\n    volumes:\n      # attach the relative directory 'data' to the container's /data path\n      - ./data:/data\n    \nsecrets:\n  rcon_pass:\n    file: ./rcon_password\n```\n"
  },
  {
    "path": "docs/sending-commands/websocket.md",
    "content": "---\ntitle: With WebSocket\n---\n\nWith `WEBSOCKET_CONSOLE` set to `true`, logs can be streamed, and commands sent, over a WebSocket connection.\nThe API is available on `/console`.\n\n## Password\nA password must be supplied using the `Sec-WebSocket-Protocol` header. This is done by putting `mc-server-runner-ws-v1` in the first slot, and the password in the second. The password can be set with `RCON_PASSWORD` or `WEBSOCKET_PASSWORD`. The latter overwrites the former. Authentication can be disabled with `WEBSOCKET_DISABLE_AUTHENTICATION`.\n??? Example \"Examples\"\n    ```js title=\"JavaScript example\"\n    let socket = new WebSocket(\"http://localhost:80/websocket\", [\"mc-server-runner-ws-v1\", \"rcon-password\"]);\n    ```\n\n## Allowed origins\nA list of comma-separated allowed origins should be supplied with `WEBSOCKET_ALLOWED_ORIGINS`. Origin checking can be disabled with `WEBSOCKET_DISABLE_ORIGIN_CHECK`.\n\n## Listen address\nThe listen address and port can be set with `WEBSOCKET_ADDRESS` (defaults to `0.0.0.0:80`), but it's recommended to listen on all interfaces when running in Docker.\n\n## Log history\nWhen a connection is established, the last 50 (by default, configurable with `WEBSOCKET_LOG_BUFFER_SIZE`) log lines are sent with a `logHistory` type message.\n\n??? tip \"Tip: Remember to forward the WebSocket port on the host\"\n\n    !!! warning \"Security Implications\"\n        By default, publishing ports in Docker binds them to all network interfaces (`0.0.0.0`), making the WebSocket console accessible to any device that can reach your host machine.\n        \n        Since the WebSocket console grants **full administrative access** to your server, it is critical to use a strong [WebSocket password](#password) or [RCON password](../configuration/server-properties.md/#rcon-password).\n      \n        If you wish to restrict access to the local machine only, refer to the [Docker documentation](https://docs.docker.com/engine/network/port-publishing/#publishing-ports) on binding to specific IP addresses (e.g., `127.0.0.1:80:80`).\n        \n        If WebSocket access is only intended for inter-container connections, consider **NOT** forwarding the port to the host machine, and putting the containers in a shared [Docker network](https://docs.docker.com/engine/network/#user-defined-networks).\n\n    ```yaml title=\"compose.yaml\"\n    services:\n      mc:\n        ports:\n          - '25565:25565'\n          - '80:80'\n    ```\n\n## Environment variables\n| Environment Variable               | Usage                                                      | Default      |\n| ---------------------------------- | ---------------------------------------------------------- | ------------ |\n| `WEBSOCKET_CONSOLE`                | Allow remote shell over WebSocket                          | `false`      |\n| `WEBSOCKET_ADDRESS`                | Bind address for WebSocket server                          | `0.0.0.0:80` |\n| `WEBSOCKET_DISABLE_ORIGIN_CHECK`   | Disable checking if origin is trusted                      | `false`      |\n| `WEBSOCKET_ALLOWED_ORIGINS`        | Comma-separated list of trusted origins                    | ` `          |\n| `WEBSOCKET_PASSWORD`               | Password will be the same as RCON_PASSWORD if unset        | ` `          |\n| `WEBSOCKET_DISABLE_AUTHENTICATION` | Disable WebSocket authentication                           | `false`      |\n| `WEBSOCKET_LOG_BUFFER_SIZE`        | Number of log lines to save and send to connecting clients | `50`         |\n\n## API Schema\n```ts title=\"API Schema\"\ninterface StdinMessage {\n  type: \"stdin\";\n  data: string;\n}\n\ninterface StdoutMessage {\n  type: \"stdout\";\n  data: string;\n}\n\ninterface StderrMessage {\n  type: \"stderr\";\n  data: string;\n}\n\ninterface LogHistoryMessage {\n  type: \"logHistory\";\n  lines: string[];\n}\n\ninterface AuthFailureMessage {\n  type: \"authFailure\";\n  reason: string;\n}\n\n// Messages sent from Client -> Server\nexport type ClientMessage = StdinMessage;\n\n// Messages sent from Server -> Client\nexport type ServerMessage =\n  | StdoutMessage\n  | StderrMessage\n  | LogHistoryMessage\n  | AuthFailureMessage;\n```\n"
  },
  {
    "path": "docs/types-and-platforms/index.md",
    "content": "# Server Types and Modpack Platforms\n\nServer types can be selected by setting the `TYPE` environment variable to one of the types described in these sections.\n\nThe default server type is [the official software provided by Mojang](https://www.minecraft.net/en-us/download/server), which can be explicitly selected by setting `TYPE` to \"VANILLA\".\n\n**From the sections to the left...**\n\nThe **modpack platforms** take a modpack identifier, file, etc and use that to select and download the appropriate version of a mod loader, such as Forge or Fabric, downloading declared mods/plugins, and apply any additional files. The modpack platforms are selected by setting `MODPACK_PLATFORM`; however, for ease of use and backward compatibility, the selection can be set in `TYPE` or `MOD_PLATFORM`.\n\nThe individual **server types** allow for selecting the mod loader / server type and specific versions of those. \n\n!!! important\n    For all the server types, the `VERSION` environment variable is used to declare the Minecraft version itself, such as 1.19.4. Each server type will have specific variables for qualifying their version in addition to the Minecraft version.\n"
  },
  {
    "path": "docs/types-and-platforms/mod-platforms/auto-curseforge.md",
    "content": "# Auto CurseForge\n\nTo manage a CurseForge modpack automatically with upgrade support, pinned or latest version tracking, set `MODPACK_PLATFORM`, `MOD_PLATFORM` or `TYPE` to \"AUTO_CURSEFORGE\". The appropriate mod loader (Forge / Fabric) version will be automatically installed as declared by the modpack. This mode will also take care of cleaning up unused files installed by previous versions of the modpack, but world data is never auto-removed.\n\n## API Key\n\n!!! warning \"CurseForge API key usage\"\n\n    A CurseForge API key is **required** to use this feature. Go to their [developer console](https://console.curseforge.com/), generate an API key, and set the environment variable `CF_API_KEY`.\n\n    When entering your API Key in a docker compose file you will need to escape any `$` character with a second `$`. Refer to [this compose file reference section](https://docs.docker.com/compose/compose-file/compose-file-v3/#variable-substitution) for more information.\n\n    Example if your key is `$11$22$33aaaaaaaaaaaaaaaaaaaaaaaaaa`:\n    ```yaml title=\"compose.yaml\"\n    environment:\n      CF_API_KEY: '$$11$$22$$33aaaaaaaaaaaaaaaaaaaaaaaaaa'\n    ```\n    If you use `docker run` you will need to make sure to use single quotes:\n\n    ```shell\n    docker run ... -e CF_API_KEY='$11$22$33aaaaaaaaaaaaaaaaaaaaaaaaaa'\n    ```\n\n    To avoid exposing the API key, it is highly recommended to use a `.env` file, which is [loaded automatically by docker compose](https://docs.docker.com/compose/environment-variables/set-environment-variables/#substitute-with-an-env-file). You **do not** need to escape `$`'s with a second `$` in the `.env` file **as long as the key is wrapped in single quotes**.\n\n    ```title=\".env\"\n    CF_API_KEY='$11$22$33aaaaaaaaaaaaaaaaaaaaaaaaaa'\n    ```\n\n    The variable should to be referenced from the compose file, such as:\n\n    ```yaml title=\"compose.yaml\"\n    environment:\n      CF_API_KEY: ${CF_API_KEY}\n    ```\n\n    The .env file should be placed in the same directory as your compose file like so:\n\n    ```\n    minecraft-server/\n    ├── .env\n    ├── compose.yaml\n    ├── data/\n    ```\n\n    To use the equivalent with `docker run` you need to specify the `.env` file explicitly:\n    ```shell\n    docker run --env-file=.env itzg/minecraft-server\n    ```\n\n    Alternately you can use [docker secrets](https://docs.docker.com/compose/how-tos/use-secrets/) with a `CF_API_KEY_FILE` environment variable:\n    ```yaml title=\"compose.yaml\"\n    service:\n      environment:\n        CF_API_KEY_FILE: /run/secrets/cf_api_key\n      secrets:\n        - cf_api_key\n\n    secrets:\n      cf_api_key:\n        file: cf_api_key.secret\n    ```\n\n\n!!! note\n    Be sure to use the appropriate [image tag for the Java version compatible with the modpack](../../versions/java.md).\n\n    Most modpacks require a good amount of memory, so it best to set `MEMORY` to at least \"4G\" since the default is only 1 GB.\n\n## Usage\n\nUse one of the following to specify the modpack to install:\n\nPass a page URL to the modpack or a specific file with `CF_PAGE_URL` such as the modpack page \"https://www.curseforge.com/minecraft/modpacks/all-the-mods-8\" or a specific file \"https://www.curseforge.com/minecraft/modpacks/all-the-mods-8/files/4248390\".\n\n!!! example \"Using CF_PAGE_URL\"\n\n    ```yaml title=\"Using compose.yaml\"\n    environment:\n      # ...\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\n      # allocate from https://console.curseforge.com/ and set in .env file\n      CF_API_KEY: ${CF_API_KEY}\n      CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/all-the-mods-8\n    ```\n\n    ```title=\"Using docker run\"\n    docker run -e CF_API_KEY=${CF_API_KEY} -e TYPE=AUTO_CURSEFORGE -e CF_PAGE_URL=https://www.curseforge.com/minecraft/modpacks/all-the-mods-8\n    ```\n\nInstead of a URL, the modpack slug can be provided as `CF_SLUG`. The slug is the short identifier visible in the URL after \"/modpacks/\", such as\n\n![cf-slug](../../img/cf-slug.png)\n\n!!! example \"Using CF_SLUG\"\n\n    ```yaml title=\"Using compose.yaml\"\n    environment:\n      # ...\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\n      # allocate from https://console.curseforge.com/ and set in .env file\n      CF_API_KEY: ${CF_API_KEY}\n      CF_SLUG: all-the-mods-8\n    ```\n\n    ```title=\"Using docker run\"\n    docker run -e CF_API_KEY=${CF_API_KEY} -e TYPE=AUTO_CURSEFORGE -e CF_SLUG=all-the-mods-8\n    ```\n\n### Pinning modpack and mod loader versions\n\nThe latest modpack file and its associated mod loader will be located and installed by default on startup (including automatic upgrading of both on subsequent startups, if a later version is found on CurseForge). If a specific version is desired instead, you can use one of the following options. With any of these options **do not select a server file** -- they lack the required manifest and defeat the ability to consistently automate startup.\n\n- Use `CF_PAGE_URL`, but include the full URL to a specific file\n- Set `CF_FILE_ID` to the numerical file ID\n- Specify a substring to match the desired filename with `CF_FILENAME_MATCHER`\n\nThe following shows where to get the URL to the specific file and also shows where the file ID is located:\n\n![cf-file-id](../../img/cf-file-id.png)\n\nThe following examples all refer to version 1.0.7 of ATM8:\n\n```yaml\n  CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/all-the-mods-8/files/4248390\n```\n\n```yaml\n  CF_SLUG: all-the-mods-8\n  CF_FILE_ID: \"4248390\"\n```\n\n```yaml\n  CF_SLUG: all-the-mods-8\n  CF_FILENAME_MATCHER: 1.0.7\n```\n\nPinning modpack version also pins the mod loader (to the version specified by the modpack). Mod loader version cannot be pinned independently of the modpack.\n\n### Custom modloader versions\n\nBy default, AUTO_CURSEFORGE will use the exact modloader version declared by the modpack. However, you can override the modloader version by setting the following environment variable:\n\n- `CF_MOD_LOADER_VERSION`: Override the mod loader version (e.g., `43.4.22`)\n\n!!! example \"Override mod loader version\"\n\n    ```yaml\n    environment:\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\n      CF_API_KEY: ${CF_API_KEY}\n      CF_SLUG: all-the-mods-8\n      CF_MOD_LOADER_VERSION: \"43.4.22\"\n    ```\n\n    ```title=\"Using docker run\"\n    docker run -e CF_MOD_LOADER_VERSION=43.4.22 -e CF_SLUG=my-fabric-pack ...\n    ```\n\n!!! warning \"Compatibility\"\n\n    Using a custom modloader version that differs significantly from what the modpack was designed for may cause compatibility issues. Use this feature carefully and test thoroughly.\n\n## Manual Downloads\n\nFor mod, modpacks, and world files that are not allowed for automated download, the container path `/downloads` can be attached and matching files will be retrieved from there. The subdirectories `mods`, `modpacks`, and `worlds` will also be checked accordingly. To change the source location of downloaded files, set `CF_DOWNLOADS_REPO` to an existing container path. To disable this feature, set `CF_DOWNLOADS_REPO` to an empty string.\n\n!!! warning \"Download using a browser\"\n\n    It's important to use a browser to download the files listed for manual download. If your Docker host is running without a graphical interface, then you can use `scp` or similar to transfer the files to it.\n\n!!! example\n\n    Assuming Docker compose is being used:\n\n    1. Create a directory next to the `compose.yaml` file. The name doesn't matter, but \"downloads\" is the common convention\n    2. From the \"Mods Need Download\" output, visit the download page of each, click on the file download and save that file into the directory created in the previous step\n    3. Add a host directory mount to the volumes section where the container path **must be** `/downloads`. The snippet below shows how that will look\n    4. Re-run `docker compose up -d` to apply the changes\n\n    ```yaml\n        volumes:\n          ./downloads:/downloads\n    ```\n\n## Unpublished Modpacks\n\nIf you wish to use an unpublished modpack zip, set the container path to the file in `CF_MODPACK_ZIP`. Similarly, the container path to a modpack manifest JSON can be passed to `CF_MODPACK_MANIFEST`.  In either case, **the modpack slug or page URL must still be provided**.\n\n!!! example\n\n    ```yaml\n    services:\n      mc:\n        image: itzg/minecraft-server:latest\n        pull_policy: daily\n        environment:\n          EULA: true\n          MODPACK_PLATFORM: AUTO_CURSEFORGE\n          # allocate from https://console.curseforge.com/ and set in .env file\n          CF_API_KEY: ${CF_API_KEY}\n          CF_MODPACK_MANIFEST: /manifests/manifest.json\n          CF_SLUG: \"custom\"\n        volumes:\n          - ./manifests:/manifests:ro\n    ```\n\n    where an exported manifest file should look like:\n\n    ```json\n    {\n      \"minecraft\": {\n        \"version\": \"1.20.4\",\n        \"modLoaders\": [\n          {\n            \"id\": \"fabric-0.15.3\",\n            \"primary\": true\n          }\n        ]\n      },\n      \"manifestType\": \"minecraftModpack\",\n      \"manifestVersion\": 1,\n      \"name\": \"Custom\",\n      \"author\": \"\",\n      \"files\": [\n        {\n          \"projectID\": 351725,\n          \"fileID\": 4973035,\n          \"required\": true\n        },\n        {\n          \"projectID\": 306612,\n          \"fileID\": 5010374,\n          \"required\": true\n        }\n      ],\n      \"overrides\": \"overrides\"\n    }\n    ```\n\n## Exclude client mods\n\nQuite often there are mods that need to be excluded, such as ones that did not properly declare as a client mod via the file's game versions. Similarly, there are some mods that are incorrectly tagged as client only. The following describes some options to exclude/include mods:\n\nMods can be excluded by passing a comma or space delimited list of **project** slugs or IDs via `CF_EXCLUDE_MODS`. Similarly, there are some mods that are incorrectly tagged as client only. For those, pass the **project** slugs or IDs via `CF_FORCE_INCLUDE_MODS`. These lists will be combined with the content of the exclude/include file, if given. Alternatively, all mods can be excluded by setting `CF_EXCLUDE_ALL_MODS` to `true`\n\n!!! note\n    `CF_FORCE_INCLUDE_MODS` will not download additional mods.\n\n    For additional mods, refer to [the `CURSEFORGE_FILES` variable](../../mods-and-plugins/curseforge-files.md).\n\nA mod's project ID can be obtained from the right hand side of the project page:\n![cf-project-id](../../img/cf-project-id.png)\n\nFor more complex scenarios, global and per modpack exclusions can be declared in a container-accessible JSON file and referenced with `CF_EXCLUDE_INCLUDE_FILE`. By default, [the file bundled with the image](https://raw.githubusercontent.com/itzg/docker-minecraft-server/master/files/cf-exclude-include.json) will be used, but can be disabled by setting this to an empty string. The schema of this file [is documented here](https://github.com/itzg/mc-image-helper#excludeinclude-file-schema).\n\nIf needing to iterate on the options above, set `CF_FORCE_SYNCHRONIZE` to \"true\" to ensure the exclude/includes are re-evaluated.\n\n!!! important\n    These options are provided to empower you to get your server up and running quickly. Please help out by reporting an issue with the respective mod project. Ideally mod developers should [use correct registrations for one-sided client mods](https://docs.minecraftforge.net/en/latest/concepts/sides/#writing-one-sided-mods). Understandably, those code changes may be non-trivial, so mod authors can also add \"Client\" to the game versions when publishing.\n\n!!! tip \"Embedded comments\"\n\n    Comments can be embedded in the list using the `#` character.\n\n    ```yaml\n          CF_EXCLUDE_MODS: |\n            # Exclude client-side mods not published correctly\n            creative-core\n            default-options\n    ```\n\n## Excluding Overrides Files\n\nModpack zip files typically include an `overrides` subdirectory that may contain config files, world data, and extra mod files. All of those files will be extracted into the `/data` path of the container. If any of those files, such as incompatible mods, need to be excluded from extraction, then the `CF_OVERRIDES_EXCLUSIONS` variable can be set with a comma or newline delimited list of ant-style paths ([see below](#ant-style-paths)) to exclude, relative to the overrides (or `/data`) directory.\n\n### Ant-style paths\n\nAnt-style paths can include the following globbing/wildcard symbols:\n\n| Symbol | Behavior                                                |\n|--------|---------------------------------------------------------|\n| `*`    | Matches zero, one, or many characters except a slash    |\n| `**`   | Matches zero, one, or many characters including slashes |\n| `?`    | Matches one character                                   |\n\n!!! example\n\n    The following compose `environment` entries show how to exclude Iris and Sodium mods from the overrides\n\n    ```yaml\n      CF_OVERRIDES_EXCLUSIONS: mods/iris*.jar,mods/sodium*.jar\n    ```\n\n    or using newline delimiter, which improves maintainability\n\n    ```yaml\n      CF_OVERRIDES_EXCLUSIONS: |\n        mods/iris*.jar\n        mods/sodium*.jar\n    ```\n\n## World/Level Data\n\nSome modpacks come with world/save data via a worlds file and/or the overrides provided with the modpack. Either approach can be selected to set the `LEVEL` to the resulting saves directory by setting `CF_SET_LEVEL_FROM` to either:\n\n- `WORLD_FILE`\n- `OVERRIDES`\n\n## Ignore missing files\n\nSome mods use temporary files from the modpack and delete them when finished. Others will patch themselves and \"disable\" the original mod jar, such as gregtech. In order to avoid the installer from detecting the absent file(s) and re-installing, those files can be ignored by passing a comma, newline delimited list, or a file globbing pattern to `CF_IGNORE_MISSING_FILES`.\n\n!!! hint\n\n    A warning log will indicate what files were found to be missing from the previous installation run, such as\n\n    ```\n    Re-installing due to missing files from modpack: [mods/gregtech-2.6.2-beta.jar]\n    ```\n\n!!! example\n\n    In a Compose file\n    ```yaml\n      environment:\n        CF_IGNORE_MISSING_FILES: |\n          mods/gregtech-2.6.2-beta.jar\n          mods/*.jar\n    ```\n\n\n## Extra options\n\nOther configuration available:\n\n- `CF_PARALLEL_DOWNLOADS` (default is 4): specify how many parallel mod downloads to perform\n- `CF_OVERRIDES_SKIP_EXISTING` (default is false): if set, files in the overrides that already exist in the data directory are skipped. **NOTE** world data is always skipped, if present.\n- `CF_FORCE_REINSTALL_MODLOADER` (default is false): this can be set to `true` to force the modpack's declared modloader, such as Forge, to be reinstalled. This is useful if some of the modloader's files become corrupted.\n"
  },
  {
    "path": "docs/types-and-platforms/mod-platforms/curseforge.md",
    "content": "# Manual CurseForge server packs\n\n!!! warning \"Deprecated\"\n\n    This mod platform type is deprecated. Please use [AUTO_CURSEFORGE](auto-curseforge.md) for new deployments.\n\nEnable this server mode by setting `MODPACK_PLATFORM`, `MOD_PLATFORM` or `TYPE` to \"CURSEFORGE\" along with the following specific variables.\n\nYou need to specify a modpack to run, using the `CF_SERVER_MOD` environment\nvariable. A CurseForge server modpack is available together with its respective\nclient modpack at <https://www.curseforge.com/minecraft/modpacks> .\n\nNow you can add a `-e CF_SERVER_MOD=name_of_modpack.zip` to your command-line.\n```shell\ndocker run -d --pull=always -v /path/on/host:/data -e TYPE=CURSEFORGE \\\n    -e CF_SERVER_MOD=SkyFactory_4_Server_4.1.0.zip \\\n    -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server\n```\nIf you want to keep the pre-download modpacks separate from your data directory,\nthen you can attach another volume at a path of your choosing and reference that.\nThe following example uses `/modpacks` as the container path as the pre-download area:\n```shell\ndocker run -d --pull=always -v /path/on/host:/data \\\n    -v /path/to/modpacks:/modpacks -e TYPE=CURSEFORGE \\\n    -e CF_SERVER_MOD=/modpacks/SkyFactory_4_Server_4.1.0.zip \\\n    -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server\n```\n### Modpack data directory\n\nBy default, CurseForge modpacks are expanded into the sub-directory `/data/FeedTheBeast` and executed from there. (The default location was chosen for legacy reasons, when Curse and FTB were maintained together.)\n\nThe directory can be changed by setting `CF_BASE_DIR`, such as `-e CF_BASE_DIR=/data`.\n\n### Buggy start scripts\n\nSome modpacks have buggy or overly complex start scripts. You can avoid using the bundled start script and use this image's standard server-starting logic by adding `-e USE_MODPACK_START_SCRIPT=false`.\n\n### Fixing \"unable to launch forgemodloader\"\n\nIf your server's modpack fails to load with an error [like this](https://support.feed-the-beast.com/t/cant-start-crashlanding-server-unable-to-launch-forgemodloader/6028/2):\n\n    unable to launch forgemodloader\n\nthen you apply a workaround by adding this to the run invocation:\n\n    -e FTB_LEGACYJAVAFIXER=true\n"
  },
  {
    "path": "docs/types-and-platforms/mod-platforms/ftb.md",
    "content": "# Feed the Beast\r\n\r\n!!! note\r\n    Requires one of the Ubuntu with Hotspot images listed in [the Java versions section](../../versions/java.md).\r\n\r\n[Feed the Beast application](https://www.feed-the-beast.com/) modpacks are supported by setting `MODPACK_PLATFORM`, `MOD_PLATFORM` or `TYPE` to \"FTBA\"\r\n\r\n!!! note\r\n    The \"A\" at the end of \"FTBA\" is important. The value \"FTB\" used to be an alias for \"CURSEFORGE\".\r\n\r\nThis mod platform type will automatically take care of downloading and installing the modpack and appropriate version of Forge, so the `VERSION` does not need to be specified.\r\n\r\n### Environment Variables:\r\n- `FTB_MODPACK_ID`: **required**, the numerical ID of the modpack to install. The ID can be located by [finding the modpack](https://www.feed-the-beast.com/modpack) and locating the ID in this part of the URL:\r\n\r\n  ```\r\n  https://www.feed-the-beast.com/modpacks/23-ftb-infinity-evolved-17\r\n                                          ^^\r\n  ```\r\n- `FTB_MODPACK_VERSION_ID`: optional, the numerical ID of the version to install. If not specified, the latest version will be installed. The \"Version ID\" can be obtained by hovering over a server file entry and grabbing [this ID in the URL](../../img/ftba-version-id-popup.png).\r\n\r\n- `FTB_FORCE_REINSTALL`: if the files become inconsistent, such as when accidentally removing them, the FTB can be forced to re-run by setting this to \"true\"\r\n\r\n### Upgrading\r\n\r\nIf a specific `FTB_MODPACK_VERSION_ID` was not specified, simply restart the container to pick up the newest modpack version. If using a specific version ID, recreate the container with the new version ID.\r\n\r\n### Example\r\n\r\nThe following example runs the latest version of [FTB Presents Direwolf20 1.12](https://ftb.neptunepowered.org/pack/ftb-presents-direwolf20-1-12/):\r\n\r\n``` shell\r\ndocker run -d --pull=always -v /path/on/host:/data \\\r\n    -e EULA=TRUE -e TYPE=FTBA \\\r\n    -e FTB_MODPACK_ID=31 -p 25565:25565 \\\r\n    itzg/minecraft-server:java8-multiarch\r\n```\r\n"
  },
  {
    "path": "docs/types-and-platforms/mod-platforms/gtnh.md",
    "content": "# Auto-setup GTNH server\n\n[GT New Horizons (GTNH)](https://www.gtnewhorizons.com/) is a Minecraft 1.7.10 modpack maintained and supported by dedicated community members! With over 10 years in development, GTNH offers a carefully balanced and immersive experience to challenge players as they climb through the 15 tiers of technology. The ultimate goal of GTNH is to build the Stargate, an interdimensional teleporter and the symbol for absolute prestige, aptitude, and determination. \n\nAs GTNH is a complex modpack with some specifics it has its own `TYPE` to simplify the deployment and update process. To use it set the environment variable `TYPE` to \"GTNH\". \n\nConfiguration options with defaults:\n\n- `GTNH_PACK_VERSION`=latest\n- `GTNH_DELETE_BACKUPS`=false\n- `SKIP_GTNH_UPDATE_CHECK`=false\n\n## Set Modpack version\n\nAs GTNH is a Minecraft 1.7.10 modpack, when using it your minecraft version is set to 1.7.10 by default. The [modpack version](https://www.gtnewhorizons.com/downloads/) can be selected by setting `GTNH_PACK_VERSION` to `latest`, `latest-dev` or any specific version number. `latest` will automatically select the latest full release version available and deploy the server with it (Note: this will also automatically update the server on startup). `latest-dev` does the same but selects the latest version marked as beta or RC (it won't select a full release version even if a newer exist). The third (and recommended) option is setting the server to a specific version like `2.8.1` to manage updates manually.\n\n> To actively prevent an update from happening you can set the environment variable `SKIP_GTNH_UPDATE_CHECK` to true this will prevent any update check from running, but will also prevent the server install from running, so just set it after the initial setup.\n\n## Resource requirements\n\n**Recommended Minimum:**\n\n  - 2-4 cpu cores\n  - 6GB of RAM +0.5GB per extra player (early game)\n  - 6GB of RAM +1GB per extra player (~UV tier+)\n  - 20GB+ storage. HDD is feasible, SSD is preferred\n\nFor more details regarding the server setup consult the [modpack wiki](https://wiki.gtnewhorizons.com/wiki/Server_Setup).\n\n## Java Version\n\nGTNH supports java 8 and 17+ (java 17+ is always recommended for maximum performance). The server will only start when a supported version of itzg/docker-minecraft-server is used.\n\nFor optimal performance choose java25 with GTNH 2.8.0 and later.\n\n## Config backups\n\nDuring version upgrade, the server will replace all config files to make sure all new features are setup as intended. The old config files are stored in a backup folder in the data directory, for you to use as reference for manual reapplication of your changed settings. Set the environment variable `GTNH_DELETE_BACKUPS` to true to delete all backup folders at startup. \n\n## server.properties defaults\n\nTo deliver the intended GTNH by default, when running a GTNH server, the following options are set in `server.properties`. It is recommended to leave them as is, but if you know what you are doing feel free to play around with them.\n\n- `LEVEL_TYPE=rwg`\n- `DIFFICULTY=hard`\n- `ALLOW_FLIGHT=true`\n- `ENABLE_COMMAND_BLOCK=true`\n- `MOTD=Greg Tech New Horizon <current-pack-version>`\n\n## Java args\n\nWith java 17+ the server starts with `-Dfml.readTimeout=180 @java9args.txt -jar lwjgl3ify-forgePatches.jar`. \n\nWith java 8 the server stars with `-XX:+UseStringDeduplication -XX:+UseCompressedOops -XX:+UseCodeCacheFlushing -Dfml.readTimeout=180 -jar forge-1.7.10-10.13.4.1614-1.7.10-universal.jar`\n"
  },
  {
    "path": "docs/types-and-platforms/mod-platforms/modrinth-modpacks.md",
    "content": "# Modrinth Modpacks\n\n[Modrinth Modpacks](https://modrinth.com/modpacks) can automatically be installed along with the required mod loader (Forge or Fabric) by setting `MODPACK_PLATFORM`, `MOD_PLATFORM` or `TYPE` to \"MODRINTH\". Upgrading (and downgrading) takes care of cleaning up old files and upgrading (and downgrading) the mod loader.\n\n## Modpack project\n\nThe desired modpack project is specified with the `MODRINTH_MODPACK` environment variable and must be one of:\n\n- The project \"slug\", which is located in the URL shown here:\n\n  ![](../../img/modrinth-project-slug.drawio.png)\n\n- The project ID, which is located in the bottom of the left panel, shown here\n\n  ![](../../img/modrinth-project-id.drawio.png)\n\n- The project page URL, such as <https://modrinth.com/modpack/cobblemon-fabric>. As described below, this can further be the page URL of a modpack's version page.\n\n- A custom URL of a hosted [mrpack file](https://support.modrinth.com/en/articles/8802351-modrinth-modpack-format-mrpack)\n\n- The container path to a local [mrpack file](https://support.modrinth.com/en/articles/8802351-modrinth-modpack-format-mrpack)\n\n## Modpack version\n\nThe automatic modpack version resolving can be narrowed in a few ways:\n\nThe latest release or beta version, respectively, of the Modrinth modpack is selected when `VERSION` is \"LATEST\" or \"SNAPSHOT\". That can be overridden by setting `MODRINTH_MODPACK_VERSION_TYPE` to \"release\", \"beta\", or \"alpha\".\n\nThe resolved modpack version can be narrowed by setting `VERSION` to a specific Minecraft version, such as \"1.19.2\".\n\nThe selected version can also be narrowed to a particular mod loader by setting `MODRINTH_LOADER` to either \"forge\", \"fabric\", or \"quilt\".\n\nInstead of auto resolving, a specific version of modpack file can be specified by passing the version's page URL to `MODRINTH_MODPACK`, such as <https://modrinth.com/modpack/cobblemon-fabric/version/1.3.2> or by setting `MODRINTH_VERSION` to the version ID or number located in the Metadata section, as shown here\n\n![](../../img/modrinth-version-id.drawio.png)\n\n## Ignore missing files\n\nSome mods, such as [MCInstance Loader](https://modrinth.com/mod/mcinstance-loader), use temporary files from the modpack and delete them when finished. In order to avoid the installer from detecting the absent file(s) and re-installing, those files can be ignored by passing a comma, newline delimited list or, a file globbing pattern to `MODRINTH_IGNORE_MISSING_FILES`.\n\n!!! example\n\n    In a Compose file\n    ```yaml\n      environment:\n        MODRINTH_IGNORE_MISSING_FILES: |\n          config/mcinstanceloader/pack.mcinstance\n          mods/*.jar\n    ```\n\n## Excluding files\n\nTo exclude client mods that are incorrectly declared by the modpack as server-compatible, set `MODRINTH_EXCLUDE_FILES` to a comma or newline delimited list of partial file names to exclude. You may need to set `MODRINTH_FORCE_SYNCHRONIZE` to \"true\" while iterating on a compatible set of mods to use.\n\n!!! example\n\n    In a Compose file:\n    ```yaml\n      MODRINTH_EXCLUDE_FILES: |\n        notenoughanimations\n        lambdynamiclights\n        euphoriapatcher\n    ```\n\n## Force-include files\n\nTo force include client mods, set `MODRINTH_FORCE_INCLUDE_FILES` to a comma or newline delimited list of partial file names. You may need to set `MODRINTH_FORCE_SYNCHRONIZE` to \"true\" while iterating on a compatible set of mods to use.\n\n!!! example\n\n    In a Compose file:\n    ```yaml\n      MODRINTH_FORCE_INCLUDE_FILES: |\n        yet-another-config-lib\n    ```\n\n## Default exclude/includes\n\nThe image comes with a default set of exclude/includes, maintained [in the repo files area](https://github.com/itzg/docker-minecraft-server/blob/master/files/modrinth-exclude-include.json) and uses the same [JSON schema](https://github.com/itzg/mc-image-helper?tab=readme-ov-file#excludeinclude-file-schema) as Auto CurseForge. Those defaults can be disabled by setting the env var `MODRINTH_DEFAULT_EXCLUDE_INCLUDES` to an empty string.\n\n## Excluding Overrides Files\n\nModrinth mrpack/zip files may include an `overrides` subdirectory that contains config files, world data, and extra mod files. All of those files will be extracted into the `/data` path of the container. If any of those files, such as incompatible mods, need to be excluded from extraction, then the `MODRINTH_OVERRIDES_EXCLUSIONS` variable can be set with a comma or newline delimited list of ant-style paths ([see below](#ant-style-paths)) to exclude, relative to the overrides (or `/data`) directory.\n\n### Ant-style paths\n\nAnt-style paths can include the following globbing/wildcard symbols:\n\n| Symbol | Behavior                                                |\n|--------|---------------------------------------------------------|\n| `*`    | Matches zero, one, or many characters except a slash    |\n| `**`   | Matches zero, one, or many characters including slashes |\n| `?`    | Matches one character                                   |\n\n!!! example\n\n    The following compose `environment` entries show how to exclude Iris and Sodium mods from the overrides\n    \n    ```yaml\n      MODRINTH_OVERRIDES_EXCLUSIONS: mods/NekosEnchantedBooks-*.jar,mods/citresewn-*.jar\n    ```\n    \n    or using newline delimiter, which improves maintainability\n    \n    ```yaml\n      MODRINTH_OVERRIDES_EXCLUSIONS: |\n        mods/NekosEnchantedBooks-*.jar\n        mods/citresewn-*.jar\n    ```\n\n"
  },
  {
    "path": "docs/types-and-platforms/server-types/bukkit-spigot.md",
    "content": "# Bukkit/Spigot\r\n\r\n!!! failure \"GetBukkit site no longer supports automated downloads\"\r\n\r\n    The downloads provider <https://getbukkit.org> seems to no longer support automated downloads. As such, it is highly recommended to switch to [Paper](paper.md) instead since it is actively maintained and fully compatible with Bukkit/Spigot plugins.\r\n\r\nRun a Bukkit/Spigot server type by setting the environment variable `TYPE` to \"BUKKIT\" or \"SPIGOT\".\r\n\r\n!!! example\r\n\r\n    Command-line\r\n    ```\r\n    docker run ... -e TYPE=SPIGOT ...\r\n    ```\r\n\r\n    Compose\r\n    ```yaml\r\n        environment:\r\n          ...\r\n          TYPE: SPIGOT\r\n    ```\r\n\r\nIf the downloaded server jar is corrupted, set `FORCE_REDOWNLOAD` to \"true\" to force a re-download during next container startup. After successfully re-downloading, you should remove that or set to \"false\".\r\n\r\nIf you are hosting your own copy of Bukkit/Spigot you can override the download URLs with:\r\n\r\n- -e BUKKIT_DOWNLOAD_URL=<url>\r\n- -e SPIGOT_DOWNLOAD_URL=<url>\r\n\r\n!!! note\r\n\r\n    Some of the `VERSION` values are not as intuitive as you would think, so make sure to click into the version entry to find the **exact** version needed for the download. For example, \"1.8\" is not sufficient since their download naming expects `1.8-R0.1-SNAPSHOT-latest` exactly.\r\n\r\n## Build from source\r\n\r\nYou can build spigot from source by setting the environment variable `BUILD_FROM_SOURCE` to \"true\".\r\n\r\n## Alternatives\r\n\r\n### Canyon\r\n\r\n[Canyon](https://github.com/canyonmodded/canyon) is a fork of CraftBukkit for Minecraft Beta 1.7.3. It includes multiple enhancements whilst also retaining compatibility with old Bukkit plugins and mods as much as possible.\r\n\r\n    -e VERSION=b1.7.3 -e TYPE=CANYON\r\n\r\n!!! important\r\n    Only `VERSION=b1.7.3` is supported. Since that version pre-dates the health check mechanism used by this image, that will need to be disabled by setting `DISABLE_HEALTHCHECK=true`.\r\n\r\nCanyon is on a temporary hiatus, so by default the final build from GitHub will be used; however, a specific build number can be selected in some instances by setting `CANYON_BUILD`, such as\r\n\r\n    -e CANYON_BUILD=6\r\n    -e CANYON_BUILD=26\r\n\r\n### Poseidon\r\n\r\n[Poseidon](https://github.com/retromcorg/Project-Poseidon) is a fork of CraftBukkit for Minecraft Beta 1.7.3. It includes multiple enhancements whilst also retaining compatibility with old Bukkit plugins.\r\n\r\n    -e VERSION=b1.7.3 -e TYPE=CANYON\r\n\r\n!!! important\r\n    Only `VERSION=b1.7.3` is supported. Since that version pre-dates the health check mechanism used by this image, that will need to be disabled by setting `DISABLE_HEALTHCHECK=true`.\r\n\r\n### Uberbukkit\r\n\r\n[Uberbukkit](https://github.com/Moresteck/uberbukkit) is a fork of CraftBukkit for Minecraft Beta with Multi version support, supports b1.0 - b1.7.3\r\n"
  },
  {
    "path": "docs/types-and-platforms/server-types/fabric.md",
    "content": "A [Fabric server](https://fabricmc.net/) can be automatically downloaded, upgraded, and run by setting the environment variable TYPE to \"FABRIC\"\r\n\r\n!!! example\r\n\r\n    Using `docker run` command line\r\n\r\n    ```shell\r\n    docker run -d --pull=always -e EULA=TRUE -e TYPE=FABRIC -p 25565:25565 itzg/minecraft-server\r\n    ```\r\n    \r\n    In a compose file service:\r\n    \r\n    ```yaml\r\n    environment:\r\n      EULA: TRUE\r\n      TYPE: FABRIC\r\n    ```\r\n\r\nBy default, the container will install the latest [fabric server launcher](https://fabricmc.net/use/server/), using the latest [fabric-loader](https://fabricmc.net/wiki/documentation:fabric_loader) against the minecraft version you have defined with `VERSION` (defaulting to the latest vanilla release of the game).\r\n\r\nA specific loader or launcher version other than the latest can be requested using `FABRIC_LOADER_VERSION` and `FABRIC_LAUNCHER_VERSION` respectively, such as:\r\n\r\n!!! example \"Using launcher and loader versions\"\r\n\r\n    With docker run\r\n\r\n    ```shell\r\n    docker run -d ... \\\r\n        -e TYPE=FABRIC \\\r\n        -e FABRIC_LAUNCHER_VERSION=0.10.2 \\\r\n        -e FABRIC_LOADER_VERSION=0.13.1\r\n    ```\r\n    \r\n    In a compose file service:\r\n    \r\n    ```yaml\r\n    environment:\r\n      EULA: TRUE\r\n      TYPE: FABRIC\r\n      FABRIC_LAUNCHER_VERSION: 0.10.2\r\n      FABRIC_LOADER_VERSION: 0.13.1\r\n    ```\r\n\r\nThe container fetches Fabric loader and game version metadata from the [Fabric meta API](https://meta.fabricmc.net). You can override the base URL with `FABRIC_META_BASE_URL` (default: `https://meta.fabricmc.net`), for example when using a mirror or custom meta endpoint.\r\n\r\n!!! note\r\n\r\n    See the [Working with mods and plugins](../../mods-and-plugins/index.md) section to set up Fabric mods and configuration.\r\n\r\n## Fabric API\r\n\r\nAs [mentioned on the Fabric download page](https://fabricmc.net/use/installer/), most mods will require the Fabric API mod to be installed. That can be easily done by utilizing [the Modrinth downloads feature](../../mods-and-plugins/modrinth.md), such as adding this to the `environment` of a compose file service:\r\n\r\n```yaml\r\n      TYPE: FABRIC\r\n      MODRINTH_PROJECTS: |\r\n        fabric-api\r\n```\r\n\r\n## Alternate launcher\r\n\r\nIf you wish to use an alternative launcher you can:  \r\n\r\n- Provide the path to a custom launcher jar available to the container with `FABRIC_LAUNCHER`, relative to `/data` (such as `-e FABRIC_LAUNCHER=fabric-server-custom.jar`)\r\n- Provide the URL to a custom launcher jar with `FABRIC_LAUNCHER_URL` (such as `-e FABRIC_LAUNCHER_URL=http://HOST/fabric-server-custom.jar`)\r\n\r\n## Force re-install\r\n\r\nIf the Fabric launcher jar becomes corrupted you can temporarily set FABRIC_FORCE_REINSTALL to \"true\" to have it re-installed on next startup."
  },
  {
    "path": "docs/types-and-platforms/server-types/forge.md",
    "content": "A [Forge server](http://www.minecraftforge.net/) can be automatically downloaded, upgraded, and run by setting the environment variable `TYPE` to \"FORGE\".\r\n\r\n!!! note \"A note from the installer\"\r\n\r\n    > Please do not automate the download and installation of Forge.\r\n    Our efforts are supported by ads from the download page.\r\n    If you MUST automate this, please consider supporting the project through <https://www.patreon.com/LexManos/>\r\n\r\n    Since my project also relies on donations, please pass it along and consider contributing to the Patreon above.\r\n\r\n!!! example\r\n\r\n    ```shell\r\n    docker run -e TYPE=FORGE ...\r\n    ```\r\n    \r\n    or in a compose file\r\n    ```yaml\r\n        environment:\r\n          TYPE: FORGE\r\n    ```\r\n\r\nThe overall version is specified by `VERSION`, [as described in the section above](../../versions/minecraft.md) and provides the same benefits of upgrading as new versions are released. By default, the recommended version of Forge for that Minecraft version will be selected. The latest version can be selected instead by setting the environment variable `FORGE_VERSION` to \"latest\". You can also choose a specific Forge version by setting `FORGE_VERSION` with that version, such as \"14.23.5.2854\".\r\n\r\n\r\n!!! example\r\n\r\n    ```shell\r\n    docker run -e TYPE=FORGE -e VERSION=1.12.2 -e FORGE_VERSION=14.23.5.2854 ...\r\n    ```\r\n    \r\n    or in a compose file\r\n    ```yaml\r\n        environment:\r\n          TYPE: FORGE\r\n          VERSION: \"1.12.2\"\r\n          FORGE_VERSION: \"14.23.5.2854\"\r\n    ```\r\n\r\nTo use a pre-downloaded Forge installer, place it in a location mounted into the container and specify the container path with `FORGE_INSTALLER`. To download a Forge installer from a custom location, such as your own file repository, specify the URL with `FORGE_INSTALLER_URL`.\r\n\r\nIn both of the cases above, there is no need for the `VERSION` or `FORGE_VERSION` variables.\r\n\r\n!!! note\r\n\r\n    If an error occurred while installing Forge, it might be possible to resolve by temporarily setting `FORGE_FORCE_REINSTALL` to \"true\". Be sure to remove that variable after successfully starting the server.\r\n\r\nURLs configurable via environment variables:\r\n\r\n- `FORGE_PROMOTIONS_URL`: default is https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json\r\n- `FORGE_MAVEN_REPO_URL`: default is https://maven.minecraftforge.net\r\n\r\n## Alternatives\r\n\r\n### NeoForge\r\n\r\nSupport for [NeoForge](https://neoforged.net/) is also provided. A NeoForge server can be automatically managed by setting `TYPE` to \"NEOFORGE\". `VERSION` specifies the Minecraft version and `NEOFORGE_VERSION` can be set to select a specific version, \"latest\", or \"beta\". By default, the latest, non-beta NeoForge version available for the requested Minecraft version will be used.\r\n\r\n!!! example\r\n\r\n    ```shell\r\n    docker run -e TYPE=NEOFORGE -e VERSION=1.20.1 -e NEOFORGE_VERSION=47.1.79 ...\r\n    ```\r\n    \r\n    or in a compose file\r\n    ```yaml\r\n        environment:\r\n          TYPE: NEOFORGE\r\n          VERSION: \"1.20.4\"\r\n          NEOFORGE_VERSION: \"beta\"\r\n    ```\r\n\r\n### Cleanroom\r\n\r\n[Cleanroom](https://github.com/CleanroomMC/Cleanroom) isn't fully automated, but can be utilized by...\r\n\r\n1. choose the desired release at https://github.com/CleanroomMC/Cleanroom/releases\r\n2. grab the link to the `*-installer.jar` file in that release\r\n3. with `TYPE` set to \"FORGE\", set `FORGE_INSTALLER_URL` to the installer jar's link\r\n\r\n!!! example\r\n\r\n    In docker compose `environment`\r\n    \r\n    ```yaml\r\n      TYPE: FORGE\r\n      FORGE_INSTALLER_URL: https://github.com/CleanroomMC/Cleanroom/releases/download/0.2.4-alpha/cleanroom-0.2.4-alpha-installer.jar\r\n    ```\r\n    \r\n    [Full example](https://github.com/itzg/docker-minecraft-server/tree/master/examples/cleanroom)"
  },
  {
    "path": "docs/types-and-platforms/server-types/hybrids.md",
    "content": "\r\n\r\n### Magma\r\n\r\nA [Magma](https://magmafoundation.org/) server, which is a combination of Forge and PaperMC, can be used with\r\n\r\n    -e TYPE=MAGMA\r\n\r\n!!! note\r\n\r\n    The Magma project has been terminated ([original link died](https://git.magmafoundation.org/magmafoundation/magma-1-20-x/-/commit/4e7abe37403c47d09b74b77bcfc26a19b18f5891), [alternate statement on their discord](https://discord.com/channels/612695539729039411/647287352833605662/1174412642962649198) ). Please use Magma Maintained for 1.12.2, 1.18.2 and 1.19.3, or Ketting for 1.20.1+.\r\n\r\n    There are limited base versions supported, so you will also need to  set `VERSION`, such as \"1.12.2\", \"1.16.5\", etc.\r\n\r\n### Magma Maintained\r\nA [Magma Maintained](https://github.com/magmamaintained/) server, which is a alternative project of Magma, can be used with\r\n\r\n    -e TYPE=MAGMA_MAINTAINED\r\n\r\n!!! note\r\n\r\n    There are limited base versions supported, so you will also need to set `VERSION`, such as \"1.12.2\", \"1.18.2\", \"1.19.3\", or \"1.20.1\".\r\n\r\n    In addition, `FORGE_VERSION` and `MAGMA_MAINTAINED_TAG` must be specified. You can find the supported `FORGE_VERSION` and `MAGMA_MAINTAINED_TAG` in a releases page for each repositories.\r\n\r\n### Ketting\r\n\r\nA [Ketting](https://github.com/kettingpowered/Ketting-1-20-x) server, which is an alternative project of Magma 1.20.1+, can be used with\r\n\r\n    -e TYPE=KETTING\r\n\r\nThere are limited base versions supported, so you will also need to  set `VERSION`, such as \"1.20.1\" or later.\r\n\r\n`FORGE_VERSION` and `KETTING_VERSION` may be specified; however, they will be defaulted by the [Ketting launcher](https://github.com/kettingpowered/kettinglauncher) otherwise.\r\nAvailable Ketting Versions may be found at [https://reposilite.c0d3m4513r.com/#/Ketting-Server-Releases/org/kettingpowered/server/forge](https://reposilite.c0d3m4513r.com/#/Ketting-Server-Releases/org/kettingpowered/server/forge).\r\nThe Version structure is `MinecraftVersion-ForgeVersion-KettingVersion` (e.g. `1.20.1-47.2.20-0.1.4` is for Minecraft `1.20.1`, Forge `47.2.20` and Ketting `0.1.4`).\r\n\r\n### Mohist\r\n\r\nA [Mohist](https://github.com/MohistMC/Mohist) server can be used with\r\n\r\n    -e TYPE=MOHIST\r\n\r\n!!! note\r\n\r\n    There are limited base versions supported, so you will also need to  set `VERSION`, such as \"1.12.2\"\r\n\r\nBy default the latest build will be used; however, a specific build number can be selected by setting `MOHIST_BUILD`, such as\r\n\r\n    -e VERSION=1.16.5 -e MOHIST_BUILD=374\r\n\r\n### Youer\r\n\r\nA [Youer](https://github.com/MohistMC/Youer) server can be used with\r\n\r\n    -e TYPE=YOUER\r\n\r\n!!! note\r\n\r\n    There are limited base versions supported, so you will also need to  set `VERSION`, such as \"1.12.2\"\r\n\r\nBy default the latest build will be used; however, a specific build number can be selected by setting `MOHIST_BUILD`, such as\r\n\r\n    -e VERSION=1.16.5 -e MOHIST_BUILD=374\r\n\r\n### Banner\r\n\r\nA [Banner](https://github.com/MohistMC/Banner) server can be used with\r\n\r\n    -e TYPE=BANNER\r\n\r\n!!! note\r\n\r\n    There are limited base versions supported, so you will also need to  set `VERSION`, such as \"1.12.2\"\r\n\r\nBy default the latest build will be used; however, a specific build number can be selected by setting `MOHIST_BUILD`, such as\r\n\r\n    -e VERSION=1.16.5 -e MOHIST_BUILD=374\r\n\r\n### Arclight\r\n\r\nA [Arclight](https://arclight.izzel.io/) type server can be used with\r\n\r\n    -e TYPE=ARCLIGHT\r\n    -e ARCLIGHT_TYPE=NEOFORGE,FORGE,FABRIC\r\n"
  },
  {
    "path": "docs/types-and-platforms/server-types/others.md",
    "content": "\r\n## SpongeVanilla\r\n\r\nEnable SpongeVanilla server mode by adding a `-e TYPE=SPONGEVANILLA` to your command-line.\r\n\r\nBy default the container will run the latest `STABLE` version.\r\nIf you want to run a specific version, you can add `-e SPONGEVERSION=1.11.2-6.1.0-BETA-19` to your command-line.\r\n\r\nBeware that current [Sponge](https://www.spongepowered.org) `STABLE` versions for Minecraft 1.12 require using [the Java 8 tag](../../versions/java.md):\r\n\r\n``` shell\r\ndocker run -d --pull=always -v /path/on/host:/data -e TYPE=SPONGEVANILLA \\\r\n    -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server:java8-multiarch\r\n```\r\n\r\nYou can also choose to use the `EXPERIMENTAL` branch.\r\nJust change it with `SPONGEBRANCH`, such as:\r\n\r\n``` shell\r\n$ docker run -d --pull=always -v /path/on/host:/data ... \\\r\n    -e TYPE=SPONGEVANILLA -e SPONGEBRANCH=EXPERIMENTAL ...\r\n```\r\n\r\n## Limbo\r\n\r\nA [Limbo](https://github.com/LOOHP/Limbo) server can be run by setting `TYPE` to `LIMBO`.\r\n\r\nConfiguration options with defaults:\r\n\r\n- `LIMBO_BUILD`=LATEST\r\n\r\n  The `VERSION` will be ignored so locate the appropriate value from [here](https://ci.loohpjames.com/job/Limbo/) to match the version expected by clients.\r\n\r\n- `FORCE_REDOWNLOAD`=false\r\n- `LIMBO_SCHEMA_FILENAME`=default.schem\r\n- `LEVEL`=\"Default;${LIMBO_SCHEMA_NAME}\"\r\n\r\n!!! note\r\n\r\n    Instead of using format codes in the MOTD, Limbo requires [JSON chat content](https://minecraft.wiki/w/Raw_JSON_text_format#Java_Edition). If a plain string is provided, which is the default, then it gets converted into the required JSON structure. \r\n\r\n## NanoLimbo\r\n\r\nA [NanoLimbo](https://github.com/BoomEaro/NanoLimbo) server can be run by setting `TYPE` to `NANOLIMBO`.\r\n\r\nNote: it is a fork of the original [NanoLimbo](https://github.com/Nan1t/NanoLimbo) made by Nan1t\r\n\r\nAn alternate Limbo server\r\n\r\n## Crucible\r\n\r\nA [Crucible](https://github.com/CrucibleMC/Crucible) server can be run by setting `TYPE` to `CRUCIBLE`.\r\n\r\nConfiguration options with defaults:\r\n\r\n- `CRUCIBLE_RELEASE`=latest\r\n\r\nCrucible is only available for 1.7.10, so be sure to set `VERSION=1.7.10`.\r\n\r\n## Custom\r\n\r\nTo use a custom server jar or class files, set `TYPE` to \"CUSTOM\" and continue with one of the following options:\r\n\r\nThe custom jar to be used can be set with `CUSTOM_SERVER` as either a URL to download or the path to a file within the container.\r\n\r\nAlternatively, the final `-jar` invocation can be replaced by setting `CUSTOM_JAR_EXEC` to \"`-cp <classpath> <classname>`\" or \"`-jar <jar file>`\" form, such as\r\n\r\n```\r\n-cp worldedit.jar:Carpet-Server.jar net.minecraft.server.MinecraftServer\r\n```\r\n\r\n!!! note\r\n\r\n    When using `docker run` make sure to quote the entire value since it has spaces in it, such as\r\n\r\n        -e CUSTOM_JAR_EXEC=\"-cp worldedit.jar:Carpet-Server.jar net.minecraft.server.MinecraftServer\"\r\n"
  },
  {
    "path": "docs/types-and-platforms/server-types/paper.md",
    "content": "A [PaperMC server](https://papermc.io/) can be automatically downloaded, upgraded, and run by setting the environment variable TYPE to \"PAPER\".\r\n\r\nBy default, the container will find and download the latest build for the `VERSION` chosen. If `VERSION` is not specified, then the latest Minecraft version released by PaperMC is selected. Along with a specific `VERSION`, a specific Paper build can be selected by setting the environment variable `PAPER_BUILD`.\r\n\r\nTo allow for the selection of experimental builds, set `PAPER_CHANNEL` to \"experimental\", otherwise only release/default channel builds are selected.\r\n\r\n!!! example\r\n\r\n    Using `docker run` command line\r\n    \r\n    ```shell\r\n    docker run ... -e TYPE=PAPER ... \r\n    \r\n    docker run ... -e TYPE=PAPER -e VERSION=1.20.6 ... \r\n    \r\n    docker run ... -e TYPE=PAPER -e VERSION=1.20.6 -e PAPER_BUILD=140 ... \r\n    \r\n    docker run ... -e TYPE=PAPER -e PAPER_CHANNEL=experimental ... \r\n    ```\r\n    \r\n    Using a compose file:\r\n    \r\n    ```yaml\r\n    environment:\r\n      TYPE: PAPER\r\n    ```\r\n    \r\n    ```yaml\r\n    environment:\r\n      TYPE: PAPER\r\n      VERSION: 1.20.6\r\n      PAPER_BUILD: 140\r\n    ```\r\n    \r\n    ```yaml\r\n    environment:\r\n      TYPE: PAPER\r\n      PAPER_CHANNEL: experimental\r\n    ```\r\n\r\n!!! tip\r\n\r\n    If you see the following error, it likely means you need to set the env var `PAPER_CHANNEL` to \"experimental\"\r\n    \r\n    ```\r\n    No build found for version 1.21 with channel 'default'\r\n    ```\r\n\r\nIf you are hosting your own copy of Paper you can override the download URL with `PAPER_DOWNLOAD_URL=<url>`.\r\n\r\nIf you have attached a host directory to the `/data` volume, then you can install plugins via the `plugins` subdirectory. You can also [attach a `/plugins` volume](../../mods-and-plugins/index.md#optional-plugins-mods-and-config-attach-points). If you add plugins while the container is running, you'll need to restart it to pick those up.\r\n\r\n[You can also auto-download plugins using `SPIGET_RESOURCES`.](../../mods-and-plugins/spiget.md)\r\n\r\n## Alternatives\r\n\r\n### Pufferfish\r\n\r\nA [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) server, which is \"a highly optimized Paper fork designed for large servers requiring both maximum performance, stability, and \"enterprise\" features.\"\r\n\r\n    -e TYPE=PUFFERFISH\r\n\r\n!!! note\r\n\r\n    The `VERSION` variable is used to select branch latest, 1.18, or 1.17. Use PUFFERFISH_BUILD to really select the SERVER VERSION number.\r\n\r\nExtra variables:\r\n\r\n- `PUFFERFISH_BUILD=lastSuccessfulBuild` : set a specific Pufferfish build to use. Example: selecting build 47 => 1.18.1, or build 50 => 1.18.2 etc\r\n- `FORCE_REDOWNLOAD=false` : set to true to force the located server jar to be re-downloaded\r\n- `USE_FLARE_FLAGS=false` : set to true to add appropriate flags for the built-in [Flare](https://blog.airplane.gg/flare) profiler\r\n\r\n### Purpur\r\n\r\nA [Purpur](https://purpurmc.org/) server, which is \"a drop-in replacement for Paper servers designed for configurability and new, fun, exciting gameplay features.\"\r\n\r\n    -e TYPE=PURPUR\r\n\r\n!!! note\r\n\r\n    The `VERSION` variable is used to lookup a build of Purpur to download\r\n\r\nExtra variables:\r\n\r\n- `PURPUR_BUILD=LATEST` : set a specific Purpur build to use\r\n- `FORCE_REDOWNLOAD=false` : set to true to force the located server jar to be re-downloaded\r\n- `USE_FLARE_FLAGS=false` : set to true to add appropriate flags for the built-in [Flare](https://blog.airplane.gg/flare) profiler\r\n- `PURPUR_DOWNLOAD_URL=<url>` : set URL to download Purpur from custom URL.\r\n\r\n### Leaf\r\n\r\nA [Leaf server](https://www.leafmc.one/) is a Paper fork focused on performance improvements and low-level optimizations for smoother gameplay.\r\n\r\nTo use a Leaf server, set the environment variable `TYPE` to `\"LEAF\"`.\r\n\r\n    -e TYPE=LEAF\r\n\r\n!!! note\r\n\r\n    The `VERSION` variable is used to select the Minecraft version to run.  \r\n    To specify a particular Leaf build, use `LEAF_BUILD`.  \r\n\r\nBy default the latest build will be used; however, a specific build number can be selected by setting `LEAF_BUILD`, such as\r\n\r\n    -e VERSION=1.21.4 -e LEAF_BUILD=441\r\n\r\n### Folia\r\n\r\nA [Folia server](https://papermc.io/software/folia) can be used by setting the environment variable `TYPE` to \"FOLIA\".\r\n\r\nBy default, the container will run the latest experimental build of [Folia server](https://papermc.io/downloads), but you can also choose to run a specific build with `-e FOLIABUILD=26`. The release channel can be changed with the variable `FOLIA_CHANNEL`; however, only experimental builds are available at this time.\r\n\r\n!!! example\r\n\r\n    Using `docker run`\r\n    \r\n    ```shell\r\n    docker run -d --pull=always \\\r\n        -v /path/on/host:/data -e TYPE=FOLIA \\\r\n        -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server\r\n    ```\r\n\r\nIf you are hosting your own copy of Folia you can override the download URL with `FOLIA_DOWNLOAD_URL=<url>`.\r\n\r\nIf you have attached a host directory to the `/data` volume, then you can install plugins via the `plugins` subdirectory. You can also [attach a `/plugins` volume](../../mods-and-plugins/index.md#optional-plugins-mods-and-config-attach-points). If you add plugins while the container is running, you'll need to restart it to pick those up.\r\n\r\n[You can also auto-download plugins using `SPIGET_RESOURCES`.](../../mods-and-plugins/spiget.md)\r\n\r\n!!! note\r\n    The Folia type inherits from the Paper type. Paper's variables will override the Folia ones.\r\n\r\n## Extra config\r\n\r\n- `SKIP_DOWNLOAD_DEFAULTS`: when set to \"true\" startup will entirely skip checking for default Paper/Bukkit/Spigot config files to download\r\n\r\n## Configuration Repositories\r\n\r\nYou can automatically download optimized or custom configuration files (e.g., `bukkit.yml`, `spigot.yml`, `paper-global.yml`, `purpur.yml`) by setting a repository URL. The container will automatically append the Minecraft version to the URL (e.g., `<REPO_URL>/1.21.1/<FILE>`).\r\n\r\n| Server Type | Variable |\r\n| :--- | :--- |\r\n| Paper | `PAPER_CONFIG_REPO` |\r\n| Pufferfish | `PUFFERFISH_CONFIG_REPO` |\r\n| Purpur | `PURPUR_CONFIG_REPO` |\r\n\r\n### Server Properties\r\n\r\nYou can also download a base `server.properties` file using `SERVER_PROPERTIES_REPO_URL`.\r\n\r\n- **Smart Mode**: If the URL does **not** end in `.properties`, the script treats it as a base URL and appends `/${VERSION}/server.properties` (matching the behavior of the config repos).\r\n- **Direct Mode**: If the URL ends in `.properties`, it downloads that specific file.\r\n\r\n**Note**: Environment variables (like `MOTD`, `DIFFICULTY`, `MAX_PLAYERS`) defined in your Docker configuration will **override** values in the downloaded `server.properties` file.\r\n"
  },
  {
    "path": "docs/types-and-platforms/server-types/quilt.md",
    "content": "Enable [Quilt server](https://quiltmc.org/) mode by adding a `-e TYPE=QUILT` to your command-line.\r\n\r\n```shell\r\ndocker run -d --pull=always \\\r\n    -v /path/on/host:/data -e TYPE=QUILT \\\r\n    -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server\r\n```\r\n\r\nBy default, the container will install the latest [quilt server launcher](https://quiltmc.org/install/server/), using the latest [quilt-installer](https://github.com/QuiltMC/quilt-installer) against the minecraft version you have defined with `VERSION` (defaulting to the latest vanilla release of the game).\r\n\r\nA specific loader or installer version other than the latest can be requested using `QUILT_LOADER_VERSION` and `QUILT_INSTALLER_VERSION` respectively, such as:\r\n\r\n```shell\r\ndocker run -d --pull=always \\\r\n    -v /path/on/host:/data ... -e TYPE=QUILT \\\r\n    -e QUILT_LOADER_VERSION=0.16.0 \\\r\n    -e QUILT_INSTALLER_VERSION=0.4.1\r\n```\r\n\r\n!!! note\r\n\r\n    If you wish to use an alternative launcher you can: \r\n\r\n    - Provide the path to a custom launcher jar available to the container with `QUILT_LAUNCHER`, relative to `/data` (such as `-e QUILT_LAUNCHER=quilt-server-custom.jar`)\r\n    - Provide the URL to a custom launcher jar with `QUILT_LAUNCHER_URL` (such as `-e QUILT_LAUNCHER_URL=http://HOST/quilt-server-custom.jar`)\r\n\r\nSee the [Working with mods and plugins](../../mods-and-plugins/index.md) section to set up Quilt mods and configuration.\r\n"
  },
  {
    "path": "docs/variables.md",
    "content": "\r\n!!! warning\r\n\r\n    The variables listed on this page are manually documented and may be out-of-date or inaccurate.\r\n\r\n    All other documentation pages are actively maintained, so please use the search box above to find the desired topic.\r\n\r\n### General options\r\n<table>\r\n    <thead>\r\n        <tr>\r\n            <th>NAME</th>\r\n            <th>DESCRIPTION</th>\r\n            <th>DEFAULT</th>\r\n            <th>REQUIRED</th>\r\n        </tr>\r\n    </thead>\r\n    <tbody>\r\n        <tr>\r\n            <td><code>UID</code></td>\r\n            <td>The linux user id to run as</td>\r\n            <td><code>1000</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>GID</code></td>\r\n            <td>The linux group id to run as</td>\r\n            <td><code>1000</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>MEMORY</code></td>\r\n            <td>The image declares an initial and maximum Java memory-heap limit of 1 GB.</td>\r\n            <td><code>1G</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>INIT_MEMORY</code></td>\r\n            <td>Independently sets the initial heap size</td>\r\n            <td><code>1G</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>MAX_MEMORY</code></td>\r\n            <td>independently sets the max heap size</td>\r\n            <td><code>1G</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>TZ</code></td>\r\n            <td>You can configure the timezone to match yours by setting the TZ environment variable.\r\n\r\nalternatively, you can mount: <code>/etc/localtime:/etc/localtime:ro\r\n\r\n/etc/timezone:/etc/timezone:ro</code>\r\n            </td>\r\n            <td><code>UTC</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>LOG_LEVEL</code></td>\r\n            <td>Root logger level (trace, debug, info, warn, error)</td>\r\n            <td><code>info</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>LOG_CONSOLE_FORMAT</code></td>\r\n            <td>Log4j2 pattern for console output (what you see in <code>docker logs</code>)</td>\r\n            <td><code>[%d{HH:mm:ss}] [%t/%level]: %msg%n</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>LOG_FILE_FORMAT</code></td>\r\n            <td>Log4j2 pattern for file logs (written to <code>logs/latest.log</code>)</td>\r\n            <td><code>[%d{HH:mm:ss}] [%t/%level]: %msg%n</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>LOG_TERMINAL_FORMAT</code></td>\r\n            <td>Log4j2 pattern for interactive terminal console (used with <code>docker attach</code>)</td>\r\n            <td><code>[%d{HH:mm:ss} %level]: %msg%n</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>ROLLING_LOG_FILE_PATTERN</code></td>\r\n            <td>Pattern for rolled/archived log file names</td>\r\n            <td><code>logs/%d{yyyy-MM-dd}-%i.log.gz</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>ROLLING_LOG_MAX_FILES</code></td>\r\n            <td>Maximum number of archived log files to keep</td>\r\n            <td><code>1000</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>ENABLE_ROLLING_LOGS</code></td>\r\n            <td><strong>Legacy option.</strong> Rolling logs are now enabled by default via templated log4j2 configuration. This option is maintained for backward compatibility but only used for error reporting</td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>ENABLE_JMX</code></td>\r\n            <td>To enable remote JMX, such as for profiling with VisualVM or JMC, add the environment variable ENABLE_JMX=true</td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>JMX_HOST</code></td>\r\n            <td>If JMX is enabled, set JMX_HOST to the IP/host running the Docker container, and add a port forwarding of TCP port 7091</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>USE_AIKAR_FLAGS</code></td>\r\n            <td><a href=\"https://aikar.co/2018/07/02/tuning-the-jvm-g1gc-garbage-collector-flags-for-minecraft/\">Aikar has done some research</a> into finding the optimal JVM flags for GC tuning, which becomes more important as more users are connected concurrently</td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>USE_MEOWICE_FLAGS</code></td>\r\n            <td><a href=\"https://github.com/MeowIce/meowice-flags?tab=readme-ov-file#why-would-i-have-to-switch-\">MeowIce has created an updated set of JVM flags</a> based on Aikar's flags but with support for optimizations for Java 17 and above</td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>USE_MEOWICE_GRAALVM_FLAGS</code></td>\r\n            <td>enables MeowIce's flags for GraalVM if USE_MEOWICE_GRAALVM_FLAGS is TRUE</td>\r\n            <td><code>true</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>JVM_OPTS</code></td>\r\n            <td>General JVM options can be passed to the Minecraft Server invocation by passing a <code>JVM_OPTS</code> environment variable. The JVM requires -XX options to precede -X options, so those can be declared in <code>JVM_XX_OPTS</code>. Both variables are space-delimited, raw JVM arguments</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>JVM_XX_OPTS</code></td>\r\n            <td>General JVM options can be passed to the Minecraft Server invocation by passing a <code>JVM_OPTS</code> environment variable. The JVM requires -XX options to precede -X options, so those can be declared in <code>JVM_XX_OPTS</code>. Both variables are space-delimited, raw JVM arguments</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>JVM_DD_OPTS</code></td>\r\n            <td>As a shorthand for passing several system properties as -D arguments, you can instead pass a comma separated list of name=value or name:value pairs with JVM_DD_OPTS. (The colon syntax is provided for management platforms like Plesk that don't allow = inside a value.)</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>EXTRA_ARGS</code></td>\r\n            <td>Arguments that would usually be passed to the jar file (those which are written after the filename)</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>LOG_TIMESTAMP </code></td>\r\n            <td>To include the timestamp with each log set to <code>true</code></td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n    </tbody>\r\n</table>\r\n\r\n### Server\r\n\r\n<table>\r\n    <thead>\r\n        <tr>\r\n            <th>NAME</th>\r\n            <th>DESCRIPTION</th>\r\n            <th>DEFAULT</th>\r\n            <th>REQUIRED</th>\r\n        </tr>\r\n    </thead>\r\n    <tbody>\r\n        <tr>\r\n            <td><code>TYPE</code></td>\r\n            <td>The server <a href=\"../types-and-platforms/\">type</a></td>\r\n            <td><code>VANILLA</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>EULA</code></td>\r\n            <td>You <strong>MUST</strong> set this to <code>true</code></td>\r\n            <td><code>&nbsp;</code></td>\r\n            <td>✅</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>VERSION</code></td>\r\n            <td>The minecraft version</td>\r\n            <td><code>LATEST</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>MOTD</code></td>\r\n            <td>Set the server log in message.</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>DIFFICULTY</code></td>\r\n            <td>The difficulty level. Available values: <code>peaceful</code>,<code>easy</code>,<code>normal</code>,<code>hard</code></td>\r\n            <td><code>easy</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>ICON</code></td>\r\n            <td>The url or file path for the icon image to use for the server. It will be downloaded, scaled, and converted to the proper format.</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>OVERRIDE_ICON</code></td>\r\n            <td>The server icon which has been set doesn't get overridden by default. Set this to <code>TRUE</code> to override the icon</td>\r\n            <td><code>FALSE</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>MODE</code></td>\r\n            <td>Minecraft servers are configured to run in Survival mode by default. You can change the mode using MODE where you can either provide the <a href=\"http://minecraft.wiki/Game_mode#Game_modes\">standard numerical values</a> or the shortcut values:<br />\r\n            <ul>\r\n                <li>creative</li>\r\n                <li>survival</li>\r\n                <li>adventure</li>\r\n                <li>spectator(minecraft 1.8 or later)</li>\r\n            </ul></td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>LEVEL</code></td>\r\n            <td>Maps to <a href=\"https://minecraft.wiki/w/Server.properties#level-name\">the <code>level-name</code> server property</a>. You can either switch between world saves or run multiple containers with different saves by using the LEVEL option</td>\r\n            <td><code>world</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>SERVER_PORT</code></td>\r\n            <td>Maps to <a href=\"https://minecraft.wiki/w/Server.properties#server-port\">the <code>server-port</code> server property</a>. Only change this value if you know what you're doing. It is only needed when using host networking and it is rare that host networking should be used.</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>STOP_SERVER_ANNOUNCE_DELAY</code></td>\r\n            <td>To allow time for players to finish what they're doing during a graceful server shutdown, set <code>STOP_SERVER_ANNOUNCE_DELAY</code> to a number of seconds to delay after an announcement is posted by the server.</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>PROXY</code></td>\r\n            <td>You may configure the use of an HTTP/HTTPS proxy by passing the proxy's URL</td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CONSOLE</code></td>\r\n            <td>Some older versions (pre-1.14) of Spigot required <code>--noconsole</code> to be passed when detaching stdin</td>\r\n            <td><code>TRUE</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>GUI</code></td>\r\n            <td>Some older servers get confused and think that the GUI interface is enabled.</td>\r\n            <td><code>TRUE</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>STOP_DURATION</code></td>\r\n            <td>When the container is signalled to stop, the Minecraft process wrapper will attempt to send a \"stop\" command via RCON or console and waits for the process to gracefully finish.</td>\r\n            <td><code>60</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>SETUP_ONLY</code></td>\r\n            <td>If you are using a host-attached data directory, then you can have the image setup the Minecraft server files and stop prior to launching the server process by setting this to <code>true</code></td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>USE_FLARE_FLAGS</code></td>\r\n            <td>To enable the JVM flags required to fully support the <a href=\"https://blog.airplane.gg/flare\">Flare profiling suite</a>.</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>USE_SIMD_FLAGS</code></td>\r\n            <td>Support for optimized SIMD operation</td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <!-- <tr>\r\n            <td><code></code></td>\r\n            <td></td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr> -->\r\n    </tbody>\r\n</table>\r\n\r\n### Server properties\r\n\r\nThis image maps known server properties as described in [this section](configuration/server-properties.md).\r\n\r\n### Custom resource pack\r\n\r\n<table>\r\n    <thead>\r\n        <tr>\r\n            <th>NAME</th>\r\n            <th>DESCRIPTION</th>\r\n            <th>DEFAULT</th>\r\n            <th>REQUIRED</th>\r\n        </tr>\r\n    </thead>\r\n    <tbody>\r\n        <tr>\r\n            <td><code>RESOURCE_PACK</code></td>\r\n            <td>A link to a custom resource pack</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>RESOURCE_PACK_SHA1</code></td>\r\n            <td>The checksum for the custom resource pack</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>RESOURCE_PACK_ENFORCE</code></td>\r\n            <td>Enforce the resource pack on clients</td>\r\n            <td><code>FALSE</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n    </tbody>\r\n</table>\r\n\r\n### Whitelist\r\n\r\n<table>\r\n    <thead>\r\n        <tr>\r\n            <th>NAME</th>\r\n            <th>DESCRIPTION</th>\r\n            <th>DEFAULT</th>\r\n            <th>REQUIRED</th>\r\n        </tr>\r\n    </thead>\r\n    <tbody>\r\n        <tr>\r\n            <td><code>ENABLE_WHITELIST</code></td>\r\n            <td>Enable the whitelist to manually manage the whitelist</td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>WHITELIST</code></td>\r\n            <td>A list of usernames and/or UUIDs separated by comma</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>WHITELIST_FILE</code></td>\r\n            <td>A url or file path to a whitelist <code>json</code> formatted file.</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>OVERRIDE_WHITELIST</code></td>\r\n            <td>Enforce regeneration of the whitelist on each server startup.</td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n    </tbody>\r\n</table>\r\n\r\n### RCON\r\n\r\n<table>\r\n    <thead>\r\n        <tr>\r\n            <th>NAME</th>\r\n            <th>DESCRIPTION</th>\r\n            <th>DEFAULT</th>\r\n            <th>REQUIRED</th>\r\n        </tr>\r\n    </thead>\r\n    <tbody>\r\n        <tr>\r\n            <td><code>ENABLE_RCON</code></td>\r\n            <td>Enable/disable RCON support; however, be aware that disabling RCON will remove and limit some features, such as interactive and color console support.</td>\r\n            <td><code>true</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>RCON_PASSWORD</code></td>\r\n            <td>You <strong>MUST</strong> change this value</td>\r\n            <td><code>Randomly generated</code></td>\r\n            <td>✅</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>RCON_PORT</code></td>\r\n            <td>The port for RCON</td>\r\n            <td><code>25575</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>BROADCAST_RCON_TO_OPS</code></td>\r\n            <td>Sets broadcast-rcon-to-ops server property</td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n\t\t\t<td><code>RCON_CMDS_STARTUP</code></td>\r\n\t\t\t<td>RCON commands to execute when the server starts.</td>\r\n\t\t\t<td><code></code></td>\r\n\t\t\t<td>⬜️</td>\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td><code>RCON_CMDS_ON_CONNECT</code></td>\r\n\t\t\t<td>RCON commands to execute whenever a client connects to the server.</td>\r\n\t\t\t<td><code></code></td>\r\n\t\t\t<td>⬜️</td>\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td><code>RCON_CMDS_FIRST_CONNECT</code></td>\r\n\t\t\t<td>RCON commands to execute on the first client connection to the server.</td>\r\n\t\t\t<td><code></code></td>\r\n\t\t\t<td>⬜️</td>\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td><code>RCON_CMDS_ON_DISCONNECT</code></td>\r\n\t\t\t<td>RCON commands to execute whenever a client disconnects from the server.</td>\r\n\t\t\t<td><code></code></td>\r\n\t\t\t<td>⬜️</td>\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td><code>RCON_CMDS_LAST_DISCONNECT</code></td>\r\n\t\t\t<td>RCON commands to execute when the last client disconnects from the server.</td>\r\n\t\t\t<td><code></code></td>\r\n\t\t\t<td>⬜️</td>\r\n\t\t</tr>\r\n    </tbody>\r\n</table>\r\n\r\n### Auto-Pause\r\n\r\n<table>\r\n    <thead>\r\n        <tr>\r\n            <th>NAME</th>\r\n            <th>DESCRIPTION</th>\r\n            <th>DEFAULT</th>\r\n            <th>REQUIRED</th>\r\n        </tr>\r\n    </thead>\r\n    <tbody>\r\n        <tr>\r\n            <td><code>ENABLE_AUTOPAUSE</code></td>\r\n            <td>Enable the Autopause functionality</td>\r\n            <td><code>FALSE</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>AUTOPAUSE_TIMEOUT_EST</code></td>\r\n            <td>describes the time between the last client disconnect and the pausing of the process</td>\r\n            <td><code>3600</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>AUTOPAUSE_TIMEOUT_INIT</code></td>\r\n            <td>describes the time between server start and the pausing of the process, when no client connects in-between</td>\r\n            <td><code>600</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>AUTOPAUSE_TIMEOUT_KN</code></td>\r\n            <td>describes the time between knocking of the port (e.g. by the main menu ping) and the pausing of the process, when no client connects in-between</td>\r\n            <td><code>120</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>AUTOPAUSE_PERIOD</code></td>\r\n            <td>describes period of the daemonized state machine, that handles the pausing of the process</td>\r\n            <td><code>10</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>AUTOPAUSE_KNOCK_INTERFACE</code></td>\r\n            <td>Describes the interface passed to the knockd daemon. If the default interface does not work, run the ifconfig command inside the container and derive the interface receiving the incoming connection from its output. The passed interface must exist inside the container. Using the loopback interface (lo) does likely not yield the desired results.</td>\r\n            <td><code>eth0</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>DEBUG_AUTOPAUSE</code></td>\r\n            <td>Adds additional debugging output for AutoPause</td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n    </tbody>\r\n</table>\r\n\r\n\r\n\r\n### Auto-Stop\r\n\r\n!!! note\r\n\r\n    AutoStop function is incompatible with the Autopause functionality, as they basically cancel out each other.\r\n\r\n<table>\r\n    <thead>\r\n        <tr>\r\n            <th>NAME</th>\r\n            <th>DESCRIPTION</th>\r\n            <th>DEFAULT</th>\r\n            <th>REQUIRED</th>\r\n        </tr>\r\n    </thead>\r\n    <tbody>\r\n        <tr>\r\n            <td><code>ENABLE_AUTOSTOP</code></td>\r\n            <td>Enable the AutoStop functionality</td>\r\n            <td><code>FALSE</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>AUTOSTOP_TIMEOUT_EST</code></td>\r\n            <td>describes the time between the last client disconnect and the stopping of the server</td>\r\n            <td><code>3600</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>AUTOSTOP_TIMEOUT_INIT</code></td>\r\n            <td>describes the time between server start and the stopping of the server, when no client connects in-between</td>\r\n            <td><code>1800</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>AUTOSTOP_PERIOD</code></td>\r\n            <td>describes period of the daemonized state machine, that handles the stopping of the serve</td>\r\n            <td><code>10</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>DEBUG_AUTOSTOP</code></td>\r\n            <td>Adds additional logging for AutoStop</td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n    </tbody>\r\n</table>\r\n\r\n\r\n### CurseForge\r\n\r\n!!! tip\r\n\r\n    Refer to the [main documentation page](types-and-platforms/mod-platforms/auto-curseforge.md) for more details and up-to-date information.\r\n\r\n<table>\r\n    <thead>\r\n        <tr>\r\n            <th>NAME</th>\r\n            <th>DESCRIPTION</th>\r\n            <th>DEFAULT</th>\r\n            <th>REQUIRED</th>\r\n        </tr>\r\n    </thead>\r\n    <tbody>\r\n        <tr>\r\n            <td><code>CF_API_KEY</code></td>\r\n            <td><strong>YOUR</strong> CurseForge (Eternal) API Key.</td>\r\n            <td><code></code></td>\r\n            <td>✅</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_API_KEY_FILE</code></td>\r\n            <td>A path to a file inside of container that contains <strong>YOUR</strong> CurseForge (Eternal) API Key.</td>\r\n            <td><code></code></td>\r\n            <td>✅</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_PAGE_URL</code></td>\r\n            <td>Pass a page URL to the modpack or a specific file</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_SLUG</code></td>\r\n            <td>Instead of a URL, the modpack slug can be provided.</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_FILE_ID</code></td>\r\n            <td>The mod curseforge numerical ID.</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_FILENAME_MATCHER</code></td>\r\n            <td>Specify a substring to match the desired filename</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_EXCLUDE_INCLUDE_FILE</code></td>\r\n            <td>Global and per modpack exclusions can be declared in a JSON file and referenced with this variable. <br /><br />By default, the <a href=\"https://github.com/itzg/docker-minecraft-server/blob/master/files/cf-exclude-include.json\">file bundled with the image</a> will be used, but can be disabled by setting this to an empty string. The schema of this file is <a href=\"https://github.com/itzg/mc-image-helper#excludeinclude-file-schema\">documented here</a>.</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_EXCLUDE_MODS</code></td>\r\n            <td>Mods can be excluded by passing a comma or space delimited list of project slugs or IDs</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_FORCE_INCLUDE_MODS</code></td>\r\n            <td>Mods can be included by passing a comma or space delimited list of project slugs or IDs</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_FORCE_SYNCHRONIZE</code></td>\r\n            <td>Forces the excludes/includes to be re-evaluated</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_SET_LEVEL_FROM</code></td>\r\n            <td>Some modpacks come with world/save data via a worlds file and/or the overrides provided with the modpack. Either approach can be selected to set the LEVEL to the resulting saves directory by setting this to either:\r\n            <ul>\r\n                <li>WORLD_FILE</li>\r\n                <li>OVERRIDES</li>\r\n            </ul></td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_PARALLEL_DOWNLOADS</code></td>\r\n            <td>specify how many parallel mod downloads to perform</td>\r\n            <td><code>4</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_OVERRIDES_SKIP_EXISTING</code></td>\r\n            <td>if set, files in the overrides that already exist in the data directory are skipped. world data is always skipped, if present.</td>\r\n            <td><code>false</code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n        <tr>\r\n            <td><code>CF_MOD_LOADER_VERSION</code></td>\r\n            <td>Override the mod loader version declared by the modpack</td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr>\r\n    </tbody>\r\n</table>\r\n\r\n<!-- ✅ ⬜️ -->\r\n<!-- <tr>\r\n            <td><code></code></td>\r\n            <td></td>\r\n            <td><code></code></td>\r\n            <td>⬜️</td>\r\n        </tr> -->\r\n"
  },
  {
    "path": "docs/versions/java.md",
    "content": "## Image tags\r\n\r\nImage references can either omit the tag, which implies the tag `latest`, such as\r\n\r\n    itzg/minecraft-server\r\n\r\nor explicitly include the tag, such as\r\n\r\n    itzg/minecraft-server:<tag>\r\n\r\nwhere `<tag>` refers to the first column of this table:\r\n\r\n| Tag            | Java version | Linux  | JVM Type           | Architecture        | Note |\r\n|----------------|--------------|--------|--------------------|---------------------|------|\r\n| latest         | 25           | Ubuntu | Hotspot            | amd64, arm64        |      |\r\n| stable         | 25           | Ubuntu | Hotspot            | amd64, arm64        |      |\r\n| java25         | 25           | Ubuntu | Hotspot            | amd64, arm64        |      |\r\n| java25-alpine  | 25           | Alpine | Hotspot            | amd64, arm64        |      |\r\n| java25-jdk     | 25           | Ubuntu | Hotspot+JDK        | amd64, arm64        |      |\r\n| java21         | 21           | Ubuntu | Hotspot            | amd64, arm64        |      |\r\n| java21-jdk     | 21           | Ubuntu | Hotspot+JDK        | amd64, arm64        |      |\r\n| java21-alpine  | 21           | Alpine | Hotspot            | amd64, arm64        |      |\r\n| java17         | 17           | Ubuntu | Hotspot            | amd64, arm64, armv7 |      |\r\n| java16         | 16           | Ubuntu | Hotspot            | amd64, arm64, armv7 | (1)  |\r\n| java11         | 11           | Ubuntu | Hotspot            | amd64, arm64, armv7 |      |\r\n| java8          | 8            | Ubuntu | Hotspot            | amd64, arm64, armv7 |      |\r\n\r\nNotes\r\n\r\n1. This version of Java is [recommended for PaperMC 1.16.5](https://docs.papermc.io/paper/getting-started/#requirements)\r\n\r\n!!! example \"Example using java8\"\r\n\r\n    With docker run command-line\r\n    \r\n    ```\r\n    docker run -it -e EULA=true itzg/minecraft-server:java8\r\n    ```\r\n    \r\n    or in a compose file\r\n    \r\n    ```yaml\r\n    services:\r\n      mc:\r\n        image: itzg/minecraft-server:java8\r\n    ```\r\n\r\n!!! note \"Latest\"\r\n\r\n    The \"latest\" tag shifts to include not only the latest features and bug fixes, but also the latest Java version that Mojang requires for the latest Minecraft version.\r\n\r\n!!! tip \"Class File Version\"\r\n\r\n    If the Minecraft startup is logging an error about a \"class file version\", then refer [to this table](https://javaalmanac.io/bytecode/versions/) to identify the JDK/Java version corresponding to each class file version.\r\n\r\n### Release versions\r\n\r\nSince the tags referenced above will shift as the newest image build brings in new features and bug fixes, released variants of those can also be used to pin a specific build of the image.\r\n\r\nThe syntax of released image tags is:\r\n\r\n    itzg/minecraft-server:<release>-<java tag>\r\n\r\nwhere `java tag` still refers to the first column of the table above and `release` refers to [one of the image releases](https://github.com/itzg/docker-minecraft-server/releases).\r\n\r\n!!! example\r\n\r\n    For example, the 2024.4.0 release of the Java 17 image would be\r\n    \r\n    ```\r\n    itzg/minecraft-server:2024.4.0-java17\r\n    ```\r\n\r\n### Stable image tag\r\n\r\nThe `stable` image tag combines the benefits of `latest` and [release versions](#release-versions) since it shifts to refer to the most recently released version. There is also a per-variant stable tag, formatted as `stable-{variant}`.\r\n\r\n## Version compatibilities\r\n\r\n[This section in the Minecraft wiki](https://minecraft.wiki/w/Tutorials/Update_Java#Why_update?) lists out versions of **vanilla** Minecraft versions and the corresponding Java version that is required.\r\n\r\n### Class file version 65.0\r\n\r\nIf encountering a startup failure similar to the following examples, then ensure that the latest image has been re-pulled to use a Java 21. Alternatively, set the image tag specifically to `itzg/minecraft-server:java21`.\r\n\r\n> Exception in thread \"ServerMain\" java.lang.UnsupportedClassVersionError: org/bukkit/craftbukkit/Main has been compiled by a more recent version of the Java Runtime (class file version 65.0), this version of the Java Runtime only recognizes class file versions up to 61.0\r\n\r\nor\r\n\r\n> Error: LinkageError occurred while loading main class net.minecraft.bundler.Main\r\njava.lang.UnsupportedClassVersionError: net/minecraft/bundler/Main has been compiled by a more recent version of the Java Runtime (class file version 65.0), this version of the Java Runtime only recognizes class file versions up to 61.0\r\n\r\n\r\n### Forge versions\r\n\r\nForge and its mods sometimes utilize non-public APIs of the JVM and as such are suspceptible to becoming broken with newer Java versions.\r\n\r\n#### Java 21\r\n\r\nSome mods even up to Minecraft 1.21 require Java 17 and will not run on the latest Java version. If you see an error like the following then be sure to explicitly use a Java 17 tagged image:\r\n\r\n```\r\nCaused by: org.spongepowered.asm.mixin.throwables.ClassMetadataNotFoundException: java.util.List\r\n\tat MC-BOOTSTRAP/org.spongepowered.mixin/org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.transformMethod(MixinPreProcessorStandard.java:754)\r\n```\r\n\r\n#### Java 8\r\n\r\nFor Forge versions less than 1.18, you _must_ use the `java8-multiarch` (or other java8) image tag.\r\n\r\nIn general, if you see the following line in a server startup failure, then it means you need to be using Java 8 instead of the latest image Java version:\r\n\r\n```\r\nCaused by: java.lang.ClassCastException: class jdk.internal.loader.ClassLoaders$AppClassLoader \r\n   cannot be cast to class java.net.URLClassLoader\r\n```\r\n\r\nForge also doesn't support openj9 JVM implementation.\r\n\r\n## Deprecated Image Tags\r\n\r\nThe following image tags have been deprecated and are no longer receiving updates:\r\n\r\n- java25-graalvm, java21-graalvm, java17-graalvm\r\n- adopt13\r\n- adopt14\r\n- adopt15\r\n- openj9-nightly\r\n- multiarch-latest\r\n- java16-openj9\r\n- java17-graalvm-ce\r\n- java17-openj9\r\n- java17-alpine\r\n- java19\r\n- java20-graalvm, java20, java20-alpine\r\n- java23-*\r\n- java24, java24-graalvm \r\n- java8-multiarch is still built and pushed, but please move to java8 instead\r\n- java8-alpine, java8-jdk, java8-openj9, java8-graalvm-ce\r\n\r\n## JSON Listing\r\n\r\n\r\nInformation about the image tags is available for programmatic access at <https://raw.githubusercontent.com/itzg/docker-minecraft-server/refs/heads/master/images.json>\r\n"
  },
  {
    "path": "docs/versions/minecraft.md",
    "content": "To use a different Minecraft version, pass the `VERSION` environment variable (case sensitive), which can have the value\r\n\r\n- `LATEST` for latest release (the default)\r\n- `SNAPSHOT` for latest snapshot\r\n- a specific version, such as `1.7.9`, `25w35a`, `26.1`, or `26.1-snapshot-1`\r\n- or an alpha and beta version, such as \"b1.7.3\" (server download might not exist)\r\n\r\nFor example, to use the latest snapshot:\r\n\r\n```\r\ndocker run -d -e VERSION=SNAPSHOT ...\r\n```\r\n\r\nor a specific version:\r\n\r\n```\r\ndocker run -d -e VERSION=1.7.9 ...\r\n```\r\n\r\nWhen using \"LATEST\" or \"SNAPSHOT\" an upgrade can be performed by simply restarting the container.\r\nDuring the next startup, if a newer version is available from the respective release channel, then\r\nthe new server jar file is downloaded and used. \r\n\r\n!!! note\r\n\r\n    Over time you might see older versions of the server jar remain in the `/data` directory. It is safe to remove those.\r\n"
  },
  {
    "path": "examples/.gitignore",
    "content": "data/\r\nmodpacks/\r\n.env\r\ndownloads/"
  },
  {
    "path": "examples/README.md",
    "content": "# Deployment Examples\n\nThis directory contains various deployment examples of the [itzg/minecraft-server](https://hub.docker.com/r/itzg/minecraft-server/) Docker image."
  },
  {
    "path": "examples/apply-extra-configs/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"TRUE\"\n      TYPE: PAPER\n      MAX_MEMORY: 4G\n      MODRINTH_PROJECTS: |\n        luckperms\n      APPLY_EXTRA_FILES: |\n        plugins/LuckPerms</shared-configs/LuckPerms/\n      DEBUG: true\n    volumes:\n      - data:/data\n      - ./shared-configs:/shared-configs:ro\n    ports:\n      - \"25565:25565\"\nvolumes:\n  data:"
  },
  {
    "path": "examples/apply-extra-configs/shared-configs/LuckPerms/config.yml",
    "content": "####################################################################################################\n# LOCALLY MODIFIED\n####################################################################################################\n# +----------------------------------------------------------------------------------------------+ #\n# |                                   __         __   ___  __         __                         | #\n# |                        |    |  | /  ` |__/  |__) |__  |__)  |\\/| /__`                        | #\n# |                        |___ \\__/ \\__, |  \\  |    |___ |  \\  |  | .__/                        | #\n# |                                                                                              | #\n# |                                     https://luckperms.net                                    | #\n# |                                                                                              | #\n# |  WIKI:        https://luckperms.net/wiki                                                     | #\n# |  DISCORD:     https://discord.gg/luckperms                                                   | #\n# |  BUG REPORTS: https://github.com/LuckPerms/LuckPerms/issues                                  | #\n# |                                                                                              | #\n# |  Each option in this file is documented and explained here:                                  | #\n# |   ==>  https://luckperms.net/wiki/Configuration                                              | #\n# |                                                                                              | #\n# |  New options are not added to this file automatically. Default values are used if an         | #\n# |  option cannot be found. The latest config versions can be obtained at the link above.       | #\n# +----------------------------------------------------------------------------------------------+ #\n####################################################################################################\n\n# +----------------------------------------------------------------------------------------------+ #\n# |                                                                                              | #\n# |                                      ESSENTIAL SETTINGS                                      | #\n# |                                                                                              | #\n# |                   Important settings that control how LuckPerms functions.                   | #\n# |                                                                                              | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# The name of the server, used for server specific permissions.\n#\n# - When set to \"global\" this setting is effectively ignored.\n# - In all other cases, the value here is added to all players in a \"server\" context.\n# - See: https://luckperms.net/wiki/Context\nserver: global\n\n# If the servers own UUID cache/lookup facility should be used when there is no record for a player\n# already in LuckPerms.\n#\n# - When this is set to 'false', commands using a player's username will not work unless the player\n#   has joined since LuckPerms was first installed.\n# - To get around this, you can use a player's uuid directly in the command, or enable this option.\n# - When this is set to 'true', the server facility is used. This may use a number of methods,\n#   including checking the servers local cache, or making a request to the Mojang API.\nuse-server-uuid-cache: false\n\n\n\n\n# +----------------------------------------------------------------------------------------------+ #\n# |                                                                                              | #\n# |                                       STORAGE SETTINGS                                       | #\n# |                                                                                              | #\n# |                Controls which storage method LuckPerms will use to store data.               | #\n# |                                                                                              | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# How the plugin should store data\n#\n# - The various options are explained in more detail on the wiki:\n#   https://luckperms.net/wiki/Storage-types\n#\n# - Possible options:\n#\n#   |  Remote databases - require connection information to be configured below\n#   |=> MySQL\n#   |=> MariaDB (preferred over MySQL)\n#   |=> PostgreSQL\n#   |=> MongoDB\n#\n#   |  Flatfile/local database - don't require any extra configuration\n#   |=> H2 (preferred over SQLite)\n#   |=> SQLite\n#\n#   |  Readable & editable text files - don't require any extra configuration\n#   |=> YAML (.yml files)\n#   |=> JSON (.json files)\n#   |=> HOCON (.conf files)\n#   |=> TOML (.toml files)\n#   |\n#   | By default, user, group and track data is separated into different files. Data can be combined\n#   | and all stored in the same file by switching to a combined storage variant.\n#   | Just add '-combined' to the end of the storage-method, e.g. 'yaml-combined'\n#\n# - A H2 database is the default option.\n# - If you want to edit data manually in \"traditional\" storage files, we suggest using YAML.\nstorage-method: h2\n\n# The following block defines the settings for remote database storage methods.\n#\n# - You don't need to touch any of the settings here if you're using a local storage method!\n# - The connection detail options are shared between all remote storage types.\ndata:\n\n  # Define the address and port for the database.\n  # - The standard DB engine port is used by default\n  #   (MySQL: 3306, PostgreSQL: 5432, MongoDB: 27017)\n  # - Specify as \"host:port\" if differs\n  address: localhost\n\n  # The name of the database to store LuckPerms data in.\n  # - This must be created already. Don't worry about this setting if you're using MongoDB.\n  database: minecraft\n\n  # Credentials for the database.\n  username: root\n  password: ''\n\n  # These settings apply to the MySQL connection pool.\n  # - The default values will be suitable for the majority of users.\n  # - Do not change these settings unless you know what you're doing!\n  pool-settings:\n\n    # Sets the maximum size of the MySQL connection pool.\n    # - Basically this value will determine the maximum number of actual\n    #   connections to the database backend.\n    # - More information about determining the size of connection pools can be found here:\n    #   https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing\n    maximum-pool-size: 10\n\n    # Sets the minimum number of idle connections that the pool will try to maintain.\n    # - For maximum performance and responsiveness to spike demands, it is recommended to not set\n    #   this value and instead allow the pool to act as a fixed size connection pool.\n    #   (set this value to the same as 'maximum-pool-size')\n    minimum-idle: 10\n\n    # This setting controls the maximum lifetime of a connection in the pool in milliseconds.\n    # - The value should be at least 30 seconds less than any database or infrastructure imposed\n    #   connection time limit.\n    maximum-lifetime: 1800000 # 30 minutes\n\n    # This setting controls how frequently the pool will 'ping' a connection in order to prevent it\n    # from being timed out by the database or network infrastructure, measured in milliseconds.\n    # - The value should be less than maximum-lifetime and greater than 30000 (30 seconds).\n    # - Setting the value to zero will disable the keepalive functionality.\n    keepalive-time: 0\n\n    # This setting controls the maximum number of milliseconds that the plugin will wait for a\n    # connection from the pool, before timing out.\n    connection-timeout: 5000 # 5 seconds\n\n    # This setting allows you to define extra properties for connections.\n    #\n    # By default, the following options are set to enable utf8 encoding. (you may need to remove\n    # these if you are using PostgreSQL)\n    #   useUnicode: true\n    #   characterEncoding: utf8\n    #\n    # You can also use this section to disable SSL connections, by uncommenting the 'useSSL' and\n    # 'verifyServerCertificate' options below.\n    properties:\n      useUnicode: true\n      characterEncoding: utf8\n      #useSSL: false\n      #verifyServerCertificate: false\n\n  # The prefix for all LuckPerms SQL tables.\n  #\n  # - This only applies for remote SQL storage types (MySQL, MariaDB, etc).\n  # - Change this if you want to use different tables for different servers.\n  table-prefix: 'luckperms_'\n\n  # The prefix to use for all LuckPerms MongoDB collections.\n  #\n  # - This only applies for the MongoDB storage type.\n  # - Change this if you want to use different collections for different servers. The default is no\n  # prefix.\n  mongodb-collection-prefix: ''\n\n  # The connection string URI to use to connect to the MongoDB instance.\n  #\n  # - When configured, this setting will override anything defined in the address, database,\n  #   username or password fields above.\n  # - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it\n  #   below.\n  # - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/\n  mongodb-connection-uri: ''\n\n# Define settings for a \"split\" storage setup.\n#\n# - This allows you to define a storage method for each type of data.\n# - The connection options above still have to be correct for each type here.\nsplit-storage:\n  # Don't touch this if you don't want to use split storage!\n  enabled: false\n  methods:\n    # These options don't need to be modified if split storage isn't enabled.\n    user: h2\n    group: h2\n    track: h2\n    uuid: h2\n    log: h2\n\n\n\n\n# +----------------------------------------------------------------------------------------------+ #\n# |                                                                                              | #\n# |                            UPDATE PROPAGATION & MESSAGING SERVICE                            | #\n# |                                                                                              | #\n# |    Controls the ways in which LuckPerms will sync data & notify other servers of changes.    | #\n# |     These options are documented on greater detail on the wiki under \"Instant Updates\".      | #\n# |                                                                                              | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# This option controls how frequently LuckPerms will perform a sync task.\n#\n# - A sync task will refresh all data from the storage, and ensure that the most up-to-date data is\n#   being used by the plugin.\n# - This is disabled by default, as most users will not need it. However, if you're using a remote\n#   storage type without a messaging service setup, you may wish to set this to something like 3.\n# - Set to -1 to disable the task completely.\nsync-minutes: -1\n\n# If the file watcher should be enabled.\n#\n# - When using a file-based storage type, LuckPerms can monitor the data files for changes, and\n#   automatically update when changes are detected.\n# - If you don't want this feature to be active, set this option to false.\nwatch-files: true\n\n# Define which messaging service should be used by the plugin.\n#\n# - If enabled and configured, LuckPerms will use the messaging service to inform other connected\n#   servers of changes.\n# - Use the command \"/lp networksync\" to manually push changes.\n# - Data is NOT stored using this service. It is only used as a messaging platform.\n#\n# - If you decide to enable this feature, you should set \"sync-minutes\" to -1, as there is no need\n#   for LuckPerms to poll the database for changes.\n#\n# - Possible options:\n#   => sql       Uses the SQL database to form a queue system for communication. Will only work when\n#                'storage-method' is set to MySQL or MariaDB. This is chosen by default if the\n#                option is set to 'auto' and SQL storage is in use. Set to 'notsql' to disable this.\n#   => pluginmsg Uses the plugin messaging channels to communicate with the proxy.\n#                LuckPerms must be installed on your proxy & all connected servers backend servers.\n#                Won't work if you have more than one proxy.\n#   => lilypad   Uses LilyPad pub-sub to push changes. You need to have the LilyPad-Connect plugin\n#                installed.\n#   => redis     Uses Redis pub-sub to push changes. Your server connection info must be configured\n#                below.\n#   => rabbitmq  Uses RabbitMQ pub-sub to push changes. Your server connection info must be\n#                configured below.\n#   => nats      Uses Nats pub-sub to push changes. Your server connection info must be\n#                configured below.\n#   => custom    Uses a messaging service provided using the LuckPerms API.\n#   => auto      Attempts to automatically setup a messaging service using redis or sql.\nmessaging-service: auto\n\n# If LuckPerms should automatically push updates after a change has been made with a command.\nauto-push-updates: true\n\n# If LuckPerms should push logging entries to connected servers via the messaging service.\npush-log-entries: true\n\n# If LuckPerms should broadcast received logging entries to players on this platform.\n#\n# - If you have LuckPerms installed on your backend servers as well as a BungeeCord proxy, you\n#   should set this option to false on either your backends or your proxies, to avoid players being\n#   messaged twice about log entries.\nbroadcast-received-log-entries: true\n\n# Settings for Redis.\n# Port 6379 is used by default; set address to \"host:port\" if differs\n# Multiple Redis nodes can be specified in the same format as a string list under the name \"addresses\".\nredis:\n  enabled: false\n  address: localhost\n  username: ''\n  password: ''\n\n# Settings for Nats.\n# Port 4222 is used by default; set address to \"host:port\" if differs\nnats:\n  enabled: false\n  address: localhost\n  username: ''\n  password: ''\n\n# Settings for RabbitMQ.\n# Port 5672 is used by default; set address to \"host:port\" if differs\nrabbitmq:\n  enabled: false\n  address: localhost\n  vhost: '/'\n  username: 'guest'\n  password: 'guest'\n\n\n\n\n# +----------------------------------------------------------------------------------------------+ #\n# |                                                                                              | #\n# |                                    CUSTOMIZATION SETTINGS                                    | #\n# |                                                                                              | #\n# |              Settings that allow admins to customize the way LuckPerms operates.             | #\n# |                                                                                              | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# Controls how temporary permissions/parents/meta should be accumulated.\n#\n# - The default behaviour is \"deny\".\n# - This behaviour can also be specified when the command is executed. See the command usage\n#   documentation for more info.\n#\n# - Possible options:\n#   => accumulate   durations will be added to the existing expiry time\n#   => replace      durations will be replaced if the new duration is later than the current\n#                   expiration\n#   => deny         the command will just fail if you try to add another node with the same expiry\ntemporary-add-behaviour: deny\n\n# Controls how LuckPerms will determine a users \"primary\" group.\n#\n# - The meaning and influence of \"primary groups\" are explained in detail on the wiki.\n# - The preferred approach is to let LuckPerms automatically determine a users primary group\n#   based on the relative weight of their parent groups.\n#\n# - Possible options:\n#   => stored                  use the value stored against the users record in the file/database\n#   => parents-by-weight       just use the users most highly weighted parent\n#   => all-parents-by-weight   same as above, but calculates based upon all parents inherited from\n#                              both directly and indirectly\nprimary-group-calculation: parents-by-weight\n\n# If the plugin should check for \"extra\" permissions with users run LP commands.\n#\n# - These extra permissions allow finer control over what users can do with each command, and who\n#   they have access to edit.\n# - The nature of the checks are documented on the wiki under \"Argument based command permissions\".\n# - Argument based permissions are *not* static, unlike the 'base' permissions, and will depend upon\n#   the arguments given within the command.\nargument-based-command-permissions: false\n\n# If the plugin should check whether senders are a member of a given group before they're able to\n# edit the groups data or add/remove other users to/from it.\n# Note: these limitations do not apply to the web editor!\nrequire-sender-group-membership-to-modify: false\n\n# If the plugin should send log notifications to users whenever permissions are modified.\n#\n# - Notifications are only sent to those with the appropriate permission to receive them\n# - They can also be temporarily enabled/disabled on a per-user basis using\n#   '/lp log notify <on|off>'\nlog-notify: true\n\n# Defines a list of log entries which should not be sent as notifications to users.\n#\n# - Each entry in the list is a RegEx expression which is matched against the log entry description.\nlog-notify-filtered-descriptions:\n#  - \"parent add example\"\n\n# If LuckPerms should automatically install translation bundles and periodically update them.\nauto-install-translations: true\n\n# Defines the options for prefix and suffix stacking.\n#\n# - The feature allows you to display multiple prefixes or suffixes alongside a players username in\n#   chat.\n# - It is explained and documented in more detail on the wiki under \"Prefix & Suffix Stacking\".\n#\n# - The options are divided into separate sections for prefixes and suffixes.\n# - The 'duplicates' setting refers to how duplicate elements are handled. Can be 'retain-all',\n#   'first-only' or 'last-only'.\n# - The value of 'start-spacer' is included at the start of the resultant prefix/suffix.\n# - The value of 'end-spacer' is included at the end of the resultant prefix/suffix.\n# - The value of 'middle-spacer' is included between each element in the resultant prefix/suffix.\n#\n# - Possible format options:\n#   => highest                        Selects the value with the highest weight, from all values\n#                                     held by or inherited by the player.\n#\n#   => lowest                         Same as above, except takes the one with the lowest weight.\n#\n#   => highest_own                    Selects the value with the highest weight, but will not\n#                                     accept any inherited values.\n#\n#   => lowest_own                     Same as above, except takes the value with the lowest weight.\n#\n#   => highest_inherited              Selects the value with the highest weight, but will only\n#                                     accept inherited values.\n#\n#   => lowest_inherited               Same as above, except takes the value with the lowest weight.\n#\n#   => highest_on_track_<track>       Selects the value with the highest weight, but only if the\n#                                     value was inherited from a group on the given track.\n#\n#   => lowest_on_track_<track>        Same as above, except takes the value with the lowest weight.\n#\n#   => highest_not_on_track_<track>   Selects the value with the highest weight, but only if the\n#                                     value was inherited from a group not on the given track.\n#\n#   => lowest_not_on_track_<track>    Same as above, except takes the value with the lowest weight.\n#\n#   => highest_from_group_<group>     Selects the value with the highest weight, but only if the\n#                                     value was inherited from the given group.\n#\n#   => lowest_from_group_<group>      Same as above, except takes the value with the lowest weight.\n#\n#   => highest_not_from_group_<group> Selects the value with the highest weight, but only if the\n#                                     value was not inherited from the given group.\n#\n#   => lowest_not_from_group_<group>  Same as above, except takes the value with the lowest weight.\nmeta-formatting:\n  prefix:\n    format:\n      - \"highest\"\n    duplicates: first-only\n    start-spacer: \"\"\n    middle-spacer: \" \"\n    end-spacer: \"\"\n  suffix:\n    format:\n      - \"highest\"\n    duplicates: first-only\n    start-spacer: \"\"\n    middle-spacer: \" \"\n    end-spacer: \"\"\n\n\n\n\n# +----------------------------------------------------------------------------------------------+ #\n# |                                                                                              | #\n# |                            PERMISSION CALCULATION AND INHERITANCE                            | #\n# |                                                                                              | #\n# |    Modify the way permission checks, meta lookups and inheritance resolutions are handled.   | #\n# |                                                                                              | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# The algorithm LuckPerms should use when traversing the \"inheritance tree\".\n#\n# - Possible options:\n#   => breadth-first            See: https://en.wikipedia.org/wiki/Breadth-first_search\n#   => depth-first-pre-order    See: https://en.wikipedia.org/wiki/Depth-first_search\n#   => depth-first-post-order   See: https://en.wikipedia.org/wiki/Depth-first_search\ninheritance-traversal-algorithm: depth-first-pre-order\n\n# If a final sort according to \"inheritance rules\" should be performed after the traversal algorithm\n# has resolved the inheritance tree.\n#\n# \"Inheritance rules\" refers to things such as group weightings, primary group status, and the\n# natural contextual ordering of the group nodes.\n#\n# Setting this to 'true' will allow for the inheritance rules to take priority over the structure of\n# the inheritance tree.\n#\n# Effectively when this setting is 'true': the tree is flattened, and rules applied afterwards,\n# and when this setting is 'false':, the rules are just applied during each step of the traversal.\npost-traversal-inheritance-sort: false\n\n# Defines the mode used to determine whether a set of contexts are satisfied.\n#\n# - Possible options:\n#   => at-least-one-value-per-key   Set A will be satisfied by another set B, if at least one of the\n#                                   key-value entries per key in A are also in B.\n#   => all-values-per-key           Set A will be satisfied by another set B, if all key-value\n#                                   entries in A are also in B.\ncontext-satisfy-mode: at-least-one-value-per-key\n\n# LuckPerms has a number of built-in contexts. These can be disabled by adding the context key to\n# the list below.\ndisabled-contexts:\n#  - \"world\"\n\n# +----------------------------------------------------------------------------------------------+ #\n# | Permission resolution settings                                                               | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# If users on this server should have their global permissions applied.\n# When set to false, only server specific permissions will apply for users on this server\ninclude-global: true\n\n# If users on this server should have their global world permissions applied.\n# When set to false, only world specific permissions will apply for users on this server\ninclude-global-world: true\n\n# If users on this server should have global (non-server specific) groups applied\napply-global-groups: true\n\n# If users on this server should have global (non-world specific) groups applied\napply-global-world-groups: true\n\n# +----------------------------------------------------------------------------------------------+ #\n# | Meta lookup settings                                                                         | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# Defines how meta values should be selected.\n#\n# - Possible options:\n#   => inheritance      Selects the meta value that was inherited first\n#   => highest-number   Selects the highest numerical meta value\n#   => lowest-number    Selects the lowest numerical meta value\nmeta-value-selection-default: inheritance\n\n# Defines how meta values should be selected per key.\nmeta-value-selection:\n#  max-homes: highest-number\n\n# +----------------------------------------------------------------------------------------------+ #\n# | Inheritance settings                                                                         | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# If the plugin should apply wildcard permissions.\n#\n# - If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered\n#   permissions matching the wildcard.\napply-wildcards: true\n\n# If LuckPerms should resolve and apply permissions according to the Sponge style implicit wildcard\n# inheritance system.\n#\n# - That being: If a user has been granted \"example\", then the player should have also be\n#   automatically granted \"example.function\", \"example.another\", \"example.deeper.nesting\",\n#   and so on.\napply-sponge-implicit-wildcards: false\n\n# If the plugin should apply negated Bukkit default permissions before it considers wildcard\n# assignments.\n#\n# - Plugin authors can define permissions which explicitly should not be given automatically to OPs.\n#   This is usually used for so called \"anti-permissions\" - permissions which, when granted, apply\n#   something negative.\n# - If this option is set to true, LuckPerms will consider any negated declarations made by\n#   plugins before it considers wildcards. (similar to the way the OP system works)\n# - If this option is set to false, LuckPerms will consider any wildcard assignments first.\napply-default-negated-permissions-before-wildcards: false\n\n# If the plugin should parse regex permissions.\n#\n# - If set to true, LuckPerms will detect regex permissions, marked with \"r=\" at the start of the\n#   node, and resolve & apply all registered permissions matching the regex.\napply-regex: true\n\n# If the plugin should complete and apply shorthand permissions.\n#\n# - If set to true, LuckPerms will detect and expand shorthand node patterns.\napply-shorthand: true\n\n# If the plugin should apply Bukkit child permissions.\n#\n# - Plugin authors can define custom permissions structures for their plugin, which will be resolved\n#   and used by LuckPerms if this setting is enabled.\napply-bukkit-child-permissions: true\n\n# If the plugin should apply Bukkit default permissions.\n#\n# - Plugin authors can define permissions which should be given to all users by default, or setup\n#   permissions which should/shouldn't be given to opped players.\n# - If this option is set to false, LuckPerms will ignore these defaults.\napply-bukkit-default-permissions: true\n\n# If the plugin should apply attachment permissions.\n#\n# - Other plugins on the server are able to add their own \"permission attachments\" to players.\n# - This allows them to grant players additional permissions which last until the end of the\n#   session, or until they're removed.\n# - If this option is set to false, LuckPerms will not include these attachment permissions when\n#   considering if a player should have access to a certain permission.\napply-bukkit-attachment-permissions: true\n\n# +----------------------------------------------------------------------------------------------+ #\n# | Extra settings                                                                               | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# A list of context calculators which will be skipped when calculating contexts.\n#\n# - You can disable context calculators by either:\n#   => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator)\n#   => specifying a sub-section of the Java package used by the calculator (e.g. com.example)\ndisabled-context-calculators: []\n\n# Allows you to set \"aliases\" for the worlds sent forward for context calculation.\n#\n# - These aliases are provided in addition to the real world name. Applied recursively.\n# - Remove the comment characters for the default aliases to apply.\nworld-rewrite:\n#  world_nether: world\n#  world_the_end: world\n\n# Define special group weights for this server.\n#\n# - Group weights can also be applied directly to group data, using the setweight command.\n# - This section allows weights to be set on a per-server basis.\ngroup-weight:\n#  admin: 10\n\n\n\n\n# +----------------------------------------------------------------------------------------------+ #\n# |                                                                                              | #\n# |                                      FINE TUNING OPTIONS                                     | #\n# |                                                                                              | #\n# |     A number of more niche settings for tweaking and changing behaviour. The section also    | #\n# | contains toggles for some more specialised features. It is only necessary to make changes to | #\n# |                  these options if you want to fine-tune LuckPerms behaviour.                 | #\n# |                                                                                              | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# +----------------------------------------------------------------------------------------------+ #\n# | Server Operator (OP) settings                                                                | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# Controls whether server operators should exist at all.\n#\n# - When set to 'false', all players will be de-opped, and the /op and /deop commands will be\n#   disabled. Note that vanilla features like the spawn-protection require an operator on the\n#   server to work.\nenable-ops: true\n\n# Enables or disables a special permission based system in LuckPerms for controlling OP status.\n#\n# - If set to true, any user with the permission \"luckperms.autoop\" will automatically be granted\n#   server operator status. This permission can be inherited, or set on specific servers/worlds,\n#   temporarily, etc.\n# - Additionally, setting this to true will force the \"enable-ops\" option above to false. All users\n#   will be de-opped unless they have the permission node, and the op/deop commands will be\n#   disabled.\n# - It is recommended that you use this option instead of assigning a single '*' permission.\nauto-op: false\n\n# Defines if \"opped\" players should be able to use all LuckPerms commands by default.\n#\n# - Set to false to only allow users who have the permissions access to the commands\ncommands-allow-op: true\n\n# +----------------------------------------------------------------------------------------------+ #\n# | Vault integration settings                                                                   | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# If Vault lookups for offline players on the main server thread should be enabled.\n#\n# LuckPerms has a \"catch\" for plugins attempting to perform unsafe offline player data lookups\n# from the main server thread. This catch raises an exception (causes an error to occur) when unsafe\n# lookups are made, instead of allowing the lookup to happen, which would likely cause the server\n# to lag.\n#\n# However, if you're willing to accept the consequences, the catch can be disabled by setting this\n# option to 'true.\nvault-unsafe-lookups: false\n\n# If LuckPerms should use the 'display name' of a group when returning groups in Vault API calls.\n#\n# - When this option is set to true, the display name of the group is returned.\n# - When this option is set to false, the standard name/id of the group is returned.\nvault-group-use-displaynames: true\n\n# Controls which group LuckPerms should use for NPC players when handling Vault requests.\n#\n# - As NPCs aren't actually real players, LuckPerms does not load any user data for them. This\n#   becomes an issue when plugins want to check for their permissions using Vault.\n# - As a solution, Vault checks for NPCs fallback to a group, which is defined below.\nvault-npc-group: default\n\n# Controls how LuckPerms should consider the OP status of NPC players when handing Vault requests.\n#\n# - If you want NPCs to have the same permissions as \"normal\" players, set this option to false.\n# - If you want NPCs to have OP status, set this option to true.\nvault-npc-op-status: false\n\n# If the vault-server option below should be used.\n#\n# - When this option is set to false, the server value defined above under \"server\" is used.\nuse-vault-server: false\n\n# The name of the server used within Vault operations.\n#\n# - If you don't want Vault operations to be server specific, set this to \"global\".\n# - Will only take effect if use-vault-server is set to true above.\nvault-server: global\n\n# If global permissions should be considered when retrieving meta or player groups\nvault-include-global: true\n\n# If Vault operations should ignore any world arguments if supplied.\nvault-ignore-world: false\n\n# +----------------------------------------------------------------------------------------------+ #\n# | Miscellaneous (and rarely used) settings                                                     | #\n# +----------------------------------------------------------------------------------------------+ #\n\n# If LuckPerms should produce extra logging output when it handles logins.\n#\n# - Useful if you're having issues with UUID forwarding or data not being loaded.\ndebug-logins: false\n\n# If LuckPerms should allow usernames with non alphanumeric characters.\n#\n# - Note that due to the design of the storage implementation, usernames must still be 16 characters\n#   or less.\nallow-invalid-usernames: false\n\n# If LuckPerms should not require users to confirm bulkupdate operations.\n#\n# - When set to true, operations will be executed immediately.\n# - This is not recommended, as bulkupdate has the potential to irreversibly delete large amounts of\n#   data, and is not designed to be executed automatically.\n# - If automation is needed, users should prefer using the LuckPerms API.\nskip-bulkupdate-confirmation: false\n\n# If LuckPerms should prevent bulkupdate operations.\n#\n# - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work.\n# - When set to false, bulkupdate operations will be allowed via the console.\ndisable-bulkupdate: false\n\n# If LuckPerms should allow a users primary group to be removed with the 'parent remove' command.\n#\n# - When this happens, the plugin will set their primary group back to default.\nprevent-primary-group-removal: false\n\n# If LuckPerms should update the list of commands sent to the client when permissions are changed.\nupdate-client-command-list: true\n\n# If LuckPerms should attempt to register \"Brigadier\" command list data for its commands.\nregister-command-list-data: true\n\n# If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands.\n# See here for more info: https://minecraft.wiki/w/Target_selectors\nresolve-command-selectors: false\n\n# If the plugin should run in \"read-only\" mode for commands.\n#\n# In this mode, players or the console will only be able to execute commands that read or view\n# data, and will not be able to execute any LP commands that modify data.\n#\n# If enabling read-only mode for just players, be aware that some other plugins may allow players\n# to execute commands as the console, allowing them to run LP commands indirectly.\n#\n# Note: This does not affect interactions with LuckPerms via the API.\ncommands-read-only-mode:\n  players: false\n  console: false\n\n# If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being\n# executed. In a sense this is a more extreme version of read-only mode (see above).\n#\n# LuckPerms will still act as the permission manager, but server administrators will be unable to\n# make changes via commands.\n#\n# If disabling commands just for players, be aware that some other plugins may allow players to\n# execute commands as the console, allowing them to run commands indirectly.\n#\n# If commands are disabled for both players and the console, LuckPerms will not attempt to register\n# a command with the server at all & in a sense will be invisible to both players and admins.\n#\n# Note: This does not affect interactions with LuckPerms via the API.\ndisable-luckperms-commands:\n  players: false\n  console: false"
  },
  {
    "path": "examples/auto-curseforge/aof7/docker-compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: true\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\n      # Allocate API key from https://console.curseforge.com/\n      # and set in .env file making sure to double up dollar signs, such as\n      # CF_API_KEY=$$2a$$10$$....\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\n      CF_API_KEY: ${CF_API_KEY}\n      CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/all-of-fabric-7\n#      CF_FILENAME_MATCHER: 1.2.2\n      CF_OVERRIDES_EXCLUSIONS: |\n        mods/iris*.jar\n        mods/sodium*.jar\n      MEMORY: 4G\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - mc-data:/data\n\nvolumes:\n  mc-data: {}\n\n"
  },
  {
    "path": "examples/auto-curseforge/atm10/docker-compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    tty: true\n    stdin_open: true\n    ports:\n      - \"25565:25565\"\n    environment:\n      EULA: true\n      MOD_PLATFORM: AUTO_CURSEFORGE\n      # Allocate API key from https://console.curseforge.com/\n      # and set in .env file making sure to double up dollar signs, such as\n      # CF_API_KEY=$$2a$$10$$....\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\n      CF_API_KEY: ${CF_API_KEY}\n      CF_SLUG: all-the-mods-10\n      # Optional: select a specific version/file\n      # CF_FILENAME_MATCHER: \"1.17\"\n      MEMORY: 4G\n      CF_OVERRIDES_EXCLUSIONS: |\n        # Not applicable for server side\n        shaderpacks/**\n    volumes:\n      # Use managed volume by default, but can change to a relative path like\n      # ./data:/data\n      # to use a host directory\n      - mc-data:/data\nvolumes:\n  mc-data:\n"
  },
  {
    "path": "examples/auto-curseforge/atm8/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server:java17\r\n    ports:\r\n      - \"25565:25565\"\r\n    environment:\r\n      EULA: \"true\"\r\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\r\n      # Allocate API key from https://console.curseforge.com/\r\n      # and set in .env file making sure to double up dollar signs, such as\r\n      # CF_API_KEY=$$2a$$10$$....\r\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\r\n      CF_API_KEY: ${CF_API_KEY}\r\n      CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/all-the-mods-8\r\n#      CF_FILENAME_MATCHER: \"1.1.0\"\r\n      MEMORY: 4G\r\n    volumes:\r\n      - mc-data:/data\r\n  # Since Docker's default volume driver creates volumes owned by root, this\r\n  # init container will change ownership to match final UID of mc service, above\r\n  init-filebrowser:\r\n    image: filebrowser/filebrowser\r\n    entrypoint: sh -c\r\n    command:\r\n      - \"chown -R 1000: /database\"\r\n    restart: no\r\n    volumes:\r\n      - filebrowser-db:/database\r\n  filebrowser:\r\n    image: filebrowser/filebrowser\r\n    depends_on:\r\n      init-filebrowser:\r\n        condition: service_completed_successfully\r\n    user: \"1000:1000\"\r\n    environment:\r\n      FB_DATABASE: /database/filebrowser.db\r\n    volumes:\r\n      # Default FB_ROOT is /srv\r\n      # In this example, the left-side needs to be the same as /data volume of mc service\r\n      - mc-data:/srv\r\n      - filebrowser-db:/database\r\n    ports:\r\n      - \"25580:80\"\r\n\r\nvolumes:\r\n  mc-data: {}\r\n  filebrowser-db: {}"
  },
  {
    "path": "examples/auto-curseforge/atm8/filebrowser.json",
    "content": "{\r\n  \"port\": 80,\r\n  \"baseURL\": \"\",\r\n  \"address\": \"\",\r\n  \"log\": \"stdout\",\r\n  \"database\": \"/database/filebrowser.db\",\r\n  \"root\": \"/srv\"\r\n}"
  },
  {
    "path": "examples/auto-curseforge/atm9/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    ports:\r\n      - \"25565:25565\"\r\n    environment:\r\n      EULA: \"true\"\r\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\r\n      # Allocate API key from https://console.curseforge.com/\r\n      # and set in .env file making sure to double up dollar signs, such as\r\n      # CF_API_KEY=$$2a$$10$$....\r\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\r\n      CF_API_KEY: ${CF_API_KEY}\r\n      CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/all-the-mods-9\r\n      # Optional: select a specific version/file\r\n      #CF_FILENAME_MATCHER: \"0.2.34\"\r\n      MEMORY: 4G\r\n    volumes:\r\n      - mc-data:/data\r\n\r\nvolumes:\r\n  mc-data: {}"
  },
  {
    "path": "examples/auto-curseforge/craftoria/docker-compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    tty: true\n    stdin_open: true\n    ports:\n      - \"25565:25565\"\n    environment:\n      EULA: true\n      ALLOW_FLIGHT: true\n      MOD_PLATFORM: AUTO_CURSEFORGE\n      CF_API_KEY: ${CF_API_KEY}\n      CF_SLUG: craftoria\n      MOTD: |\n        A %TYPE% server on %VERSION%\n        running %MODPACK_NAME% %MODPACK_VERSION%\n      MEMORY: 8G\n    volumes:\n      # Use managed volume by default, but can change to a relative path like\n      # ./data:/data\n      # to use a host directory\n      - mc-data:/data\nvolumes:\n  mc-data:\n"
  },
  {
    "path": "examples/auto-curseforge/fabric-custom-loader/docker-compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"TRUE\"\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\n      # Allocate API key from https://console.curseforge.com/\n      CF_API_KEY: ${CF_API_KEY}\n      # Example of overriding modloader version for a Fabric modpack\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\n      CF_SLUG: \"cobblemon-fabric\"\n      # Override the mod loader version from the modpack\n      CF_MOD_LOADER_VERSION: \"0.16.14\"\n      MEMORY: \"4G\"\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - ./data:/data\n    stdin_open: true\n    tty: true\n\n# See https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#custom-modloader-versions\n"
  },
  {
    "path": "examples/auto-curseforge/ftb-evolution/compose.yaml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    tty: true\n    stdin_open: true\n    ports:\n      # Game port\n      - \"25565:25565/tcp\"\n    environment:\n      EULA: \"TRUE\"\n      TYPE: AUTO_CURSEFORGE\n      # Allocate API key from https://console.curseforge.com/\n      # and set in .env file making sure to double up dollar signs, such as\n      # CF_API_KEY=$$2a$$10$$....\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\n      CF_API_KEY: ${CF_API_KEY}\n      CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/ftb-evolution\n      # This modpack includes an override client-side only mod that will prevent the server from starting.\n      # For more information on override exclusions see: \n      # https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#excluding-overrides-files\n      CF_OVERRIDES_EXCLUSIONS: mods/rgp_client*.jar\n      MEMORY: 6G\n    volumes:\n      # attach the relative directory 'data' to the container's /data path\n      - ./data:/data"
  },
  {
    "path": "examples/auto-curseforge/modpack-manifest/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    environment:\r\n      EULA: true\r\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\r\n      # Allocate API key from https://console.curseforge.com/\r\n      # and set in .env file making sure to double up dollar signs, such as\r\n      # CF_API_KEY=$$2a$$10$$....\r\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\r\n      CF_API_KEY: ${CF_API_KEY}\r\n      CF_MODPACK_MANIFEST: /manifests/manifest.json\r\n      CF_SLUG: \"custom\"\r\n      MEMORY: 2G\r\n    volumes:\r\n      - mc-data:/data\r\n      - ./manifests:/manifests:ro\r\n    ports:\r\n      - \"25565:25565\"\r\n\r\nvolumes:\r\n  mc-data: {}\r\n"
  },
  {
    "path": "examples/auto-curseforge/modpack-manifest/manifests/manifest.json",
    "content": "{\r\n  \"minecraft\": {\r\n    \"version\": \"1.20.4\",\r\n    \"modLoaders\": [\r\n      {\r\n        \"id\": \"fabric-0.15.3\",\r\n        \"primary\": true\r\n      }\r\n    ]\r\n  },\r\n  \"manifestType\": \"minecraftModpack\",\r\n  \"manifestVersion\": 1,\r\n  \"name\": \"Custom\",\r\n  \"author\": \"\",\r\n  \"files\": [\r\n    {\r\n      \"projectID\": 351725,\r\n      \"fileID\": 4973035,\r\n      \"required\": true\r\n    },\r\n    {\r\n      \"projectID\": 306612,\r\n      \"fileID\": 5010374,\r\n      \"required\": true\r\n    }\r\n  ],\r\n  \"overrides\": \"overrides\"\r\n}"
  },
  {
    "path": "examples/auto-curseforge/one-block-modded/.gitignore",
    "content": "/.env"
  },
  {
    "path": "examples/auto-curseforge/one-block-modded/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    ports:\r\n      - \"25565:25565\"\r\n    environment:\r\n      EULA: \"true\"\r\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\r\n      # Allocate API key from https://console.curseforge.com/\r\n      # and set in .env file making sure to double up dollar signs, such as\r\n      # CF_API_KEY=$$2a$$10$$....\r\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\r\n      CF_API_KEY: ${CF_API_KEY}\r\n      CF_SLUG: one-block-modded\r\n      # Modpack client zip must be manually downloaded from\r\n      # https://www.curseforge.com/minecraft/modpacks/one-block-modded\r\n      # due to authors disallowing project distribution\r\n      CF_MODPACK_ZIP: /modpacks/Modded+OneBlock.zip\r\n      CF_SET_LEVEL_FROM: OVERRIDES\r\n      DEBUG: \"false\"\r\n      MEMORY: 4G\r\n    volumes:\r\n      - mc-data:/data\r\n      - ./modpacks:/modpacks:ro\r\n\r\nvolumes:\r\n  mc-data: {}\r\n"
  },
  {
    "path": "examples/auto-curseforge/pixelmon/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server:java8-multiarch\r\n    ports:\r\n      - \"25565:25565\"\r\n    environment:\r\n      EULA: \"true\"\r\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\r\n      # Allocate API key from https://console.curseforge.com/\r\n      # and set in .env file making sure to double up dollar signs, such as\r\n      # CF_API_KEY=$$2a$$10$$....\r\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\r\n      CF_API_KEY: ${CF_API_KEY}\r\n      CF_SLUG: the-pixelmon-modpack\r\n      CF_FILENAME_MATCHER: \"9.1.2\"\r\n      MEMORY: 4G\r\n    volumes:\r\n      - mc-data:/data\r\n\r\nvolumes:\r\n  mc-data: {}"
  },
  {
    "path": "examples/auto-curseforge/prominence2/docker-compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    ports:\n      - \"25565:25565\"\n    environment:\n      EULA: \"true\"\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\n      # Allocate API key from https://console.curseforge.com/\n      # and set in .env file making sure to double up dollar signs, such as\n      # CF_API_KEY=$$2a$$10$$....\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\n      CF_API_KEY: ${CF_API_KEY}\n      CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/prominence-2-rpg\n      CF_FILENAME_MATCHER: \"2.8.7\"\n      MEMORY: 4G\n    volumes:\n      - mc-data:/data\n\nvolumes:\n  mc-data: {}"
  },
  {
    "path": "examples/auto-curseforge/rad2/compose.yaml",
    "content": "# This runs the most recent version of Roguelike Adventures and Dungeons 2. Version 1.11 as of writing.\nservices:\n  mc:\n    image: itzg/minecraft-server:java8\n    ports:\n      - \"25565:25565\"\n    environment:\n      EULA: \"true\"\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\n      # Allocate API key from https://console.curseforge.com/\n      # and set in .env file making sure to double up dollar signs, such as\n      # CF_API_KEY=$$2a$$10$$....\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\n      CF_API_KEY: ${CF_API_KEY}\n      CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/roguelike-adventures-and-dungeons-2\n      # Optional: select a specific version/file\n      #CF_FILENAME_MATCHER: \"0.2.34\"\n      CF_EXCLUDE_MODS: |\n        # Exclude client-side mods not published correctly\n        creative-core\n        default-options\n        itemphysic-lite\n        konkrete\n        oauth\n      CF_FORCE_SYNCHRONIZE: \"true\"\n      MEMORY: 4G\n    volumes:\n      - mc-data:/data\n      - C:\\Users\\geoff\\git\\mc-image-helper\\build\\install\\mc-image-helper:/usr/share/mc-image-helper:ro\n\nvolumes:\n  mc-data: {}\n"
  },
  {
    "path": "examples/auto-curseforge/skyfactory4/compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server:java8\r\n    environment:\r\n      EULA: \"true\"\r\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\r\n      CF_SLUG: skyfactory-4\r\n      CF_FILENAME_MATCHER: 4.2.4\r\n      CF_API_KEY: ${CF_API_KEY}\r\n      MEMORY: 3G\r\n    ports:\r\n      - \"25565:25565\"\r\n    volumes:\r\n      - mc-data:/data\r\nvolumes:\r\n  mc-data:"
  },
  {
    "path": "examples/auto-curseforge/skyfactory5/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: true\n      # https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\n#      CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/skyfactory-5/files/6290684\n      CF_SLUG: skyfactory-5\n      # Comment out the following to get the latest version or pick a version from\n      # https://www.curseforge.com/minecraft/modpacks/skyfactory-5/files/all?page=1&pageSize=20\n      CF_FILENAME_MATCHER: 5.0.8\n      # Allocate API key from https://console.curseforge.com/\n      # and set in .env file making sure to double up dollar signs, such as\n      # CF_API_KEY=$$2a$$10$$....\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\n      CF_API_KEY: ${CF_API_KEY}\n      MEMORY: 4G\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - mc-data:/data\nvolumes:\n  mc-data:\n"
  },
  {
    "path": "examples/auto-curseforge/using-excludes/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server:java8\r\n    ports:\r\n      - \"25565:25565\"\r\n    environment:\r\n      EULA: \"true\"\r\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\r\n      # Allocate API key from https://console.curseforge.com/\r\n      # and set in .env file making sure to double up dollar signs, such as\r\n      # CF_API_KEY=$$2a$$10$$....\r\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\r\n      CF_API_KEY: ${CF_API_KEY}\r\n      CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/minecraft-eternal/files/4102634\r\n      MEMORY: 4G\r\n    volumes:\r\n      - mc-data:/data\r\n\r\nvolumes:\r\n  mc-data: {}"
  },
  {
    "path": "examples/auto-curseforge/valhesia5/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    ports:\r\n      - \"25565:25565\"\r\n    environment:\r\n      EULA: \"true\"\r\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\r\n      # Allocate API key from https://console.curseforge.com/\r\n      # and set in .env file making sure to double up dollar signs, such as\r\n      # CF_API_KEY=$$2a$$10$$....\r\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\r\n      CF_API_KEY: ${CF_API_KEY}\r\n      CF_FORCE_SYNCHRONIZE: \"true\"\r\n      CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/valhelsia-5/files/4429560\r\n      MEMORY: 4G\r\n    volumes:\r\n      - data:/data\r\n\r\nvolumes:\r\n  data: {}"
  },
  {
    "path": "examples/auto-curseforge/vault-hunters-1-18-2/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server:java17\r\n    ports:\r\n      - \"25565:25565\"\r\n    environment:\r\n      EULA: \"true\"\r\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\r\n      # Allocate API key from https://console.curseforge.com/\r\n      # and set in .env file making sure to double up dollar signs, such as\r\n      # CF_API_KEY=$$2a$$10$$....\r\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\r\n      CF_API_KEY: ${CF_API_KEY}\r\n      CF_SLUG: vault-hunters-1-18-2\r\n      MOTD: \"§4----- §2 Vault Hunters: 1.18.2 §4 -----§r\\\\n     §4------ §e vaulthunters.gg  §4------\"\r\n      MEMORY: 6G # 4G for base server + 2G per player\r\n      ALLOW_FLIGHT: true\r\n      ENABLE_COMMAND_BLOCK: true\r\n      DIFFICULTY: hard\r\n    volumes:\r\n      - mc-data:/data\r\n\r\nvolumes:\r\n  mc-data: {}\r\n"
  },
  {
    "path": "examples/autopause/compose.yml",
    "content": "services:\n  minecraft:\n    image: itzg/minecraft-server\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - \"mc:/data\"\n    environment:\n      EULA: \"TRUE\"\n      TYPE: PAPER\n      ENABLE_AUTOPAUSE: \"TRUE\"\n      MAX_TICK_TIME: \"-1\"\n      # More aggressive settings for demo purposes\n      AUTOPAUSE_TIMEOUT_INIT: \"30\"\n      AUTOPAUSE_TIMEOUT_EST: \"10\"\n      JVM_DD_OPTS: \"disable.watchdog:true\"\n    restart: unless-stopped\n\nvolumes:\n  mc: {}\n"
  },
  {
    "path": "examples/autostop/compose.yml",
    "content": "services:\n  minecraft:\n    image: itzg/minecraft-server\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - \"mc:/data\"\n    environment:\n      EULA: \"TRUE\"\n      ENABLE_AUTOSTOP: \"TRUE\"\n      # More aggressive settings for demo purposes\n      AUTOSTOP_TIMEOUT_INIT: \"30\"\n      AUTOSTOP_TIMEOUT_EST: \"20\"\n    # Important not to auto-restart the server!!!\n    restart: \"no\"\n\nvolumes:\n  mc: {}\n"
  },
  {
    "path": "examples/bentobox/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"TRUE\"\n      TYPE: PAPER\n      DEBUG: true\n      PLUGINS: |\n        https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot\n        https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot\n        https://hangarcdn.papermc.io/plugins/BentoboxWorld/bentobox/versions/3.7.4/PAPER/BentoBox-3.7.4.jar\n      DOWNLOAD_EXTRA_CONFIGS: |\n        # Even though it's a jar can be treated just like any other file\n        plugins/BentoBox/addons<https://github.com/BentoBoxWorld/AOneBlock/releases/download/1.20.0/AOneBlock-1.20.0.jar\n    ports:\n      - \"25565:25565\"\n      - \"25565:25565/udp\"\n      - \"19132:19132/udp\"\n    volumes:\n      - data:/data\nvolumes:\n  data:"
  },
  {
    "path": "examples/bettermc/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    environment:\r\n      EULA: true\r\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\r\n      # Allocate API key from https://console.curseforge.com/\r\n      # and set in .env file making sure to double up dollar signs, such as\r\n      # CF_API_KEY=$$2a$$10$$....\r\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\r\n      CF_API_KEY: ${CF_API_KEY}\r\n      CF_SLUG: better-mc-fabric-bmc1\r\n#      CF_FILENAME_MATCHER: v18.5\r\n      MEMORY: 4G\r\n    volumes:\r\n      - mc:/data\r\n    ports:\r\n      - \"25565:25565\"\r\n\r\nvolumes:\r\n  mc: {}"
  },
  {
    "path": "examples/canyon/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server:java8\n    environment:\n      EULA: true\n      TYPE: canyon\n      VERSION: b1.7.3\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - mc-data:/data\nvolumes:\n  mc-data:\n"
  },
  {
    "path": "examples/cleanroom/compose.yml",
    "content": "# Provides an example of running CleanroomMC from https://github.com/CleanroomMC/Cleanroom,\r\n# which is a Forge fork\r\nservices:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    environment:\r\n      EULA: true\r\n      TYPE: FORGE\r\n      FORGE_INSTALLER_URL: https://github.com/CleanroomMC/Cleanroom/releases/download/0.2.4-alpha/cleanroom-0.2.4-alpha-installer.jar\r\n    ports:\r\n      - \"25565:25565\"\r\n    volumes:\r\n      - mc-data:/data\r\nvolumes:\r\n  mc-data:"
  },
  {
    "path": "examples/curseforge-files/docker-compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: true\n      TYPE: FORGE\n      # Allocate API key from https://console.curseforge.com/\n      # and set in .env file making sure to double up dollar signs, such as\n      # CF_API_KEY=$$2a$$10$$....\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\n      CF_API_KEY: ${CF_API_KEY}\n      VERSION: 1.19.2\n      CURSEFORGE_FILES: |\n        geckolib # some comment\n        # Removing temporarily\n        #aquaculture\n        naturalist\n    ports:\n      - \"25565:25565\""
  },
  {
    "path": "examples/docker-compose-big.yml",
    "content": "services:\n  minecraft:\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - \"mcbig:/data\"\n    environment:\n      EULA: \"TRUE\"\n      MAX_MEMORY: 32G\n      MAX_BUILD_HEIGHT: 256\n      VIEW_DISTANCE: 15\n      LEVEL_TYPE: LARGEBIOMES\n      MAX_PLAYERS: 100\n      CONSOLE: \"false\"\n    image: itzg/minecraft-server\n    restart: always\n  rcon:\n    image: itzg/rcon\n    ports:\n      - \"4326:4326\"\n      - \"4327:4327\"\n    volumes:\n      - \"rcon:/opt/rcon-web-admin/db\"\n\nvolumes:\n  mcbig:\n  rcon:\n"
  },
  {
    "path": "examples/docker-compose-curseforge-atm7.yaml",
    "content": "####################################################################\n#                         CURSEFORGE                               #\n#                                                                  #\n# Date: 20221005                                                   #\n#                                                                  #\n# Mod: All The Mods 7 0.4.32                                       #\n#                                                                  #\n# Notes: Verify that there is no EULA file in the modpack.zip      #\n#        if you do not delete it the EULA flag below will be       #\n#        overwritten when the modpack is copied.                   #\n#                                                                  #\n####################################################################\nservices:\n\n####################################################################\n#                         Service Name                             #\n#                                                                  #\n# Define Service Name here. If using RCON this name will be        #\n# referenced again as RWA_RCON_HOST below.                         #\n#                                                                  #\n# Example: 'name:' or 'mc_atm6:'                                   #\n####################################################################\n  mc_atm7:\n\n####################################################################\n#                      Image & Container Name                      #\n#                                                                  #\n# Specify Image Name and Java Version. The 'image' will always be  #\n# 'itzg/minecraft-server' however the tag added to the end is      #\n# where you can specify the java version or container architecture.#\n# See readme.md for a full list.                                   #\n#                                                                  #\n# 'container_name:' This can be anything you like. This is the name#\n# that will show when you run 'docker ps' commands.                #\n####################################################################\n    image: itzg/minecraft-server\n    container_name: mc_atm7\n\n####################################################################\n#                         Server Ports                             #\n#                                                                  #\n# Specify external port.                                           #\n####################################################################\n    ports:\n      - 25565:25565\n\n####################################################################\n#                   Automatic Server Restart                       #\n#                                                                  #\n# Define a restart policy here.                                    #\n#    - 'no' = Do not restart.                                      #\n#    - 'on-failure' = Restart if container exits because an error. #\n#    - 'always' = Regardless of stop reason.                       #\n#    - 'unless-stopped' = Similar to always except if stopped.     #\n####################################################################\n    restart: unless-stopped\n\n####################################################################\n#                     Volume and Folder Access                     #\n#                                                                  #\n# This section defines what folders and volumes you want to give   #\n# this container access to. It is recommended to leaves these set  #\n# to the default values unless you know what you are doing.        #\n#                                                                  #\n# Place your mod zip file in a folder called 'modpacks' in the     #\n# same directory you place this docker-compose file.               #\n#                                                                  #\n# Specify the data volume name or directory here as well.          #\n# In this example the volume name is 'data'. When docker creates   #\n# the volume it will add what ever name you give it here to the    #\n# end of the container name specified above. In this example it    #\n# would be named 'mc_atm6_data'. If you change this be sure to     #\n# update the volume name at the bottom of this config.             #\n####################################################################\n    volumes:\n      - ./modpacks:/modpacks:ro\n      - data:/data\n\n####################################################################\n#                             EULA                                 #\n#                                                                  #\n# Accept EULA by setting to \"true\"                                 #\n####################################################################\n    environment:\n      EULA: \"true\"\n\n####################################################################\n#                       CURSEFORGE INSTALL                         #\n#                                                                  #\n# Sets install type to FORGE and specifys the zip folder name      #\n# and location of your mod pack.                                   #\n#                                                                  #\n# TYPE: Defines the install type as CURSEFORGE                     #\n#                                                                  #\n# CF_SERVER_MOD: Define where the modpack.zip is located.          #\n#                                                                  #\n# Place your mod zip file in a folder called 'modpacks' in the     #\n# same directory you place this docker-compose file.               #\n####################################################################\n      TYPE: CURSEFORGE\n      CF_SERVER_MOD: /modpacks/ATM7-0.4.32-server.zip\n\n####################################################################\n#                          Server Memory                           #\n#                                                                  #\n# Set Maximum amount of memory allowed for your server.            #\n####################################################################\n      MEMORY: \"8G\"\n\n####################################################################\n#                         Logging Options                          #\n#                                                                  #\n# Rolling logs are now enabled by default with templated           #\n# log4j2 configuration. You can customize:                         #\n#                                                                  #\n# LOG_LEVEL: Log level (default: info)                             #\n# LOG_CONSOLE_FORMAT: Console output format (docker logs)          #\n# LOG_FILE_FORMAT: File log format (logs/latest.log)               #\n# ROLLING_LOG_MAX_FILES: Max archived files (default: 1000)        #\n#                                                                  #\n# Example: Add full timestamp to logs                              #\n# LOG_CONSOLE_FORMAT: \"[%d{yyyy-MM-dd HH:mm:ss}] [%t/%level]: %msg%n\"\n# LOG_FILE_FORMAT: \"[%d{yyyy-MM-dd HH:mm:ss}] [%t/%level]: %msg%n\"\n#                                                                  #\n# ENABLE_ROLLING_LOGS is now legacy and no longer needed.          #\n####################################################################\n\n####################################################################\n#                          Server Timezone                         #\n#                                                                  #\n# Specify server Timezone                                          #\n####################################################################\n      TZ: \"America/New_York\"\n\n####################################################################\n#                       Minecraft Game Options                     #\n#                                                                  #\n# List any game options you want to define here. A full list can   #\n# be found on the readme.md page on github.                        #\n####################################################################\n      OVERRIDE_SERVER_PROPERTIES: \"true\"\n      DIFFICULTY: \"easy\"\n      MAX_TICK_TIME: \"-1\"\n      ALLOW_FLIGHT: \"true\"\n      OPS: \"\"\n      VIEW_DISTANCE: 10\n      MAX_PLAYERS: 10\n      PVP: \"false\"\n      LEVEL_TYPE: \"biomesoplenty\"\n      MOTD: \"Welcome Home\"\n\n####################################################################\n#                            Volumes                               #\n#                                                                  #\n# Define data volume name here. You should leave this set to the   #\n# default.                                                         #\n####################################################################\nvolumes:\n  data:\n"
  },
  {
    "path": "examples/docker-compose-curseforge.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server:${IMAGE_TAG:-java8}\n    volumes:\n    - ./modpacks:/modpacks:ro\n    - data:/data\n    environment:\n      EULA: \"true\"\n      TYPE: CURSEFORGE\n      CF_SERVER_MOD: /modpacks/SIMPLE-SERVER-FILES-0.3.20.zip\n#      CF_SERVER_MOD: /modpacks/createlive3serverfiles+1.4.2.zip\n#      CF_SERVER_MOD: /modpacks/Valhelsia+3-3.5.1-SERVER.zip\n#      CF_SERVER_MOD: https://media.forgecdn.net/files/3012/800/SkyFactory-4_Server_4.2.2.zip\n#      CF_SERVER_MOD: /modpacks/${MODPACK:-SkyFactory_4_Server_4.1.0.zip}\n    ports:\n      - \"25565:25565\"\n\nvolumes:\n  data: {}"
  },
  {
    "path": "examples/docker-compose-forge-bettermcplus",
    "content": "####################################################################\n#                       FORGE_GENERIC_PACK                         #\n#                                                                  #\n# Date: 20221005                                                   #\n#                                                                  #\n# Mod: Better Minecraft Plus v40                                   #\n#                                                                  #\n# Game Version: 1.16.5                                             #\n#                                                                  #\n# Notes: Verify that there is no EULA file in the modpack.zip      #\n#        if you do not delete it the EULA flag below will be       #\n#        overwritten when the modpack is copied.                   #\n#                                                                  #\n####################################################################\nservices:\n\n####################################################################\n#                         Service Name                             #\n#                                                                  #\n# Define Service Name here. If using RCON this name will be        #\n# referenced again as RWA_RCON_HOST below.                         #\n#                                                                  #\n# Example: 'name:' or 'mc_atm6:'                                   #\n####################################################################\n  mc_bmp:\n\n####################################################################\n#                      Image & Container Name                      #\n#                                                                  #\n# Specify Image Name and Java Version. The 'image' will always be  #\n# 'itzg/minecraft-server' however the tag added to the end is      #\n# where you can specify the java version or container architecture.#\n# See readme.md for a full list.                                   #\n#                                                                  #\n# 'container_name:' This can be anything you like. This is the name#\n# that will show when you run 'docker ps' commands.                #\n####################################################################\n    image: itzg/minecraft-server:java8\n    container_name: mc_bmp\n\n####################################################################\n#                         Server Ports                             #\n#                                                                  #\n# Specify external port.                                           #\n####################################################################\n    ports:\n      - 25565:25565\n\n####################################################################\n#                   Automatic Server Restart                       #\n#                                                                  #\n# Define a restart policy here.                                    #\n#    - 'no' = Do not restart.                                      #\n#    - 'on-failure' = Restart if container exits because an error. #\n#    - 'always' = Regardless of stop reason.                       #\n#    - 'unless-stopped' = Similar to always except if stopped.     #\n####################################################################\n    restart: unless-stopped\n\n####################################################################\n#                     Volume and Folder Access                     #\n#                                                                  #\n# This section defines what folders and volumes you want to give   #\n# this container access to. It is recommended to leaves these set  #\n# to the default values unless you know what you are doing.        #\n#                                                                  #\n# Place your mod zip file in a folder called 'modpacks' in the     #\n# same directory you place this docker-compose file.               #\n#                                                                  #\n# Specify the data volume name or directory here as well.          #\n# In this example the volume name is 'data'. When docker creates   #\n# the volume it will add what ever name you give it here to the    #\n# end of the container name specified above. In this example it    #\n# would be named 'mc_atm6_data'. If you change this be sure to     #\n# update the volume name at the bottom of this config.             #\n####################################################################\n    volumes:\n      - ./modpacks:/modpacks:ro\n      - data:/data\n\n####################################################################\n#                             EULA                                 #\n#                                                                  #\n# Accept EULA by setting to \"true\"                                 #\n####################################################################\n    environment:\n      EULA: \"true\"\n\n####################################################################\n#                          FORGE INSTALL                           #\n#                                                                  #\n# Sets install type to FORGE and specifys the zip folder name      #\n# and location of your mod pack.                                   #\n#                                                                  #\n# TYPE: Defines the install type as FORGE                          #\n#                                                                  #\n# VERSION: Defines the version of MC the modpack is based on.      #\n#                                                                  #\n# FORGE_VERSION: Defines the version of FORGE the modpack uses.     #\n#               This can usually be found in the modpack.zip as    #\n#               installer.jar                                      #\n#                                                                  #\n# GENERIC_PACK: Define where the modpack.zip is located.           #\n#                                                                  #\n# Place your mod zip file in a folder called 'modpacks' in the     #\n# same directory you place this docker-compose file.               #\n####################################################################\n      TYPE: FORGE\n      VERSION: 1.16.5\n      FORGE_VERSION: 36.2.39\n      GENERIC_PACK: /modpacks/Better+MC+Server+Pack+PLUS+1.16.5+v40+HF.zip\n\n####################################################################\n#                          Server Memory                           #\n#                                                                  #\n# Set Maximum amount of memory allowed for your server.            #\n####################################################################\n      MEMORY: \"8G\"\n\n####################################################################\n#                         Logging Options                          #\n#                                                                  #\n# Rolling logs are now enabled by default with templated           #\n# log4j2 configuration. You can customize:                         #\n#                                                                  #\n# LOG_LEVEL: Log level (default: info)                             #\n# LOG_CONSOLE_FORMAT: Console output format (docker logs)          #\n# LOG_FILE_FORMAT: File log format (logs/latest.log)               #\n# ROLLING_LOG_MAX_FILES: Max archived files (default: 1000)        #\n#                                                                  #\n# Example: Add full timestamp to logs                              #\n# LOG_CONSOLE_FORMAT: \"[%d{yyyy-MM-dd HH:mm:ss}] [%t/%level]: %msg%n\"\n# LOG_FILE_FORMAT: \"[%d{yyyy-MM-dd HH:mm:ss}] [%t/%level]: %msg%n\"\n#                                                                  #\n# ENABLE_ROLLING_LOGS is now legacy and no longer needed.          #\n####################################################################\n\n####################################################################\n#                          Server Timezone                         #\n#                                                                  #\n# Specify server Timezone                                          #\n####################################################################\n      TZ: \"America/New_York\"\n\n####################################################################\n#                       Minecraft Game Options                     #\n#                                                                  #\n# List any game options you want to define here. A full list can   #\n# be found on the readme.md page on github.                        #\n####################################################################\n      OVERRIDE_SERVER_PROPERTIES: \"true\"\n      DIFFICULTY: \"easy\"\n      MAX_TICK_TIME: \"-1\"\n      VIEW_DISTANCE: \"6\"\n      ALLOW_FLIGHT: \"true\"\n      OPS: \"\"\n      VIEW_DISTANCE: 10\n      MAX_PLAYERS: 10\n      PVP: \"false\"\n      LEVEL_TYPE: \"biomesoplenty\"\n      MOTD: \"Welcome Home\"\n\n####################################################################\n#                            Volumes                               #\n#                                                                  #\n# Define data volume name here. You should leave this set to the   #\n# default.                                                         #\n####################################################################\nvolumes:\n  data:\n"
  },
  {
    "path": "examples/docker-compose-forge.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    ports:\n    # expose the Minecraft server port outside of container\n    - \"25565:25565\"\n    environment:\n      # REQUIRED for all types\n      EULA: \"TRUE\"\n      # Set server type (vs the default of vanilla)\n      TYPE: FORGE\n      DEBUG: \"true\"\n    volumes:\n    # use a named, managed volume for data volume\n    - mc_forge:/data\n    # attach local host directory \"mods\" in same directory as this compose file\n    # all mods in this directory get copied into /data/mods at startup\n    - ./mods:/mods:ro\n\nvolumes:\n  # declared the named volume, but use default/local storage engine\n  mc_forge: {}\n"
  },
  {
    "path": "examples/docker-compose-magma.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server:java8\n    tty: true\n    stdin_open: true\n    ports:\n      - \"25565:25565\"\n    environment:\n      EULA: \"TRUE\"\n      TYPE: MAGMA\n      VERSION: 1.16.5\n    volumes:\n      - data:/data\nvolumes:\n  data: {}"
  },
  {
    "path": "examples/docker-compose-mods-mount.yml",
    "content": "services:\r\n  minecraft:\r\n    image: itzg/minecraft-server\r\n    volumes:\r\n      - ./data:/data\r\n      - ./mods:/mods\r\n    ports:\r\n      - \"25565:25565\"\r\n    environment:\r\n      EULA: \"true\"\r\n      TYPE: \"FORGE\"\r\n      VERSION: \"1.19.2\"\r\n    tty: true\r\n    stdin_open: true"
  },
  {
    "path": "examples/docker-compose-rconcmd.yml",
    "content": "services:\n  minecraft:\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - \"mc:/data\"\n    environment:\n      EULA: \"TRUE\"\n      TYPE: FABRIC\n      MEMORY: \"2G\"\n      CURSEFORGE_FILES: |\n        fabric-api\n        chunky-pregenerator\n      # Allocate API key from https://console.curseforge.com/\n      # and set in .env file making sure to double up dollar signs, such as\n      # CF_API_KEY=$$2a$$10$$....\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\n      CF_API_KEY: ${CF_API_KEY}\n      # YAML Heredoc, be sure to use '|-' this will remove the first newline and final new line.\n      # This is versus '|' that will leaving with two empty strings at top and bottom.\n      RCON_CMDS_STARTUP:  |-\n        /gamerule doFireTick false\n        /team add New\n        /team add Old\n        /chunky radius 1000\n        /chunky start\n      RCON_CMDS_ON_CONNECT: |-\n        /team join New @a[team=]\n        /give @a[team=New] birch_boat\n        /team join Old @a[team=New]\n      RCON_CMDS_FIRST_CONNECT: |-\n        /chunky pause\n      RCON_CMDS_LAST_DISCONNECT: |-\n        /kill @e[type=minecraft:boat]\n        /chunky continue\n    restart: unless-stopped\nvolumes:\n  mc: {}\n"
  },
  {
    "path": "examples/docker-compose-simple.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    tty: true\n    stdin_open: true\n    ports:\n      - \"25565:25565\"\n    environment:\n      EULA: \"TRUE\"\n    volumes:\n      # attach the relative directory 'data' to the container's /data path\n      - ./data:/data\n"
  },
  {
    "path": "examples/docker-compose-spongeforge.yml",
    "content": "# Forge with Sponge API support. THIS REQUIRES DOWNLOADING SPONGEFORGE.\n# Place the SpongeForge jar file in /data/mods. Other Forge mods go here as well.\n# Place Sponge mods in /data/mods/plugins. Yes, this is a directory inside the Forge mod directory. Do NOT use /data/plugins.\n\nservices:\n  minecraft:\n    image: itzg/minecraft-server:java8\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - \"mc:/data\"\n    environment:\n      EULA: \"TRUE\"\n      #VERSION: \"1.12.2\" (Ensure this is compatbile with the version of SpongeForge you are using!)\n      TYPE: \"FORGE\"\n      FORGE_VERSION: \"RECOMMENDED\"\n      #FORGE_VERSION: \"14.23.5.2807\"\n      CONSOLE: \"false\"\n      ENABLE_RCON: \"true\"\n      RCON_PASSWORD: \"testing\"\n      RCON_PORT: 28016\n    restart: unless-stopped\n  rcon:\n    image: itzg/rcon\n    ports:\n      - \"4326:4326\"\n      - \"4327:4327\"\n    volumes:\n      - \"rcon:/opt/rcon-web-admin/db\"\n\nvolumes:\n  mc:\n  rcon:\n"
  },
  {
    "path": "examples/docker-compose-world-download.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    ports:\n      - 25565:25565\n    environment:\n      EULA: \"TRUE\"\n      VERSION: 1.7.2\n      WORLD: https://www.minecraftmaps.com/survival-maps/cube-survival/download\n"
  },
  {
    "path": "examples/docker-compose.yml",
    "content": "services:\n  minecraft:\n    image: itzg/minecraft-server\n    stdin_open: true\n    tty: true\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - ./data:/data\n    environment:\n      EULA: \"TRUE\"\n    restart: unless-stopped\n"
  },
  {
    "path": "examples/fabric/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"true\"\n      TYPE: FABRIC\n#      VERSION: 1.21.4\n#      FABRIC_INSTALLER_VERSION: 1.0.1\n#      FABRIC_LOADER_VERSION: 0.16.10\n      # Since Fabric server type only includes the loader, most times\n      # the fabric-api is required for other mods to function\n      MODRINTH_PROJECTS: |\n        fabric-api\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - fabric:/data\n\nvolumes:\n  fabric: {}"
  },
  {
    "path": "examples/fabric-cardboard/compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server:latest\r\n    tty: true\r\n    stdin_open: true\r\n    ports:\r\n      - \"25565:25565\"\r\n    environment:\r\n      EULA: \"TRUE\"\r\n      TYPE: \"FABRIC\"\r\n      MEMORY: 4G\r\n      MODRINTH_PROJECTS: |\r\n        fabric-api\r\n        cardboard:beta\r\n      USES_PLUGINS: true\r\n    volumes:\r\n      - mc-data:/data\r\n      - ./plugins:/plugins:ro\r\nvolumes:\r\n  mc-data:"
  },
  {
    "path": "examples/ftba/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    ports:\n      # expose the Minecraft server port outside of container\n      - \"25565:25565\"\n    environment:\n      # REQUIRED for all types\n      EULA: \"TRUE\"\n      # Set server type (vs the default of vanilla)\n      TYPE: FTBA\n      # Use Pack ID from https://www.feed-the-beast.com/modpacks/119-ftb-presents-direwolf20-120?tab=about\n      FTB_MODPACK_ID: \"119\"\n#      FTB_MODPACK_VERSION_ID: \"\"\n      MOTD: |\n        An FTB server running\n        %MODPACK_NAME% version %MODPACK_VERSION%\n      MEMORY: 4G\n    volumes:\n      # use a named, managed volume for data volume\n      - data:/data\n\nvolumes:\n  # declared the named volume, but use default/local storage engine\n  data: {}\n"
  },
  {
    "path": "examples/ftba/stoneblock4/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server:java25\n    tty: true\n    stdin_open: true\n    environment:\n      EULA: \"TRUE\"\n      TYPE: FTBA\n      FTB_MODPACK_ID: \"130\"\n      FTB_MODPACK_VERSION_ID: \"100171\"\n      MEMORY: \"4G\"\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - data:/data\nvolumes:\n  data:"
  },
  {
    "path": "examples/generic-pack/rlcraft/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server:java8\n    volumes:\n    - data:/data\n    - ./modpacks:/modpacks:ro\n    environment:\n      EULA: \"true\"\n      TYPE: \"FORGE\"\n      VERSION: \"1.12.2\"\n      FORGE_VERSION: \"14.23.5.2860\"\n      OVERRIDE_SERVER_PROPERTIES: \"true\"\n      DIFFICULTY: \"hard\"\n      MAX_TICK_TIME: \"-1\"\n      ALLOW_FLIGHT: \"true\"\n      ENABLE_COMMAND_BLOCK: \"true\"\n      VIEW_DISTANCE: \"10\"\n      MEMORY: \"4G\"\n      # Download from https://www.curseforge.com/minecraft/modpacks/rlcraft and place in modpacks subdir\n      GENERIC_PACK: \"/modpacks/RLCraft+Server+Pack+1.12.2+-+Release+v2.9.1c.zip\"\n    ports:\n      - 25565:25565\n\nvolumes:\n  data:\n"
  },
  {
    "path": "examples/geyser/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    environment:\r\n      EULA: \"true\"\r\n      TYPE: \"PAPER\"\r\n      PLUGINS: |\r\n        https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot\r\n        https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot\r\n    ports:\r\n      - \"25565:25565\"\r\n      - \"19132:19132/udp\"\r\n    volumes:\r\n      - ./data:/data"
  },
  {
    "path": "examples/gtnh/docker-compose-type-gtnh.yaml",
    "content": "services:\n  mc:\n    # make sure this java version matches with pack java version\n    image: itzg/minecraft-server:java25\n    tty: true\n    stdin_open: true\n    ports:\n      - \"25565:25565\"\n    environment:\n      EULA: \"TRUE\"\n      TYPE: GTNH\n      GTNH_PACK_VERSION: \"2.8.1\"\n      # Enable to delete old config backups\n      # GTNH_DELETE_BACKUPS: true\n      # Use to prevent updates\n      # SKIP_GTNH_UPDATE_CHECK: true\n      MEMORY: 6G\n      # Bring in standard extra mods described at https://wiki.gtnewhorizons.com/wiki/Additional_Mods\n#      MODS: |\n#        https://github.com/Kynake/BetterFoliage/releases/download/1.2.1/BetterFoliage-LegacyEdition-1.2.1.jar\n    volumes:\n      # attach a managed volume, change to a relative or absolute host directory if needed\n      - mc-data:/data\nvolumes:\n  mc-data:\n"
  },
  {
    "path": "examples/gtnh/docker-compose.yaml",
    "content": "services:\n  mc:\n    # make sure this java version matches with pack java version\n    image: itzg/minecraft-server:java25\n    tty: true\n    stdin_open: true\n    ports:\n      - \"25565:25565\"\n    environment:\n      EULA: \"TRUE\"\n      TYPE: CUSTOM\n      GENERIC_PACKS: GT_New_Horizons_2.8.0_Server_Java_17-25\n      GENERIC_PACKS_SUFFIX: .zip\n      GENERIC_PACKS_PREFIX: https://downloads.gtnewhorizons.com/ServerPacks/\n      # if this isn't true, then the container tries to download the modpack every run\n      SKIP_GENERIC_PACK_UPDATE_CHECK: \"true\"\n      MEMORY: 6G\n      # Make sure that this matches what is in your pack's startserver bash file\n      JVM_OPTS: \"-Dfml.readTimeout=180 @java9args.txt\"\n      # You may choose to instead download a specific jar version yourself, and change this value to the local jar.\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/configuration/misc-options/#running-with-a-custom-server-jar\n      CUSTOM_SERVER: \"https://github.com/GTNewHorizons/lwjgl3ify/releases/download/2.1.16/lwjgl3ify-2.1.16-forgePatches.jar\"\n      # Set server.properties according to GTNH server defaults\n      MOTD: \"GT:New Horizons 2.8.0\"\n      DIFFICULTY: \"hard\"\n      ENABLE_COMMAND_BLOCK: \"true\"\n      SPAWN_PROTECTION: 1\n      VIEW_DISTANCE: 8\n      MODE: 0\n      LEVEL_TYPE: \"rwg\"\n      ALLOW_FLIGHT: \"TRUE\"\n    volumes:\n      # attach a managed volume, change to a relative or absolute host directory if needed\n      - mc-data:/data\nvolumes:\n  mc-data:\n"
  },
  {
    "path": "examples/jline/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    tty: true\r\n    stdin_open: true\r\n    environment:\r\n      EULA: true\r\n      TYPE: FABRIC\r\n      MODRINTH_PROJECTS: |\r\n        jline4mcdsrv\r\n    volumes:\r\n      - mc-data:/data\r\nvolumes:\r\n  mc-data: {}"
  },
  {
    "path": "examples/k8s/using-statefulset.yml",
    "content": "---\napiVersion: apps/v1\nkind: StatefulSet\nmetadata:\n  labels:\n    app: mc-example\n  name: mc-example\nspec:\n  replicas: 1\n  serviceName: mc-example\n  selector:\n    matchLabels:\n      app: mc-example\n  template:\n    metadata:\n      labels:\n        app: mc-example\n    spec:\n      containers:\n        - name: mc\n          image: itzg/minecraft-server\n          imagePullPolicy: Always\n          env:\n            - name: EULA\n              value: \"TRUE\"\n          volumeMounts:\n            - mountPath: /data\n              name: data\n          readinessProbe:\n            exec:\n              command:\n                - mc-monitor \n                - status \n                - --host \n                - localhost \n                - --port\n                - \"25565\"\n            initialDelaySeconds: 30\n            periodSeconds: 5\n            failureThreshold: 18\n  volumeClaimTemplates:\n    - metadata:\n        name: data\n      spec:\n        accessModes:\n          - ReadWriteOnce\n        resources:\n          requests:\n            storage: 1Gi\n---\napiVersion: v1\nkind: Service\nmetadata:\n  labels:\n    service: mc-example\n  name: mc-example\nspec:\n  ports:\n    - port: 25565\n      targetPort: 25565\n  selector:\n    app: mc-example\n  type: NodePort\n"
  },
  {
    "path": "examples/k8s/vanilla-deployment.yml",
    "content": "apiVersion: v1\nkind: Service\nmetadata:\n  name: mc-vanilla\nspec:\n  type: NodePort\n  ports:\n  - port: 25565\n    nodePort: 30000\n  selector:\n    app: mc-vanilla\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: mc-vanilla\nspec:\n  selector:\n    matchLabels:\n      app: mc-vanilla\n  template:\n    metadata:\n      labels:\n        app: mc-vanilla\n    spec:\n      containers:\n      - image: itzg/minecraft-server\n        name: mc-vanilla\n        env:\n          # Use secret in real usage\n        - name: EULA\n          value: \"true\"\n        ports:\n        - containerPort: 25565\n          name: main\n        readinessProbe:\n          exec:\n            command: [ \"/usr/local/bin/mc-monitor\", \"status\", \"--host\", \"localhost\" ]\n          # Give it i + p * f seconds to be ready, so 120 seconds\n          initialDelaySeconds: 20\n          periodSeconds: 5\n          failureThreshold: 20\n        # Monitor ongoing liveness\n        livenessProbe:\n          exec:\n            command: [\"/usr/local/bin/mc-monitor\", \"status\", \"--host\", \"localhost\"]\n          initialDelaySeconds: 120\n          periodSeconds: 60\n        volumeMounts:\n        - name: mc-data\n          mountPath: /data\n      volumes:\n      - name: mc-data\n        emptyDir: {}\n"
  },
  {
    "path": "examples/ketting/docker-compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: true\n      TYPE: KETTING\n      VERSION: 1.20.1\n      # Allocate API key from https://console.curseforge.com/\n      # and set in .env file making sure to double up dollar signs, such as\n      # CF_API_KEY=$$2a$$10$$....\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\n      CF_API_KEY: \"${CF_API_KEY}\"\n      CURSEFORGE_FILES: https://www.curseforge.com/minecraft/mc-mods/aquaculture\n    ports:\n      - \"25565:25565\""
  },
  {
    "path": "examples/lazymc/docker_compose.yml",
    "content": "# Lazymc requires that the minecraft server have a static IP.\n#\n# To ensure that our servers have a static IP we need to create\n# a network for our services to use.\n#\n# By default, Docker uses 172.17.0.0/16 subnet range.\n# So we need to create a new network in a different subnet\n# See the readme for more information.\n#\n# Please ensure that the subnet falls within the private CIDRs:\n# https://datatracker.ietf.org/doc/html/rfc1918#section-3\n#\n# And that it is not in use by anything else.\nnetworks:\n  minecraft-network:\n    driver: bridge    \n    ipam:\n      config:\n        - subnet: 172.18.0.0/16\n\nservices:\n  lazymc:\n    image: ghcr.io/joesturge/lazymc-docker-proxy:latest\n    # the IPs should start at .2 as .1 is reserved for the gateway\n    networks:\n      minecraft-network:\n        ipv4_address: 172.18.0.2\n    restart: unless-stopped\n    volumes:\n      # you should mount the minecraft server dir under /server, using read only.\n      - data:/server:ro\n      # you need to supply the docker socket, so that the container can run docker command\n      - /var/run/docker.sock:/var/run/docker.sock:ro\n    ports:\n      # lazymc-docker-proxy acts as a proxy, so there is\n      # no need to expose the server port on the Minecraft container\n      - \"25565:25565\"\n\n  # Standard Docker Minecraft server, also works with other server types\n  mc:\n    image: itzg/minecraft-server:java21\n    # Assign a static IP to the server container\n    networks:\n      minecraft-network:\n        ipv4_address: 172.18.0.3\n    # We need to add a label here so that lazymc-docker-proxy knows which\n    # container to manage\n    labels:\n      # Set lazymc.enabled to true to enable lazymc on this container\n      - lazymc.enabled=true\n      # Required to find the container to manage it\n      - lazymc.group=mc\n      # Point to the service name of the Minecraft server\n      - lazymc.server.address=mc:25565\n    tty: true\n    stdin_open: true\n    # This container should be managed solely by the lazymc container\n    # so set restart to no, or else the container will start again...\n    restart: no\n    environment:\n      EULA: \"TRUE\"\n    volumes:\n      - data:/data\n\nvolumes:\n  data:\n"
  },
  {
    "path": "examples/lazytainer/docker_compose.yml",
    "content": "services:\n  lazytainer:\n    image: ghcr.io/vmorganp/lazytainer:master\n    environment:\n      VERBOSE: false\n    ports:\n      - 25565:25565\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock:ro\n    labels:\n      - lazytainer.group.minecraft.sleepMethod=stop\n      - lazytainer.group.minecraft.ports=25565\n      - lazytainer.group.minecraft.minPacketThreshold=2 # Start after two incomming packets\n      - lazytainer.group.minecraft.inactiveTimeout=600 # 10 minutes, to allow the server to bootstrap. You can probably make this lower later if you want.\n    restart: unless-stopped\n    network_mode: bridge\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: TRUE\n      TYPE: PAPER\n      MEMORY: 4G\n    volumes:\n      - ./data:/data\n    labels:\n      - lazytainer.group=minecraft\n    depends_on:\n      - lazytainer\n    network_mode: service:lazytainer\n    tty: true\n    stdin_open: true\n    restart: unless-stopped\n"
  },
  {
    "path": "examples/mc-router-autoscale/compose-minimal.yml",
    "content": "# Source: https://github.com/itzg/mc-router/blob/main/examples/docker-autoscale/compose-minimal.yml\nservices:\n  router:\n    image: itzg/mc-router\n    environment:\n      IN_DOCKER: true\n      AUTO_SCALE_DOWN: true\n      AUTO_SCALE_UP: true\n      AUTO_SCALE_DOWN_AFTER: 2h\n      AUTO_SCALE_ASLEEP_MOTD: \"Server is asleep. Join again to wake it up!\"\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock:ro\n  vanilla:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"TRUE\"\n    labels:\n      mc-router.host: \"vanilla.example.com\"\n  paper:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"TRUE\"\n      TYPE: PAPER\n    labels:\n      mc-router.host: \"paper.example.com\"\n"
  },
  {
    "path": "examples/mc-router-autoscale/compose.yml",
    "content": "# Source: https://github.com/itzg/mc-router/blob/main/examples/docker-autoscale/compose.yml\n# This is a verbose example with comments and explanations for configuring auto-scaling behavior\n# for Docker backend servers. See compose-minimal.yml for a simple minimal example.\nservices:\n  router:\n    image: itzg/mc-router\n    environment:\n      IN_DOCKER: true\n      # Global auto-scaling settings for all docker-backend servers\n      # Settings can be overridden per-backend using labels\n      # as shown in the backend services below (except for AUTO_SCALE_DOWN_AFTER which is global only)\n      # Enable auto-scaling down after inactivity for all backends by default\n      AUTO_SCALE_DOWN: true\n      # Enable auto-scaling up after player join for all backends by default\n      AUTO_SCALE_UP: true\n      # Time of inactivity after which to scale down (default: 10m) - Global only setting\n      AUTO_SCALE_DOWN_AFTER: 2h\n      # MOTD to show when server is asleep (default: empty string - don't show MOTD, show server offline instead)\n      AUTO_SCALE_ASLEEP_MOTD: \"Server is asleep. Join again to wake it up!\"\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock:ro\n  vanilla:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"TRUE\"\n    labels:\n      # If global auto scaling settings are enabled, this backend will\n      # auto-scale without any additional auto-scale related configuration\n      mc-router.host: \"vanilla.example.com\"\n  fabric:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"TRUE\"\n      TYPE: FABRIC\n    labels:\n      mc-router.host: \"fabric.example.com\"\n      # Disable auto-scaling for this backend specifically\n      mc-router.auto-scale-up: false\n      mc-router.auto-scale-down: false\n  paper:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"TRUE\"\n      TYPE: PAPER\n    labels:\n      mc-router.host: \"paper.example.com\"\n      # Override asleep MOTD for this backend\n      mc-router.auto-scale-asleep-motd: \"Paper is folded. Join to unfold!\"\n"
  },
  {
    "path": "examples/modrinth/bmc4/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: true\n      MODPACK_PLATFORM: MODRINTH\n      # NOTE: v36 doesn't startup correctly\n      MODRINTH_MODPACK: https://modrinth.com/modpack/better-mc-forge-bmc4/version/v34.5\n      MODRINTH_OVERRIDES_EXCLUSIONS: |\n        config/paxi/datapacks/BE_default_endgen_fix*\n      MODRINTH_FORCE_SYNCHRONIZE: true\n      MEMORY: 4G\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - mc-data:/data\n      # or use a host directory binding\n#      - ./data:/data\nvolumes:\n  mc-data:"
  },
  {
    "path": "examples/modrinth/local-modpack/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    environment:\r\n      EULA: true\r\n      MODPACK_PLATFORM: MODRINTH\r\n      # Download the mrpack file from https://modrinth.com/modpack/cobblemon-fabric/version/1.4.1 and place in\r\n      # modpacks directory next to this compose file.\r\n      MODRINTH_MODPACK: /modpacks/Cobblemon Modpack [Fabric] 1.4.1.mrpack\r\n    ports:\r\n      -  \"25565:25565\"\r\n    volumes:\r\n      - mc-data:/data\r\n      - ./modpacks:/modpacks:ro\r\n\r\nvolumes:\r\n  mc-data: {}\r\n"
  },
  {
    "path": "examples/modrinth/modrinth-modpack/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    tty: true\n    stdin_open: true\n    ports:\n      - \"25565:25565\"\n    environment:\n      EULA: \"TRUE\"\n      MODPACK_PLATFORM: MODRINTH\n      MODRINTH_MODPACK: https://modrinth.com/modpack/cobblemon-fabric/version/1.6.1.4\n      # or for auto-upgrading to latest\n      # MODRINTH_MODPACK: https://modrinth.com/modpack/cobblemon-fabric\n      # or just cobblemon-fabric or 5FFgwNNP\n      # and could replace version URL with\n      # MODRINTH_VERSION: 98odLiu9\n      # MODRINTH_VERSION: 1.6.1.4\n      # MODRINTH_VERSION: \"Cobblemon [Fabric] 1.6.1.4\"\n    volumes:\n      # attach the relative directory 'data' to the container's /data path\n      - ./data:/data\n"
  },
  {
    "path": "examples/modrinth/modrinth-paper/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"true\"\n      TYPE: PAPER\n      VERSION: 1.19.3\n      MODRINTH_PROJECTS: \"coreprotect\"\n      MODRINTH_PROJECTS_DEFAULT_VERSION_TYPE: \"release\"\n    volumes:\n      - ./data:/data\n"
  },
  {
    "path": "examples/modrinth/modrinth-projects/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    tty: true\n    stdin_open: true\n    ports:\n      - \"25565:25565\"\n    environment:\n      EULA: \"TRUE\"\n      TYPE: FABRIC\n      MODRINTH_PROJECTS: fallingtree\n    volumes:\n      - data:/data\n\nvolumes:\n  data: {}"
  },
  {
    "path": "examples/modrinth/version-from-modrinth-projects/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: true\n      TYPE: paper\n      MODRINTH_PROJECTS: |\n        viaversion\n        viabackwards\n        griefprevention\n        discordsrv\n      VERSION_FROM_MODRINTH_PROJECTS: true\n      MEMORY: 2G\n    ports:\n      - \"25565:25565\""
  },
  {
    "path": "examples/mods-file/docker-compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"true\"\n      TYPE: FORGE\n      VERSION: 1.15.2\n      MODS_FILE: /extras/mods.txt\n      REMOVE_OLD_MODS: \"true\"\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - data:/data\n      - ./mods.txt:/extras/mods.txt:ro\n\nvolumes:\n  data:\n"
  },
  {
    "path": "examples/mods-file/mods.txt",
    "content": "https://edge.forgecdn.net/files/2965/233/Bookshelf-1.15.2-5.6.40.jar\nhttps://edge.forgecdn.net/files/2926/27/ProgressiveBosses-2.1.5-mc1.15.2.jar\nhttps://edge.forgecdn.net/files/3248/905/goblintraders-1.3.1-1.15.2.jar\nhttps://edge.forgecdn.net/files/3272/32/jei-1.15.2-6.0.3.16.jar\nhttps://edge.forgecdn.net/files/2871/647/ToastControl-1.15.2-3.0.1.jar\nhttps://edge.forgecdn.net/files/3101/903/Druidcraft-1.15-0.4.45.jar\nhttps://edge.forgecdn.net/files/2880/426/DefaultOptions_1.15.2-11.0.1.jar\nhttps://edge.forgecdn.net/files/2996/535/MekanismAdditions-1.15.2-9.10.9.422.jar\nhttps://edge.forgecdn.net/files/3147/275/blockcarpentry-1.15-0.8.2.jar\nhttps://edge.forgecdn.net/files/3005/715/refinedstorageaddons-0.6.3.jar\nhttps://edge.forgecdn.net/files/3140/146/rftoolsbase-1.15-1.1.10.jar\nhttps://edge.forgecdn.net/files/3236/649/cc-tweaked-1.15.2-1.95.3.jar\nhttps://edge.forgecdn.net/files/3048/970/light-overlay-4.7.2.jar\nhttps://edge.forgecdn.net/files/3015/904/randompatches-1.15.2-1.22.1.1.jar\nhttps://edge.forgecdn.net/files/3131/439/engineersdecor-1.15.2-1.1.4.jar\nhttps://edge.forgecdn.net/files/3051/255/carryon-1.15.2-1.13.0.5.jar\nhttps://edge.forgecdn.net/files/3175/752/EnderStorage-1.15.2-2.5.2.164-universal.jar\nhttps://edge.forgecdn.net/files/2997/84/Savage-and-Ravage-1.15.2-1.1.4.jar\nhttps://edge.forgecdn.net/files/3053/840/PackMenu-1.15.2-1.2.8.jar\nhttps://edge.forgecdn.net/files/3003/397/JEITweaker-1.15.2-1.0.1.3.jar\nhttps://edge.forgecdn.net/files/3069/489/absentbydesign-1.15.2-1.1.1.jar\nhttps://edge.forgecdn.net/files/2997/601/Upgrade-Aquatic-1.15.2-1.7.1.jar\nhttps://edge.forgecdn.net/files/2950/766/ReAuth-1.14-1.15-3.8.1.jar\nhttps://edge.forgecdn.net/files/3024/179/Powah-1.15.2-1.1.15.jar\nhttps://edge.forgecdn.net/files/3092/975/StorageDrawers-1.15.2-7.0.3.jar\nhttps://edge.forgecdn.net/files/2853/267/furniture-7.0.0-pre16-1.15.1.jar\nhttps://edge.forgecdn.net/files/2987/251/AppleSkin-mc1.15.2-forge-1.0.14.jar\nhttps://edge.forgecdn.net/files/2980/323/industrial-foregoing-1.15.2-2.3.3-e356e61.jar\nhttps://edge.forgecdn.net/files/3024/178/Lollipop-1.15.2-1.0.16.jar\nhttps://edge.forgecdn.net/files/3210/106/tablechair-1.4.jar\nhttps://edge.forgecdn.net/files/3047/358/xercapaint-1.15.2-3.3.jar\nhttps://edge.forgecdn.net/files/2873/657/BonsaiTrees-2.1.2.6.jar\nhttps://edge.forgecdn.net/files/2938/583/config-2-3.0.jar\nhttps://edge.forgecdn.net/files/2991/235/Waystones_1.15.2-6.0.2.jar\nhttps://edge.forgecdn.net/files/2957/23/OpenLoader-1.15.2-4.0.5.jar\nhttps://edge.forgecdn.net/files/3275/718/XaerosWorldMap_1.13.2_Forge_1.15.2.jar\nhttps://edge.forgecdn.net/files/2876/104/AI-Improvements-1.15.2-0.3.0.jar\nhttps://edge.forgecdn.net/files/3005/515/mysticallib-1.15.2-2.0.1.jar\nhttps://edge.forgecdn.net/files/3008/867/WAWLA-1.15.2-3.0.4.jar\nhttps://edge.forgecdn.net/files/2964/474/WailaHarvestability-mc1.15.2-1.1.12.jar\nhttps://edge.forgecdn.net/files/3103/508/Kiwi-1.15.2-2.8.5.jar\nhttps://edge.forgecdn.net/files/3128/662/Atum-1.15.2-2.1.12.jar\nhttps://edge.forgecdn.net/files/2988/910/Buzzier-Bees-1.15.2-1.5.2.jar\nhttps://edge.forgecdn.net/files/3030/627/u_team_core-1.15.2-3.0.2.169.jar\nhttps://edge.forgecdn.net/files/3003/984/SilentLib-1.15.2-4.6.6+59.jar\nhttps://edge.forgecdn.net/files/2968/353/refinedpipes-0.4.2.jar\nhttps://edge.forgecdn.net/files/3140/149/rftoolsbuilder-1.15-2.1.16.jar\nhttps://edge.forgecdn.net/files/3067/203/mightyarchitect-mc1.15.2_v0.5.jar\nhttps://edge.forgecdn.net/files/2980/153/EquipmentTooltips-1.15.2-1.4.3+14.jar\nhttps://edge.forgecdn.net/files/3103/510/FruitTrees-1.15.2-1.7.0.jar\nhttps://edge.forgecdn.net/files/2989/38/Atmospheric-1.15.2-1.4.1.jar\nhttps://edge.forgecdn.net/files/3096/836/rftoolscontrol-1.15-3.0.9.jar\nhttps://edge.forgecdn.net/files/2989/662/Enhanced-Mushrooms-1.15.2-v1.2.2.jar\nhttps://edge.forgecdn.net/files/3227/891/SimpleStorageNetwork-1.15.2-1.0.2.jar\nhttps://edge.forgecdn.net/files/3217/290/minecolonies-0.13.645-RELEASE-universal.jar\nhttps://edge.forgecdn.net/files/2935/384/SnowRealMagic-1.15.2-1.7.5.jar\nhttps://edge.forgecdn.net/files/2988/584/aiotbotania-1.15.2-1.2.3.jar\nhttps://edge.forgecdn.net/files/3255/408/mowziesmobs-1.5.15.jar\nhttps://edge.forgecdn.net/files/3062/510/HealthOverlay-1.15.2-1.0.2.jar\nhttps://edge.forgecdn.net/files/3023/121/neoncraft-1.1.jar\nhttps://edge.forgecdn.net/files/2997/617/Swamp-Expansion-1.15.2-1.7.3.jar\nhttps://edge.forgecdn.net/files/2980/252/titanium-1.15.2-2.4.2.jar\nhttps://edge.forgecdn.net/files/3152/946/rftoolsutility-1.15-2.1.20.jar\nhttps://edge.forgecdn.net/files/3083/277/swingthroughgrass-1.15.2-1.4.1.jar\nhttps://edge.forgecdn.net/files/3138/530/SilentGear-1.15.2-1.11.4+187.jar\nhttps://edge.forgecdn.net/files/3105/429/DarkUtilities-1.15.2-3.1.9.jar\nhttps://edge.forgecdn.net/files/3099/23/ImmersiveEngineering-1.15.2-4.1.1-125.jar\nhttps://edge.forgecdn.net/files/2856/529/FastFurnace-1.15.1-3.0.0.jar\nhttps://edge.forgecdn.net/files/2988/999/BiomesOPlenty-1.15.2-10.0.0.366-universal.jar\nhttps://edge.forgecdn.net/files/2989/95/The-Endergetic-Expansion-1.15.2-v1.3.2.jar\nhttps://edge.forgecdn.net/files/2993/960/FluxNetworks-1.15.2-5.0.3-4.jar\nhttps://edge.forgecdn.net/files/3028/611/ServerTabInfo-1.15.2-1.2.8.jar\nhttps://edge.forgecdn.net/files/3134/277/fairylights-3.0.15-1.15.2.jar\nhttps://edge.forgecdn.net/files/3048/54/useful_backpacks-1.15.2-1.10.3.77.jar\nhttps://edge.forgecdn.net/files/3273/515/mcw-trapdoors-1.0.2-mc1.15.2.jar\nhttps://edge.forgecdn.net/files/2986/639/CraftingTweaks_1.15.2-11.0.1.jar\nhttps://edge.forgecdn.net/files/3261/454/create-mc1.15.2_v0.3.1a.jar\nhttps://edge.forgecdn.net/files/3211/10/supermartijn642configlib-1.0.5-mc1.15.jar\nhttps://edge.forgecdn.net/files/2995/786/TerraForged-1.15.2-0.0.15.jar\nhttps://edge.forgecdn.net/files/2892/562/leavesdecayonotherleaves-1.1.jar"
  },
  {
    "path": "examples/mohist/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    ports:\n      - \"25565:25565\"\n    environment:\n      EULA: \"TRUE\"\n      TYPE: MOHIST\n      VERSION: 1.12.2\n      MOHIST_BUILD: 347\n    volumes:\n      - data:/data\n\nvolumes:\n  data: {}\n"
  },
  {
    "path": "examples/multi-project/README.md",
    "content": "This project demonstrates the use of two compose projects, `dbs` and `servers`, where the following capabilities are demonstrated:\r\n\r\n- Managing databases, such as MariaDB, in its own compose project: `dbs`\r\n- Using an `.env` file to avoid including user details within the compose file \r\n- Accessing the database from a separate project, `servers`, via an external network declaration\r\n  - LuckPerms is configured to access the MariaDB instance\r\n- Isolating the Minecraft server container by purposely **not** declaring port mappings\r\n- Running Waterfall as a proxy\r\n- Using configuration mount points to pre-configure Waterfall and the Minecraft server\r\n- Using Spiget to download plugins, in this case LuckPerms"
  },
  {
    "path": "examples/multi-project/dbs/docker-compose.yml",
    "content": "services:\r\n  mariadb:\r\n    image: mariadb:10\r\n    environment:\r\n      MARIADB_RANDOM_ROOT_PASSWORD: yes\r\n      MARIADB_DATABASE: luckperms\r\n      # These are loaded by compose from .env\r\n      MARIADB_USER: ${LUCKPERMS_USER}\r\n      MARIADB_PASSWORD: ${LUCKPERMS_PASSWORD}\r\n    volumes:\r\n      - mariadb:/var/lib/mysql\r\n\r\n  adminer:\r\n    image: adminer\r\n    ports:\r\n      - \"8806:8080\"\r\n\r\nvolumes:\r\n  mariadb: {}"
  },
  {
    "path": "examples/multi-project/servers/docker-compose.yml",
    "content": "services:\r\n  proxy:\r\n    image: itzg/mc-proxy\r\n    environment:\r\n      TYPE: WATERFALL\r\n    ports:\r\n      - \"25565:25577\"\r\n    volumes:\r\n      - waterfall:/server\r\n      - ./waterfall-config:/config\r\n# If you want to use Velocity here is a premade version that supports Velocity\r\n#  velocity-proxy:\r\n#    image: itzg/mc-proxy\r\n#    environment:\r\n#      TYPE: VELOCITY\r\n#    ports:\r\n#      - \"25565:25577\"\r\n#    volumes:\r\n#      - velocity:/server\r\n#      - ./velocity-config:/config\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    environment:\r\n      EULA: \"true\"\r\n      TYPE: PAPER\r\n      # 28140: luckperms\r\n      SPIGET_RESOURCES: \"28140\"\r\n      # since we're behind a proxy\r\n      ONLINE_MODE: \"false\"\r\n    volumes:\r\n      - mc:/data\r\n      # mainly to drop in config files specific to plugins\r\n      - ./mc-plugins:/plugins\r\n    networks:\r\n      # so proxy can reach us\r\n      - default\r\n      # so we can use databases project\r\n      - dbs\r\n\r\nvolumes:\r\n  mc: {}\r\n  waterfall: {}\r\n\r\nnetworks:\r\n  dbs:\r\n    # declared in ../dbs\r\n    external: true\r\n    name: dbs_default\r\n"
  },
  {
    "path": "examples/multi-project/servers/mc-plugins/LuckPerms/config.yml",
    "content": "####################################################################################################\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# |                                   __         __   ___  __         __                         | #\r\n# |                        |    |  | /  ` |__/  |__) |__  |__)  |\\/| /__`                        | #\r\n# |                        |___ \\__/ \\__, |  \\  |    |___ |  \\  |  | .__/                        | #\r\n# |                                                                                              | #\r\n# |                                     https://luckperms.net                                    | #\r\n# |                                                                                              | #\r\n# |  WIKI:        https://luckperms.net/wiki                                                     | #\r\n# |  DISCORD:     https://discord.gg/luckperms                                                   | #\r\n# |  BUG REPORTS: https://github.com/lucko/LuckPerms/issues                                      | #\r\n# |                                                                                              | #\r\n# |  Each option in this file is documented and explained here:                                  | #\r\n# |   ==>  https://luckperms.net/wiki/Configuration                                              | #\r\n# |                                                                                              | #\r\n# |  New options are not added to this file automatically. Default values are used if an         | #\r\n# |  option cannot be found. The latest config versions can be obtained at the link above.       | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n####################################################################################################\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# |                                                                                              | #\r\n# |                                      ESSENTIAL SETTINGS                                      | #\r\n# |                                                                                              | #\r\n# |                   Important settings that control how LuckPerms functions.                   | #\r\n# |                                                                                              | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# The name of the server, used for server specific permissions.\r\n#\r\n# - When set to \"global\" this setting is effectively ignored.\r\n# - In all other cases, the value here is added to all players in a \"server\" context.\r\n# - See: https://luckperms.net/wiki/Context\r\nserver: global\r\n\r\n# If the servers own UUID cache/lookup facility should be used when there is no record for a player\r\n# already in LuckPerms.\r\n#\r\n# - When this is set to 'false', commands using a player's username will not work unless the player\r\n#   has joined since LuckPerms was first installed.\r\n# - To get around this, you can use a player's uuid directly in the command, or enable this option.\r\n# - When this is set to 'true', the server facility is used. This may use a number of methods,\r\n#   including checking the servers local cache, or making a request to the Mojang API.\r\nuse-server-uuid-cache: false\r\n\r\n\r\n\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# |                                                                                              | #\r\n# |                                       STORAGE SETTINGS                                       | #\r\n# |                                                                                              | #\r\n# |                Controls which storage method LuckPerms will use to store data.               | #\r\n# |                                                                                              | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# How the plugin should store data\r\n#\r\n# - The various options are explained in more detail on the wiki:\r\n#   https://luckperms.net/wiki/Storage-types\r\n#\r\n# - Possible options:\r\n#\r\n#   |  Remote databases - require connection information to be configured below\r\n#   |=> MySQL\r\n#   |=> MariaDB (preferred over MySQL)\r\n#   |=> PostgreSQL\r\n#   |=> MongoDB\r\n#\r\n#   |  Flatfile/local database - don't require any extra configuration\r\n#   |=> H2 (preferred over SQLite)\r\n#   |=> SQLite\r\n#\r\n#   |  Readable & editable text files - don't require any extra configuration\r\n#   |=> YAML (.yml files)\r\n#   |=> JSON (.json files)\r\n#   |=> HOCON (.conf files)\r\n#   |=> TOML (.toml files)\r\n#   |\r\n#   | By default, user, group and track data is separated into different files. Data can be combined\r\n#   | and all stored in the same file by switching to a combined storage variant.\r\n#   | Just add '-combined' to the end of the storage-method, e.g. 'yaml-combined'\r\n#\r\n# - A H2 database is the default option.\r\n# - If you want to edit data manually in \"traditional\" storage files, we suggest using YAML.\r\nstorage-method: MariaDB\r\n\r\n# The following block defines the settings for remote database storage methods.\r\n#\r\n# - You don't need to touch any of the settings here if you're using a local storage method!\r\n# - The connection detail options are shared between all remote storage types.\r\ndata:\r\n\r\n  # Define the address and port for the database.\r\n  # - The standard DB engine port is used by default\r\n  #   (MySQL: 3306, PostgreSQL: 5432, MongoDB: 27017)\r\n  # - Specify as \"host:port\" if differs\r\n  address: mariadb\r\n\r\n  # The name of the database to store LuckPerms data in.\r\n  # - This must be created already. Don't worry about this setting if you're using MongoDB.\r\n  database: luckperms\r\n\r\n  # Credentials for the database.\r\n  username: luckperms\r\n  password: 'luckpermspw'\r\n\r\n  # These settings apply to the MySQL connection pool.\r\n  # - The default values will be suitable for the majority of users.\r\n  # - Do not change these settings unless you know what you're doing!\r\n  pool-settings:\r\n\r\n    # Sets the maximum size of the MySQL connection pool.\r\n    # - Basically this value will determine the maximum number of actual\r\n    #   connections to the database backend.\r\n    # - More information about determining the size of connection pools can be found here:\r\n    #   https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing\r\n    maximum-pool-size: 10\r\n\r\n    # Sets the minimum number of idle connections that the pool will try to maintain.\r\n    # - For maximum performance and responsiveness to spike demands, it is recommended to not set\r\n    #   this value and instead allow the pool to act as a fixed size connection pool.\r\n    #   (set this value to the same as 'maximum-pool-size')\r\n    minimum-idle: 10\r\n\r\n    # This setting controls the maximum lifetime of a connection in the pool in milliseconds.\r\n    # - The value should be at least 30 seconds less than any database or infrastructure imposed\r\n    #   connection time limit.\r\n    maximum-lifetime: 1800000 # 30 minutes\r\n\r\n    # This setting controls how frequently the pool will 'ping' a connection in order to prevent it\r\n    # from being timed out by the database or network infrastructure, measured in milliseconds.\r\n    # - The value should be less than maximum-lifetime and greater than 30000 (30 seconds).\r\n    # - Setting the value to zero will disable the keepalive functionality.\r\n    keepalive-time: 0\r\n\r\n    # This setting controls the maximum number of milliseconds that the plugin will wait for a\r\n    # connection from the pool, before timing out.\r\n    connection-timeout: 5000 # 5 seconds\r\n\r\n    # This setting allows you to define extra properties for connections.\r\n    #\r\n    # By default, the following options are set to enable utf8 encoding. (you may need to remove\r\n    # these if you are using PostgreSQL)\r\n    #   useUnicode: true\r\n    #   characterEncoding: utf8\r\n    #\r\n    # You can also use this section to disable SSL connections, by uncommenting the 'useSSL' and\r\n    # 'verifyServerCertificate' options below.\r\n    properties:\r\n      useUnicode: true\r\n      characterEncoding: utf8\r\n      #useSSL: false\r\n      #verifyServerCertificate: false\r\n\r\n  # The prefix for all LuckPerms SQL tables.\r\n  #\r\n  # - This only applies for remote SQL storage types (MySQL, MariaDB, etc).\r\n  # - Change this if you want to use different tables for different servers.\r\n  table-prefix: 'luckperms_'\r\n\r\n  # The prefix to use for all LuckPerms MongoDB collections.\r\n  #\r\n  # - This only applies for the MongoDB storage type.\r\n  # - Change this if you want to use different collections for different servers. The default is no\r\n  # prefix.\r\n  mongodb-collection-prefix: ''\r\n\r\n  # The connection string URI to use to connect to the MongoDB instance.\r\n  #\r\n  # - When configured, this setting will override anything defined in the address, database,\r\n  #   username or password fields above.\r\n  # - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it\r\n  #   below.\r\n  # - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/\r\n  mongodb-connection-uri: ''\r\n\r\n# Define settings for a \"split\" storage setup.\r\n#\r\n# - This allows you to define a storage method for each type of data.\r\n# - The connection options above still have to be correct for each type here.\r\nsplit-storage:\r\n  # Don't touch this if you don't want to use split storage!\r\n  enabled: false\r\n  methods:\r\n    # These options don't need to be modified if split storage isn't enabled.\r\n    user: h2\r\n    group: h2\r\n    track: h2\r\n    uuid: h2\r\n    log: h2\r\n\r\n\r\n\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# |                                                                                              | #\r\n# |                            UPDATE PROPAGATION & MESSAGING SERVICE                            | #\r\n# |                                                                                              | #\r\n# |    Controls the ways in which LuckPerms will sync data & notify other servers of changes.    | #\r\n# |     These options are documented on greater detail on the wiki under \"Instant Updates\".      | #\r\n# |                                                                                              | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# This option controls how frequently LuckPerms will perform a sync task.\r\n#\r\n# - A sync task will refresh all data from the storage, and ensure that the most up-to-date data is\r\n#   being used by the plugin.\r\n# - This is disabled by default, as most users will not need it. However, if you're using a remote\r\n#   storage type without a messaging service setup, you may wish to set this to something like 3.\r\n# - Set to -1 to disable the task completely.\r\nsync-minutes: -1\r\n\r\n# If the file watcher should be enabled.\r\n#\r\n# - When using a file-based storage type, LuckPerms can monitor the data files for changes, and\r\n#   automatically update when changes are detected.\r\n# - If you don't want this feature to be active, set this option to false.\r\nwatch-files: true\r\n\r\n# Define which messaging service should be used by the plugin.\r\n#\r\n# - If enabled and configured, LuckPerms will use the messaging service to inform other connected\r\n#   servers of changes.\r\n# - Use the command \"/lp networksync\" to manually push changes.\r\n# - Data is NOT stored using this service. It is only used as a messaging platform.\r\n#\r\n# - If you decide to enable this feature, you should set \"sync-minutes\" to -1, as there is no need\r\n#   for LuckPerms to poll the database for changes.\r\n#\r\n# - Possible options:\r\n#   => sql       Uses the SQL database to form a queue system for communication. Will only work when\r\n#                'storage-method' is set to MySQL or MariaDB. This is chosen by default if the\r\n#                option is set to 'auto' and SQL storage is in use. Set to 'notsql' to disable this.\r\n#   => pluginmsg Uses the plugin messaging channels to communicate with the proxy.\r\n#                LuckPerms must be installed on your proxy & all connected servers backend servers.\r\n#                Won't work if you have more than one proxy.\r\n#   => lilypad   Uses LilyPad pub-sub to push changes. You need to have the LilyPad-Connect plugin\r\n#                installed.\r\n#   => redis     Uses Redis pub-sub to push changes. Your server connection info must be configured\r\n#                below.\r\n#   => rabbitmq  Uses RabbitMQ pub-sub to push changes. Your server connection info must be\r\n#                configured below.\r\n#   => custom    Uses a messaging service provided using the LuckPerms API.\r\n#   => auto      Attempts to automatically setup a messaging service using redis or sql.\r\nmessaging-service: auto\r\n\r\n# If LuckPerms should automatically push updates after a change has been made with a command.\r\nauto-push-updates: true\r\n\r\n# If LuckPerms should push logging entries to connected servers via the messaging service.\r\npush-log-entries: true\r\n\r\n# If LuckPerms should broadcast received logging entries to players on this platform.\r\n#\r\n# - If you have LuckPerms installed on your backend servers as well as a BungeeCord proxy, you\r\n#   should set this option to false on either your backends or your proxies, to avoid players being\r\n#   messaged twice about log entries.\r\nbroadcast-received-log-entries: true\r\n\r\n# Settings for Redis.\r\n# Port 6379 is used by default; set address to \"host:port\" if differs\r\nredis:\r\n  enabled: false\r\n  address: localhost\r\n  username: ''\r\n  password: ''\r\n\r\n# Settings for RabbitMQ.\r\n# Port 5672 is used by default; set address to \"host:port\" if differs\r\nrabbitmq:\r\n  enabled: false\r\n  address: localhost\r\n  vhost: '/'\r\n  username: 'guest'\r\n  password: 'guest'\r\n\r\n\r\n\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# |                                                                                              | #\r\n# |                                    CUSTOMIZATION SETTINGS                                    | #\r\n# |                                                                                              | #\r\n# |              Settings that allow admins to customize the way LuckPerms operates.             | #\r\n# |                                                                                              | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# Controls how temporary permissions/parents/meta should be accumulated.\r\n#\r\n# - The default behaviour is \"deny\".\r\n# - This behaviour can also be specified when the command is executed. See the command usage\r\n#   documentation for more info.\r\n#\r\n# - Possible options:\r\n#   => accumulate   durations will be added to the existing expiry time\r\n#   => replace      durations will be replaced if the new duration is later than the current\r\n#                   expiration\r\n#   => deny         the command will just fail if you try to add another node with the same expiry\r\ntemporary-add-behaviour: deny\r\n\r\n# Controls how LuckPerms will determine a users \"primary\" group.\r\n#\r\n# - The meaning and influence of \"primary groups\" are explained in detail on the wiki.\r\n# - The preferred approach is to let LuckPerms automatically determine a users primary group\r\n#   based on the relative weight of their parent groups.\r\n#\r\n# - Possible options:\r\n#   => stored                  use the value stored against the users record in the file/database\r\n#   => parents-by-weight       just use the users most highly weighted parent\r\n#   => all-parents-by-weight   same as above, but calculates based upon all parents inherited from\r\n#                              both directly and indirectly\r\nprimary-group-calculation: parents-by-weight\r\n\r\n# If the plugin should check for \"extra\" permissions with users run LP commands.\r\n#\r\n# - These extra permissions allow finer control over what users can do with each command, and who\r\n#   they have access to edit.\r\n# - The nature of the checks are documented on the wiki under \"Argument based command permissions\".\r\n# - Argument based permissions are *not* static, unlike the 'base' permissions, and will depend upon\r\n#   the arguments given within the command.\r\nargument-based-command-permissions: false\r\n\r\n# If the plugin should check whether senders are a member of a given group before they're able to\r\n# edit the groups data or add/remove other users to/from it.\r\n# Note: these limitations do not apply to the web editor!\r\nrequire-sender-group-membership-to-modify: false\r\n\r\n# If the plugin should send log notifications to users whenever permissions are modified.\r\n#\r\n# - Notifications are only sent to those with the appropriate permission to receive them\r\n# - They can also be temporarily enabled/disabled on a per-user basis using\r\n#   '/lp log notify <on|off>'\r\nlog-notify: true\r\n\r\n# Defines a list of log entries which should not be sent as notifications to users.\r\n#\r\n# - Each entry in the list is a RegEx expression which is matched against the log entry description.\r\nlog-notify-filtered-descriptions:\r\n#  - \"parent add example\"\r\n\r\n# If LuckPerms should automatically install translation bundles and periodically update them.\r\nauto-install-translations: true\r\n\r\n# Defines the options for prefix and suffix stacking.\r\n#\r\n# - The feature allows you to display multiple prefixes or suffixes alongside a players username in\r\n#   chat.\r\n# - It is explained and documented in more detail on the wiki under \"Prefix & Suffix Stacking\".\r\n#\r\n# - The options are divided into separate sections for prefixes and suffixes.\r\n# - The 'duplicates' setting refers to how duplicate elements are handled. Can be 'retain-all',\r\n#   'first-only' or 'last-only'.\r\n# - The value of 'start-spacer' is included at the start of the resultant prefix/suffix.\r\n# - The value of 'end-spacer' is included at the end of the resultant prefix/suffix.\r\n# - The value of 'middle-spacer' is included between each element in the resultant prefix/suffix.\r\n#\r\n# - Possible format options:\r\n#   => highest                        Selects the value with the highest weight, from all values\r\n#                                     held by or inherited by the player.\r\n#\r\n#   => lowest                         Same as above, except takes the one with the lowest weight.\r\n#\r\n#   => highest_own                    Selects the value with the highest weight, but will not\r\n#                                     accept any inherited values.\r\n#\r\n#   => lowest_own                     Same as above, except takes the value with the lowest weight.\r\n#\r\n#   => highest_inherited              Selects the value with the highest weight, but will only\r\n#                                     accept inherited values.\r\n#\r\n#   => lowest_inherited               Same as above, except takes the value with the lowest weight.\r\n#\r\n#   => highest_on_track_<track>       Selects the value with the highest weight, but only if the\r\n#                                     value was inherited from a group on the given track.\r\n#\r\n#   => lowest_on_track_<track>        Same as above, except takes the value with the lowest weight.\r\n#\r\n#   => highest_not_on_track_<track>   Selects the value with the highest weight, but only if the\r\n#                                     value was inherited from a group not on the given track.\r\n#\r\n#   => lowest_not_on_track_<track>    Same as above, except takes the value with the lowest weight.\r\n#\r\n#   => highest_from_group_<group>     Selects the value with the highest weight, but only if the\r\n#                                     value was inherited from the given group.\r\n#\r\n#   => lowest_from_group_<group>      Same as above, except takes the value with the lowest weight.\r\n#\r\n#   => highest_not_from_group_<group> Selects the value with the highest weight, but only if the\r\n#                                     value was not inherited from the given group.\r\n#\r\n#   => lowest_not_from_group_<group>  Same as above, except takes the value with the lowest weight.\r\nmeta-formatting:\r\n  prefix:\r\n    format:\r\n      - \"highest\"\r\n    duplicates: first-only\r\n    start-spacer: \"\"\r\n    middle-spacer: \" \"\r\n    end-spacer: \"\"\r\n  suffix:\r\n    format:\r\n      - \"highest\"\r\n    duplicates: first-only\r\n    start-spacer: \"\"\r\n    middle-spacer: \" \"\r\n    end-spacer: \"\"\r\n\r\n\r\n\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# |                                                                                              | #\r\n# |                            PERMISSION CALCULATION AND INHERITANCE                            | #\r\n# |                                                                                              | #\r\n# |    Modify the way permission checks, meta lookups and inheritance resolutions are handled.   | #\r\n# |                                                                                              | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# The algorithm LuckPerms should use when traversing the \"inheritance tree\".\r\n#\r\n# - Possible options:\r\n#   => breadth-first            See: https://en.wikipedia.org/wiki/Breadth-first_search\r\n#   => depth-first-pre-order    See: https://en.wikipedia.org/wiki/Depth-first_search\r\n#   => depth-first-post-order   See: https://en.wikipedia.org/wiki/Depth-first_search\r\ninheritance-traversal-algorithm: depth-first-pre-order\r\n\r\n# If a final sort according to \"inheritance rules\" should be performed after the traversal algorithm\r\n# has resolved the inheritance tree.\r\n#\r\n# \"Inheritance rules\" refers to things such as group weightings, primary group status, and the\r\n# natural contextual ordering of the group nodes.\r\n#\r\n# Setting this to 'true' will allow for the inheritance rules to take priority over the structure of\r\n# the inheritance tree.\r\n#\r\n# Effectively when this setting is 'true': the tree is flattened, and rules applied afterwards,\r\n# and when this setting is 'false':, the rules are just applied during each step of the traversal.\r\npost-traversal-inheritance-sort: false\r\n\r\n# Defines the mode used to determine whether a set of contexts are satisfied.\r\n#\r\n# - Possible options:\r\n#   => at-least-one-value-per-key   Set A will be satisfied by another set B, if at least one of the\r\n#                                   key-value entries per key in A are also in B.\r\n#   => all-values-per-key           Set A will be satisfied by another set B, if all key-value\r\n#                                   entries in A are also in B.\r\ncontext-satisfy-mode: at-least-one-value-per-key\r\n\r\n# LuckPerms has a number of built-in contexts. These can be disabled by adding the context key to\r\n# the list below.\r\ndisabled-contexts:\r\n#  - \"world\"\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# | Permission resolution settings                                                               | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# If users on this server should have their global permissions applied.\r\n# When set to false, only server specific permissions will apply for users on this server\r\ninclude-global: true\r\n\r\n# If users on this server should have their global world permissions applied.\r\n# When set to false, only world specific permissions will apply for users on this server\r\ninclude-global-world: true\r\n\r\n# If users on this server should have global (non-server specific) groups applied\r\napply-global-groups: true\r\n\r\n# If users on this server should have global (non-world specific) groups applied\r\napply-global-world-groups: true\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# | Meta lookup settings                                                                         | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# Defines how meta values should be selected.\r\n#\r\n# - Possible options:\r\n#   => inheritance      Selects the meta value that was inherited first\r\n#   => highest-number   Selects the highest numerical meta value\r\n#   => lowest-number    Selects the lowest numerical meta value\r\nmeta-value-selection-default: inheritance\r\n\r\n# Defines how meta values should be selected per key.\r\nmeta-value-selection:\r\n#  max-homes: highest-number\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# | Inheritance settings                                                                         | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# If the plugin should apply wildcard permissions.\r\n#\r\n# - If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered\r\n#   permissions matching the wildcard.\r\napply-wildcards: true\r\n\r\n# If LuckPerms should resolve and apply permissions according to the Sponge style implicit wildcard\r\n# inheritance system.\r\n#\r\n# - That being: If a user has been granted \"example\", then the player should have also be\r\n#   automatically granted \"example.function\", \"example.another\", \"example.deeper.nesting\",\r\n#   and so on.\r\napply-sponge-implicit-wildcards: false\r\n\r\n# If the plugin should apply negated Bukkit default permissions before it considers wildcard\r\n# assignments.\r\n#\r\n# - Plugin authors can define permissions which explicitly should not be given automatically to OPs.\r\n#   This is usually used for so called \"anti-permissions\" - permissions which, when granted, apply\r\n#   something negative.\r\n# - If this option is set to true, LuckPerms will consider any negated declarations made by\r\n#   plugins before it considers wildcards. (similar to the way the OP system works)\r\n# - If this option is set to false, LuckPerms will consider any wildcard assignments first.\r\napply-default-negated-permissions-before-wildcards: false\r\n\r\n# If the plugin should parse regex permissions.\r\n#\r\n# - If set to true, LuckPerms will detect regex permissions, marked with \"r=\" at the start of the\r\n#   node, and resolve & apply all registered permissions matching the regex.\r\napply-regex: true\r\n\r\n# If the plugin should complete and apply shorthand permissions.\r\n#\r\n# - If set to true, LuckPerms will detect and expand shorthand node patterns.\r\napply-shorthand: true\r\n\r\n# If the plugin should apply Bukkit child permissions.\r\n#\r\n# - Plugin authors can define custom permissions structures for their plugin, which will be resolved\r\n#   and used by LuckPerms if this setting is enabled.\r\napply-bukkit-child-permissions: true\r\n\r\n# If the plugin should apply Bukkit default permissions.\r\n#\r\n# - Plugin authors can define permissions which should be given to all users by default, or setup\r\n#   permissions which should/shouldn't be given to opped players.\r\n# - If this option is set to false, LuckPerms will ignore these defaults.\r\napply-bukkit-default-permissions: true\r\n\r\n# If the plugin should apply attachment permissions.\r\n#\r\n# - Other plugins on the server are able to add their own \"permission attachments\" to players.\r\n# - This allows them to grant players additional permissions which last until the end of the\r\n#   session, or until they're removed.\r\n# - If this option is set to false, LuckPerms will not include these attachment permissions when\r\n#   considering if a player should have access to a certain permission.\r\napply-bukkit-attachment-permissions: true\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# | Extra settings                                                                               | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# A list of context calculators which will be skipped when calculating contexts.\r\n#\r\n# - You can disable context calculators by either:\r\n#   => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator)\r\n#   => specifying a sub-section of the Java package used by the calculator (e.g. com.example)\r\ndisabled-context-calculators: []\r\n\r\n# Allows you to set \"aliases\" for the worlds sent forward for context calculation.\r\n#\r\n# - These aliases are provided in addition to the real world name. Applied recursively.\r\n# - Remove the comment characters for the default aliases to apply.\r\nworld-rewrite:\r\n#  world_nether: world\r\n#  world_the_end: world\r\n\r\n# Define special group weights for this server.\r\n#\r\n# - Group weights can also be applied directly to group data, using the setweight command.\r\n# - This section allows weights to be set on a per-server basis.\r\ngroup-weight:\r\n#  admin: 10\r\n\r\n\r\n\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# |                                                                                              | #\r\n# |                                      FINE TUNING OPTIONS                                     | #\r\n# |                                                                                              | #\r\n# |     A number of more niche settings for tweaking and changing behaviour. The section also    | #\r\n# | contains toggles for some more specialised features. It is only necessary to make changes to | #\r\n# |                  these options if you want to fine-tune LuckPerms behaviour.                 | #\r\n# |                                                                                              | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# | Server Operator (OP) settings                                                                | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# Controls whether server operators should exist at all.\r\n#\r\n# - When set to 'false', all players will be de-opped, and the /op and /deop commands will be\r\n#   disabled. Note that vanilla features like the spawn-protection require an operator on the\r\n#   server to work.\r\nenable-ops: true\r\n\r\n# Enables or disables a special permission based system in LuckPerms for controlling OP status.\r\n#\r\n# - If set to true, any user with the permission \"luckperms.autoop\" will automatically be granted\r\n#   server operator status. This permission can be inherited, or set on specific servers/worlds,\r\n#   temporarily, etc.\r\n# - Additionally, setting this to true will force the \"enable-ops\" option above to false. All users\r\n#   will be de-opped unless they have the permission node, and the op/deop commands will be\r\n#   disabled.\r\n# - It is recommended that you use this option instead of assigning a single '*' permission.\r\nauto-op: false\r\n\r\n# Defines if \"opped\" players should be able to use all LuckPerms commands by default.\r\n#\r\n# - Set to false to only allow users who have the permissions access to the commands\r\ncommands-allow-op: true\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# | Vault integration settings                                                                   | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# If Vault lookups for offline players on the main server thread should be enabled.\r\n#\r\n# LuckPerms has a \"catch\" for plugins attempting to perform unsafe offline player data lookups\r\n# from the main server thread. This catch raises an exception (causes an error to occur) when unsafe\r\n# lookups are made, instead of allowing the lookup to happen, which would likely cause the server\r\n# to lag.\r\n#\r\n# However, if you're willing to accept the consequences, the catch can be disabled by setting this\r\n# option to 'true.\r\nvault-unsafe-lookups: false\r\n\r\n# If LuckPerms should use the 'display name' of a group when returning groups in Vault API calls.\r\n#\r\n# - When this option is set to true, the display name of the group is returned.\r\n# - When this option is set to false, the standard name/id of the group is returned.\r\nvault-group-use-displaynames: true\r\n\r\n# Controls which group LuckPerms should use for NPC players when handling Vault requests.\r\n#\r\n# - As NPCs aren't actually real players, LuckPerms does not load any user data for them. This\r\n#   becomes an issue when plugins want to check for their permissions using Vault.\r\n# - As a solution, Vault checks for NPCs fallback to a group, which is defined below.\r\nvault-npc-group: default\r\n\r\n# Controls how LuckPerms should consider the OP status of NPC players when handing Vault requests.\r\n#\r\n# - If you want NPCs to have the same permissions as \"normal\" players, set this option to false.\r\n# - If you want NPCs to have OP status, set this option to true.\r\nvault-npc-op-status: false\r\n\r\n# If the vault-server option below should be used.\r\n#\r\n# - When this option is set to false, the server value defined above under \"server\" is used.\r\nuse-vault-server: false\r\n\r\n# The name of the server used within Vault operations.\r\n#\r\n# - If you don't want Vault operations to be server specific, set this to \"global\".\r\n# - Will only take effect if use-vault-server is set to true above.\r\nvault-server: global\r\n\r\n# If global permissions should be considered when retrieving meta or player groups\r\nvault-include-global: true\r\n\r\n# If Vault operations should ignore any world arguments if supplied.\r\nvault-ignore-world: false\r\n\r\n# +----------------------------------------------------------------------------------------------+ #\r\n# | Miscellaneous (and rarely used) settings                                                     | #\r\n# +----------------------------------------------------------------------------------------------+ #\r\n\r\n# If LuckPerms should produce extra logging output when it handles logins.\r\n#\r\n# - Useful if you're having issues with UUID forwarding or data not being loaded.\r\ndebug-logins: false\r\n\r\n# If LuckPerms should allow usernames with non alphanumeric characters.\r\n#\r\n# - Note that due to the design of the storage implementation, usernames must still be 16 characters\r\n#   or less.\r\nallow-invalid-usernames: false\r\n\r\n# If LuckPerms should not require users to confirm bulkupdate operations.\r\n#\r\n# - When set to true, operations will be executed immediately.\r\n# - This is not recommended, as bulkupdate has the potential to irreversibly delete large amounts of\r\n#   data, and is not designed to be executed automatically.\r\n# - If automation is needed, users should prefer using the LuckPerms API.\r\nskip-bulkupdate-confirmation: false\r\n\r\n# If LuckPerms should prevent bulkupdate operations.\r\n#\r\n# - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work.\r\n# - When set to false, bulkupdate operations will be allowed via the console.\r\ndisable-bulkupdate: false\r\n\r\n# If LuckPerms should allow a users primary group to be removed with the 'parent remove' command.\r\n#\r\n# - When this happens, the plugin will set their primary group back to default.\r\nprevent-primary-group-removal: false\r\n\r\n# If LuckPerms should update the list of commands sent to the client when permissions are changed.\r\nupdate-client-command-list: true\r\n\r\n# If LuckPerms should attempt to register \"Brigadier\" command list data for its commands.\r\nregister-command-list-data: true\r\n\r\n# If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands.\r\n# See here for more info: https://minecraft.wiki/Commands#Target_selectors\r\nresolve-command-selectors: false\r\n"
  },
  {
    "path": "examples/multi-project/servers/velocity-config/velocity.toml",
    "content": "# Config version. Do not change this\nconfig-version = \"2.6\"\n\n# What port should the proxy be bound to? By default, we'll bind to all addresses on port 25565.\nbind = \"0.0.0.0:25565\"\n\n# What should be the MOTD? This gets displayed when the player adds your server to\n# their server list. Only MiniMessage format is accepted.\nmotd = \"<#09add3>A Velocity Server\"\n\n# What should we display for the maximum number of players? (Velocity does not support a cap\n# on the number of players online.)\nshow-max-players = 500\n\n# Should we authenticate players with Mojang? By default, this is on.\nonline-mode = true\n\n# Should the proxy enforce the new public key security standard? By default, this is on.\nforce-key-authentication = true\n\n# If client's ISP/AS sent from this proxy is different from the one from Mojang's\n# authentication server, the player is kicked. This disallows some VPN and proxy\n# connections but is a weak form of protection.\nprevent-client-proxy-connections = false\n\n# Should we forward IP addresses and other data to backend servers?\n# Available options:\n# - \"none\":        No forwarding will be done. All players will appear to be connecting\n#                  from the proxy and will have offline-mode UUIDs.\n# - \"legacy\":      Forward player IPs and UUIDs in a BungeeCord-compatible format. Use this\n#                  if you run servers using Minecraft 1.12 or lower.\n# - \"bungeeguard\": Forward player IPs and UUIDs in a format supported by the BungeeGuard\n#                  plugin. Use this if you run servers using Minecraft 1.12 or lower, and are\n#                  unable to implement network level firewalling (on a shared host).\n# - \"modern\":      Forward player IPs and UUIDs as part of the login process using\n#                  Velocity's native forwarding. Only applicable for Minecraft 1.13 or higher.\nplayer-info-forwarding-mode = \"NONE\"\n\n# If you are using modern or BungeeGuard IP forwarding, configure a file that contains a unique secret here.\n# The file is expected to be UTF-8 encoded and not empty.\nforwarding-secret-file = \"forwarding.secret\"\n\n# Announce whether or not your server supports Forge. If you run a modded server, we\n# suggest turning this on.\n# \n# If your network runs one modpack consistently, consider using ping-passthrough = \"mods\"\n# instead for a nicer display in the server list.\nannounce-forge = false\n\n# If enabled (default is false) and the proxy is in online mode, Velocity will kick\n# any existing player who is online if a duplicate connection attempt is made.\nkick-existing-players = false\n\n# Should Velocity pass server list ping requests to a backend server?\n# Available options:\n# - \"disabled\":    No pass-through will be done. The velocity.toml and server-icon.png\n#                  will determine the initial server list ping response.\n# - \"mods\":        Passes only the mod list from your backend server into the response.\n#                  The first server in your try list (or forced host) with a mod list will be\n#                  used. If no backend servers can be contacted, Velocity won't display any\n#                  mod information.\n# - \"description\": Uses the description and mod list from the backend server. The first\n#                  server in the try (or forced host) list that responds is used for the\n#                  description and mod list.\n# - \"all\":         Uses the backend server's response as the proxy response. The Velocity\n#                  configuration is used if no servers could be contacted.\nping-passthrough = \"DISABLED\"\n\n# If not enabled (default is true) player IP addresses will be replaced by <ip address withheld> in logs\nenable-player-address-logging = true\n\n[servers]\n# Configure your servers here. Each key represents the server's name, and the value\n# represents the IP address of the server to connect to.\nlobby = \"127.0.0.1:30066\"\nfactions = \"127.0.0.1:30067\"\nminigames = \"127.0.0.1:30068\"\n\n# In what order we should try servers when a player logs in or is kicked from a server.\ntry = [\n    \"lobby\"\n]\n\n[forced-hosts]\n# Configure your forced hosts here.\n\"lobby.example.com\" = [\n    \"lobby\"\n]\n\"factions.example.com\" = [\n    \"factions\"\n]\n\"minigames.example.com\" = [\n    \"minigames\"\n]\n\n[advanced]\n# How large a Minecraft packet has to be before we compress it. Setting this to zero will\n# compress all packets, and setting it to -1 will disable compression entirely.\ncompression-threshold = 256\n\n# How much compression should be done (from 0-9). The default is -1, which uses the\n# default level of 6.\ncompression-level = -1\n\n# How fast (in milliseconds) are clients allowed to connect after the last connection? By\n# default, this is three seconds. Disable this by setting this to 0.\nlogin-ratelimit = 3000\n\n# Specify a custom timeout for connection timeouts here. The default is five seconds.\nconnection-timeout = 5000\n\n# Specify a read timeout for connections here. The default is 30 seconds.\nread-timeout = 30000\n\n# Enables compatibility with HAProxy's PROXY protocol. If you don't know what this is for, then\n# don't enable it.\nhaproxy-protocol = false\n\n# Enables TCP fast open support on the proxy. Requires the proxy to run on Linux.\ntcp-fast-open = false\n\n# Enables BungeeCord plugin messaging channel support on Velocity.\nbungee-plugin-message-channel = true\n\n# Shows ping requests to the proxy from clients.\nshow-ping-requests = false\n\n# By default, Velocity will attempt to gracefully handle situations where the user unexpectedly\n# loses connection to the server without an explicit disconnect message by attempting to fall the\n# user back, except in the case of read timeouts. BungeeCord will disconnect the user instead. You\n# can disable this setting to use the BungeeCord behavior.\nfailover-on-unexpected-server-disconnect = true\n\n# Declares the proxy commands to 1.13+ clients.\nannounce-proxy-commands = true\n\n# Enables the logging of commands\nlog-command-executions = false\n\n# Enables logging of player connections when connecting to the proxy, switching servers\n# and disconnecting from the proxy.\nlog-player-connections = true\n\n[query]\n# Whether to enable responding to GameSpy 4 query responses or not.\nenabled = false\n\n# If query is enabled, on what port should the query protocol listen on?\nport = 25565\n\n# This is the map name that is reported to the query services.\nmap = \"Velocity\"\n\n# Whether plugins should be shown in query response by default or not\nshow-plugins = false\n"
  },
  {
    "path": "examples/multi-project/servers/waterfall-config/config.yml",
    "content": "server_connect_timeout: 5000\r\nenforce_secure_profile: false\r\nremote_ping_cache: -1\r\nforge_support: true\r\nplayer_limit: -1\r\npermissions:\r\n  default:\r\n    - bungeecord.command.server\r\n    - bungeecord.command.list\r\n  admin:\r\n    - bungeecord.command.alert\r\n    - bungeecord.command.end\r\n    - bungeecord.command.ip\r\n    - bungeecord.command.reload\r\n    - bungeecord.command.kick\r\ntimeout: 30000\r\nlog_commands: false\r\nnetwork_compression_threshold: 256\r\nonline_mode: true\r\ndisabled_commands:\r\n  - disabledcommandhere\r\nservers:\r\n  mc:\r\n    motd: 'Lobby'\r\n    address: mc:25565\r\n    restricted: false\r\nlisteners:\r\n  - query_port: 25577\r\n    motd: '&1Another Bungee server'\r\n    tab_list: GLOBAL_PING\r\n    query_enabled: false\r\n    proxy_protocol: false\r\n    forced_hosts:\r\n      pvp.md-5.net: pvp\r\n    ping_passthrough: false\r\n    priorities:\r\n      - mc\r\n    bind_local_address: true\r\n    host: 0.0.0.0:25577\r\n    max_players: 1\r\n    tab_size: 60\r\n    force_default_server: false\r\nip_forward: false\r\nremote_ping_timeout: 5000\r\nprevent_proxy_connections: false\r\ngroups:\r\n  md_5:\r\n    - admin\r\nconnection_throttle: 4000\r\nstats: 39440d41-8d83-40cb-a6a7-d95c3197c4d2\r\nconnection_throttle_limit: 3\r\nlog_pings: true\r\n"
  },
  {
    "path": "examples/multi-project/servers/waterfall-config/waterfall.yml",
    "content": "plugin_channel_name_limit: 128\r\nuse_netty_dns_resolver: true\r\ndisable_modern_tab_limiter: true\r\nlog_initial_handler_connections: true\r\nthrottling:\r\n  tab_complete: 1000\r\ngame_version: ''\r\ndisable_tab_list_rewrite: true\r\nregistered_plugin_channels_limit: 128\r\ndisable_entity_metadata_rewrite: false\r\n"
  },
  {
    "path": "examples/multiline-motd/docker-compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: true\n      MOTD: |\n        A §l§cMinecraft§r server\n        line two\n# or\n#      MOTD: \"line one\\nline two\"\n    ports:\n      - \"25565:25565\""
  },
  {
    "path": "examples/neoforge/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    environment:\r\n      EULA: true\r\n      TYPE: NEOFORGE\r\n      VERSION: latest\r\n      NEOFORGE_VERSION: beta\r\n    ports:\r\n      - \"25565:25565\"\r\n    volumes:\r\n      - mc:/data\r\nvolumes:\r\n  mc: {}"
  },
  {
    "path": "examples/ngrok/compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    tty: true\r\n    stdin_open: true\r\n    ports:\r\n      - \"25565:25565\"\r\n    environment:\r\n      EULA: \"TRUE\"\r\n    restart: unless-stopped\r\n    volumes:\r\n      # attach the relative directory 'data' to the container's /data path\r\n      - ./data:/data\r\n\r\n  ngrok:\r\n    image: ngrok/ngrok:latest\r\n    command:\r\n      - \"tcp\"\r\n      - \"mc:25565\"\r\n    environment:\r\n      NGROK_AUTHTOKEN: <YourAuthTokenHere>\r\n    ports:\r\n      - 4551:4551"
  },
  {
    "path": "examples/optimized-server-config/README.md",
    "content": "# Optimized Server Configuration (Paper, Purpur, Pufferfish)\n\nThis example demonstrates how to automatically download and apply optimized configuration files for your Minecraft server from a remote Git repository. This is useful for maintaining a standard, high-performance configuration across multiple server instances.\n\n## Features\n\n- **Automatic Download**: Fetches `bukkit.yml`, `spigot.yml`, and server-specific configs (`paper-global.yml`, `purpur.yml`, `pufferfish.yml`) on startup.\n- **Version Aware**: Downloads configurations from a folder matching your Minecraft version (e.g., `1.21.1`).\n- **Base `server.properties`**: Optionally download a base `server.properties` file while still allowing environment variable overrides.\n\n## Repository Structure\n\nYour configuration repository should be structured by Minecraft version. For example:\n\n```text\nmy-config-repo/\n├── 1.21.1/\n│   ├── bukkit.yml\n│   ├── spigot.yml\n│   ├── purpur.yml\n│   ├── pufferfish.yml\n│   └── server.properties\n├── 1.20.4/\n│   └── ...\n└── README.md\n```\n\n## Docker Compose Examples\n\n### 1. Paper Server\n\nFor Paper, use `PAPER_CONFIG_REPO`.\n\n```yaml\nversion: \"3.8\"\n\nservices:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"TRUE\"\n      TYPE: \"PAPER\"\n      VERSION: \"1.21.1\"\n\n      # URL to the root of your config repository (e.g. raw.githubusercontent.com)\n      # The script automatically appends \"/<VERSION>/<FILE>\" to this URL.\n      PAPER_CONFIG_REPO: \"https://raw.githubusercontent.com/Alpha018/paper-config-optimized/refs/heads/main\"\n```\n\n### 2. Purpur Server\n\nFor Purpur, use `PURPUR_CONFIG_REPO`.\n\n```yaml\nversion: \"3.8\"\n\nservices:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"TRUE\"\n      TYPE: \"PURPUR\"\n      VERSION: \"1.21.1\"\n      \n      # URL to the root of your config repository (e.g. raw.githubusercontent.com)\n      # The script automatically appends \"/<VERSION>/<FILE>\" to this URL.\n      PURPUR_CONFIG_REPO: \"https://raw.githubusercontent.com/Alpha018/paper-config-optimized/refs/heads/main\"\n```\n\n### 2. Pufferfish Server\n\nFor Pufferfish, use `PUFFERFISH_CONFIG_REPO`.\n\n```yaml\nversion: \"3.8\"\n\nservices:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"TRUE\"\n      TYPE: \"PUFFERFISH\"\n      VERSION: \"1.21.1\" # Must match a folder in your repo\n      \n      # Automagically download optimized configs (bukkit.yml, spigot.yml, pufferfish.yml)\n      PUFFERFISH_CONFIG_REPO: \"https://raw.githubusercontent.com/Alpha018/paper-config-optimized/refs/heads/main\"\n```\n\n### 3. Downloading `server.properties` (Optional)\n\nIf you also want to download a base `server.properties` file from your repository, you must explicitly set `SERVER_PROPERTIES_REPO_URL`.\n\n**Smart Feature**:\n\n- If you provide a **base URL** (like the repo root), the script will automatically append `/<VERSION>/server.properties`.\n- If you provide a **full URL** (ending in `.properties`), it will download that specific file.\n\n**Crucial Note**: Environment variables (like `MOTD`, `DIFFICULTY`, `MAX_PLAYERS`) in your Docker Compose **WILL override** values in the downloaded file. This allows you to have a shared optimized base but customize specifics per instance.\n\n```yaml\n    environment:\n      # ... other vars ...\n      \n      # Option A: Base URL (Smart)\n      # Will look for: https://.../main/1.21.1/server.properties\n      SERVER_PROPERTIES_REPO_URL: \"https://raw.githubusercontent.com/Alpha018/paper-config-optimized/refs/heads/main\"\n\n      # Option B: Specific URL (Manual)\n      # SERVER_PROPERTIES_REPO_URL: \"https://gist.githubusercontent.com/.../server.properties\"\n      \n      # These will OVERRIDE settings in the downloaded file\n      MOTD: \"My Custom Instance\"\n      MAX_PLAYERS: 50\n```\n\n## Supported Variables\n\n| Variable | Description |\n| :--- | :--- |\n| `PAPER_CONFIG_REPO` | Base URL for Paper configs. Downloads `paper-global.yml`, `bukkit.yml`, `spigot.yml`, etc. |\n| `PURPUR_CONFIG_REPO` | Base URL for Purpur configs. Downloads `purpur.yml`, `bukkit.yml`, `spigot.yml`. |\n| `PUFFERFISH_CONFIG_REPO` | Base URL for Pufferfish configs. Downloads `pufferfish.yml`, `bukkit.yml`, `spigot.yml`. |\n| `SERVER_PROPERTIES_REPO_URL` | Boolean/URL. Set to download `server.properties`. Can be a base URL or direct file link. |\n\n## Contribution\n\nGot ideas to squeeze even more performance out of these configs? Or maybe you found a better way to structure things?\n\nI'd love to see your improvements! Feel free to open a Pull Request or an Issue in the [repository](https://github.com/Alpha018/paper-config-optimized). Let's make these configs the best they can be together.\n"
  },
  {
    "path": "examples/optimized-server-config/docker-compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    container_name: paper\n    environment:\n      EULA: \"true\"\n      TYPE: \"PAPER\"\n      VIEW_DISTANCE: 10\n      MEMORY: 2G\n      PAPER_CONFIG_REPO: \"https://raw.githubusercontent.com/Alpha018/paper-config-optimized/main\"\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - mc-paper:/data\n    restart: unless-stopped\nvolumes:\n  mc-paper: {}\n"
  },
  {
    "path": "examples/packwiz/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    tty: true\n    stdin_open: true\n    environment:\n      EULA: true\n      # Match loader from versions section of https://github.com/packwiz/packwiz-example-pack/blob/v1/pack.toml\n      TYPE: QUILT\n      VERSION: \"1.19\"\n      QUILT_LOADER_VERSION: \"0.17.0\"\n      PACKWIZ_URL: https://raw.githubusercontent.com/packwiz/packwiz-example-pack/refs/heads/v1/pack.toml\n    volumes:\n      - ./data:/data\n    ports:\n      - \"25565:25565\""
  },
  {
    "path": "examples/paper/compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    tty: true\n    stdin_open: true\n    environment:\n      EULA: \"true\"\n      TYPE: PAPER\n      MEMORY: 2G\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - mc-data:/data\n    restart: unless-stopped\nvolumes:\n  mc-data: {}\n"
  },
  {
    "path": "examples/paper/paper-build-plugins/Dockerfile",
    "content": "FROM itzg/minecraft-server\n\nENV TYPE=PAPER\n\nCOPY plugins/*.jar /plugins/"
  },
  {
    "path": "examples/paper/paper-build-plugins/docker-compose.yml",
    "content": "services:\n  mc:\n    build: .\n    environment:\n      EULA: \"TRUE\"\n    ports:\n    - 25565:25565\n    stdin_open: true\n    tty: true"
  },
  {
    "path": "examples/paper/paper-build-plugins/plugins/.gitignore",
    "content": "*.jar"
  },
  {
    "path": "examples/paper/paper-build-plugins/plugins/README.md",
    "content": "Download Bukkit/Spigot plugin jars, such as [WorldEdit](https://dev.bukkit.org/projects/worldedit/files) and place them here. At image build time the `COPY` step will place those jars in `/plugins`. At container startup, the contents of `/plugins` are sync'ed into `/data/plugins` for use with Bukkit/Spigot/Paper server types."
  },
  {
    "path": "examples/paper/paper-proxy-protocol/config/paper-global.yml",
    "content": "proxies:\r\n  proxy-protocol: true"
  },
  {
    "path": "examples/paper/paper-proxy-protocol/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    environment:\r\n      EULA: true\r\n      TYPE: PAPER\r\n    ports:\r\n      # Use host port 25566 assuming mc-router or similar is bound to 25565\r\n      - \"25566:25565\"\r\n    volumes:\r\n      - mc-data:/data\r\n      - ./config:/config:ro\r\n\r\nvolumes:\r\n  mc-data: {}"
  },
  {
    "path": "examples/proxied/compose.yml",
    "content": "services:\n  mc:\n#    build: .\n    image: itzg/minecraft-server\n    ports:\n      - 25565:25565\n    environment:\n      EULA: \"TRUE\"\n#      TYPE: SPIGOT\n#      VERSION: 1.11.2\n      PROXY: proxy:3128\n  proxy:\n    image: sameersbn/squid\n\n"
  },
  {
    "path": "examples/quilt/docker-compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"true\"\n      TYPE: QUILT\n    ports:\n      - \"25565:25565\"\n    volumes:\n      - data:/data\n\nvolumes:\n  data: {}"
  },
  {
    "path": "examples/rlcraft/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server:java8-multiarch\r\n    environment:\r\n      EULA: true\r\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\r\n      # Allocate API key from https://console.curseforge.com/\r\n      # and set in .env file making sure to double up dollar signs, such as\r\n      # CF_API_KEY=$$2a$$10$$....\r\n      # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key\r\n      CF_API_KEY: ${CF_API_KEY}\r\n      CF_SLUG: rlcraft\r\n      CF_FILENAME_MATCHER: 2.9.3\r\n      CF_FORCE_SYNCHRONIZE: true\r\n      MEMORY: 4G\r\n    volumes:\r\n      - ./data:/data\r\n    ports:\r\n      - \"25565:25565\"\r\n"
  },
  {
    "path": "examples/simple-voice-chat/fabric-compose.yaml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    tty: true\n    stdin_open: true\n    ports:\n      # Game port\n      - \"25565:25565/tcp\"\n      # Voice chat port\n      - \"24454:24454/udp\"\n    environment:\n      EULA: \"TRUE\"\n      TYPE: \"FABRIC\"\n      VERSION: \"1.21.1\"\n      # This will select the latest version of simple voice chat for 1.21.1.\n      # You can specify a version by appending :versionID (e.g. simple-voice-chat:anabvqRL)\n      MODRINTH_PROJECTS: \"simple-voice-chat\"\n      MEMORY: 8G\n    volumes:\n      # attach the relative directory 'data' to the container's /data path\n      - ./data:/data"
  },
  {
    "path": "examples/simple-voice-chat/neoforge-compose.yaml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    tty: true\n    stdin_open: true\n    ports:\n      # Game port\n      - \"25565:25565/tcp\"\n      # Voice chat port\n      - \"24454:24454/udp\"\n    environment:\n      EULA: \"TRUE\"\n      TYPE: \"NEOFORGE\"\n      VERSION: \"1.21.1\"\n      # This will select the latest version of simple voice chat for 1.21.1.\n      # You can specify a version by appending :versionID (e.g. simple-voice-chat:anabvqRL)\n      MODRINTH_PROJECTS: \"simple-voice-chat\"\n      MEMORY: 8G\n    volumes:\n      # attach the relative directory 'data' to the container's /data path\n      - ./data:/data"
  },
  {
    "path": "examples/simple-voice-chat/paper-compose.yaml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    tty: true\n    stdin_open: true\n    ports:\n      # Game port\n      - \"25565:25565/tcp\"\n      # Voice chat port\n      - \"24454:24454/udp\"\n    environment:\n      EULA: \"TRUE\"\n      TYPE: \"PAPER\"\n      VERSION: \"1.21.1\"\n      # This will select the latest version of simple voice chat for 1.21.1.\n      # You can specify a version by appending :versionID (e.g. simple-voice-chat:anabvqRL)\n      MODRINTH_PROJECTS: \"simple-voice-chat\"\n      MEMORY: 8G\n    volumes:\n      # attach the relative directory 'data' to the container's /data path\n      - ./data:/data"
  },
  {
    "path": "examples/skript/docker-compose.yml",
    "content": "services:\n  mc:\n    image: itzg/minecraft-server\n    environment:\n      EULA: true\n      TYPE: PAPER\n      PLUGINS: https://github.com/SkriptLang/Skript/releases/download/2.8.4/Skript.jar\n      SPIGET_RESOURCES: 103749\n    ports:\n      - \"25565:25565\""
  },
  {
    "path": "examples/spiget/docker-compose.yml",
    "content": "# NOTE\n# This file is purposely named spiget with an \"e\" since it provides an example of the\n# feature https://docker-minecraft-server.readthedocs.io/en/latest/mods-and-plugins/spiget/\n# which uses the Spiget API at https://spiget.org/\n\nservices:\n  mc:\n    image: itzg/minecraft-server\n    ports:\n      - \"25565:25565\"\n    environment:\n      EULA: \"TRUE\"\n      TYPE: PAPER\n      SPIGET_RESOURCES: 34315,3836\n      REMOVE_OLD_MODS: true\n    volumes:\n      - ./data:/data\n"
  },
  {
    "path": "examples/spigot/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server\r\n    environment:\r\n      EULA: true\r\n      TYPE: SPIGOT\r\n      VERSION: 1.20.4\r\n    ports:\r\n      - \"25565:25565\"\r\n    volumes:\r\n      - data:/data\r\nvolumes:\r\n  data: {}\r\n"
  },
  {
    "path": "examples/tailscale/compose.yml",
    "content": "services:\r\n  tailscale-client:\r\n    image: tailscale/tailscale:latest\r\n    container_name: tailscale\r\n    hostname: tailscale-minecraft # This name will be the one on the tailscale network\r\n    environment:\r\n      TS_AUTHKEY: \"tskey-auth-PLACE-YOUR-KEY-HERE\"\r\n      TS_STATE_DIR: \"/var/lib/tailscale\"\r\n      TS_USERSPACE: \"FALSE\" # If not using Auto-pause it can be set as true\r\n    volumes:\r\n      - ./ts-minecraft/state:/var/lib/tailscale\r\n      - /dev/net/tun:/dev/net/tun\r\n    cap_add:\r\n      - net_admin\r\n      - sys_module\r\n    restart: unless-stopped\r\n    # ports: # Not needed, tailscale is directly linking to the container.\r\n      # - \"25565:25565\"\r\n  minecraft-server:\r\n    image: itzg/minecraft-server\r\n    network_mode: container:tailscale\r\n    stdin_open: true\r\n    tty: true\r\n    environment:\r\n      EULA: \"TRUE\"\r\n      # ENABLE_AUTOPAUSE: \"TRUE\"\r\n      # AUTOPAUSE_KNOCK_INTERFACE: \"tailscale0\"\r\n      # MAX_TICK_TIME: \"-1\"\r\n    volumes:\r\n      - minecraftserver:/data\r\n    restart: unless-stopped\r\n\r\n    \r\nvolumes:\r\n  minecraftserver:"
  },
  {
    "path": "examples/valhelsia2/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    image: itzg/minecraft-server:java8-multiarch\r\n    volumes:\r\n      - data:/data\r\n      - ./modpacks:/modpacks:ro\r\n    environment:\r\n      EULA: \"true\"\r\n      TYPE: FORGE\r\n      VERSION: \"1.15.2\"\r\n      GENERIC_PACK: /modpacks/Valhelsia_2-2.3.4-SERVER.zip\r\n      MEMORY: \"2G\"\r\n    ports:\r\n      - \"25565:25565\"\r\n    healthcheck:\r\n      test: NONE\r\n\r\nvolumes:\r\n  data: {}"
  },
  {
    "path": "examples/vanilla-tweaks/docker-compose.yml",
    "content": "services:\n  vanillatweaks_file:\n    restart: \"no\"\n    image: itzg/minecraft-server\n    ports:\n      - \"25565:25565/tcp\"\n    environment:\n      EULA: \"TRUE\"\n      VERSION: ${MINECRAFT_VERSION:-LATEST}\n      VANILLATWEAKS_FILE: /config/vanillatweaks-datapacks.json\n      REMOVE_OLD_VANILLATWEAKS: \"TRUE\"\n    volumes:\n      - data:/data\n      - ./vanillatweaks-datapacks.json:/config/vanillatweaks-datapacks.json:ro\n  vanillatweaks_sharecode:\n    # port is set to 25566 to not conflict with vanillatweaks_file example\n    ports:\n      - \"25566:25565/tcp\"\n    restart: \"no\"\n    image: itzg/minecraft-server\n    environment:\n      EULA: \"TRUE\"\n      VERSION: ${MINECRAFT_VERSION:-LATEST}\n      VANILLATWEAKS_SHARECODE: MGr52E\n      REMOVE_OLD_VANILLATWEAKS: \"TRUE\"\n  vanillatweaks_file_datapacks_and_resourcepacks_and_craftingtweaks:\n    # port is set to 25567 to not conflict with vanillatweaks_file example\n    restart: \"no\"\n    image: itzg/minecraft-server\n    ports:\n      - \"25567:25565/tcp\"\n    environment:\n      EULA: \"TRUE\"\n      VERSION: ${MINECRAFT_VERSION:-LATEST}\n      VANILLATWEAKS_FILE: /config/vanillatweaks-datapacks.json,/config/vanillatweaks-resourcepacks.json,/config/vanillatweaks-craftingtweaks.json\n      REMOVE_OLD_VANILLATWEAKS: \"TRUE\"\n    volumes:\n      - data:/data\n      - ./vanillatweaks-datapacks.json:/config/vanillatweaks-datapacks.json:ro\n      - ./vanillatweaks-resourcepacks.json:/config/vanillatweaks-resourcepacks.json:ro\n      - ./vanillatweaks-craftingtweaks.json:/config/vanillatweaks-craftingtweaks.json:ro\n"
  },
  {
    "path": "examples/vanilla-tweaks/vanillatweaks-craftingtweaks.json",
    "content": "{\n    \"type\": \"craftingtweaks\",\n    \"version\": \"1.18\",\n    \"packs\": {\n        \"quality of life\": [\n            \"dropper to dispenser\",\n            \"double slabs\",\n            \"back to blocks\"\n        ]\n    },\n    \"result\": \"ok\"\n}"
  },
  {
    "path": "examples/vanilla-tweaks/vanillatweaks-datapacks.json",
    "content": "{\n    \"type\": \"datapacks\",\n    \"version\": \"1.18\",\n    \"packs\": {\n        \"survival\": [\n            \"graves\",\n            \"multiplayer sleep\",\n            \"afk display\",\n            \"armor statues\",\n            \"unlock all recipes\",\n            \"fast leaf decay\",\n            \"coordinates hud\"\n        ],\n        \"items\": [\"armored elytra\"]\n    },\n    \"result\": \"ok\"\n}\n"
  },
  {
    "path": "examples/vanilla-tweaks/vanillatweaks-resourcepacks.json",
    "content": "{\n    \"type\": \"resourcepacks\",\n    \"version\": \"1.18\",\n    \"packs\": {\n        \"aesthetic\": [\"CherryPicking\", \"BlackNetherBricks\", \"AlternateBlockDestruction\"]\n    },\n    \"result\": \"ok\"\n}"
  },
  {
    "path": "files/cf-exclude-include.json",
    "content": "{\n  \"globalExcludes\": [\n    \"advancement-plaques\",\n    \"ambience-music-mod\",\n    \"ambientsounds\",\n    \"appleskin\",\n    \"armor-chroma\",\n    \"armor-toughness-bar\",\n    \"audio-extension-for-fancymenu-forge\",\n    \"auudio-forge\",\n    \"beehivetooltips\",\n    \"better-advancements\",\n    \"better-foliage\",\n    \"better-modlist-neoforge\",\n    \"better-placement\",\n    \"better-sprinting\",\n    \"better-third-person\",\n    \"better-tips-nbt-tag\",\n    \"betterf3\",\n    \"betterfps\",\n    \"bettergrassify\",\n    \"biomeinfo\",\n    \"block-drops-jei-addon\",\n    \"blur-forge\",\n    \"cartography\",\n    \"chattoggle\",\n    \"cherished-worlds\",\n    \"chunk-animator\",\n    \"clickable-advancements\",\n    \"colorwheel\",\n    \"colorwheel-patcher\",\n    \"compass-coords\",\n    \"config-menus-forge\",\n    \"configured\",\n    \"controllable\",\n    \"controlling\",\n    \"craftpresence\",\n    \"crash-assistant\",\n    \"cull-less-leaves\",\n    \"ctm\",\n    \"custom-main-menu\",\n    \"dark-mode-everywhere\",\n    \"defensive-measures\",\n    \"ding\",\n    \"distraction-free-recipes\",\n    \"drippy-loading-screen\",\n    \"dynamic-surroundings\",\n    \"dynamic-view\",\n    \"dynamiclights-reforged\",\n    \"easiervillagertrading\",\n    \"effective-forge\",\n    \"embeddium\",\n    \"embeddium-extension\",\n    \"embeddium-extras\",\n    \"enchantment-descriptions\",\n    \"enhanced-boss-bars\",\n    \"enhancedvisuals\",\n    \"entity-collision-fps-fix\",\n    \"entity-model-features\",\n    \"entity-texture-features-fabric\",\n    \"entityculling\",\n    \"equipment-compare\",\n    \"essential-mod\",\n    \"euphoria-patches\",\n    \"extreme-sound-muffler\",\n    \"ezzoom\",\n    \"fading-night-vision\",\n    \"falling-leaves-forge\",\n    \"fancymenu\",\n    \"farsight\",\n    \"faster-ladder-climbing\",\n    \"fastquit\",\n    \"fastquit-forge\",\n    \"flerovium\",\n    \"foamfix-optimization-mod\",\n    \"forgeskyboxes\",\n    \"fps-reducer\",\n    \"free-cam\",\n    \"ftb-backups-2\",\n    \"fullscreen-windowed-borderless-for-minecraft\",\n    \"hwyla\",\n    \"iceberg\",\n    \"ignitioncoil\",\n    \"illager-raid-music\",\n    \"inmisaddon\",\n    \"iris-flywheel-compat\",\n    \"irisshaders\",\n    \"item-borders\",\n    \"item-highlighter\",\n    \"item-obliterator\",\n    \"itemphysic-lite\",\n    \"itemzoom\",\n    \"just-enough-harvestcraft\",\n    \"just-enough-mekanism-multiblocks\",\n    \"just-enough-resources-jer\",\n    \"just-zoom\",\n    \"legendary-tooltips\",\n    \"lighty\",\n    \"loot-capacitor-tooltips\",\n    \"loot-journal\",\n    \"lootbeams\",\n    \"magnesium-extras\",\n    \"make-bubbles-pop\",\n    \"mekalus-oculus-fork-with-fixed-mekanism-mekasuit\",\n    \"menumobs\",\n    \"minecraft-rich-presence\",\n    \"mining-speed-tooltips\",\n    \"model-gap-fix\",\n    \"more-overlays\",\n    \"mouse-tweaks\",\n    \"neat\",\n    \"nekos-enchanted-books\",\n    \"neoculus\",\n    \"no-nv-flash\",\n    \"no-recipe-book\",\n    \"not-enough-animations\",\n    \"oculus\",\n    \"ok-zoomer\",\n    \"oldjavawarning\",\n    \"overloaded-armor-bar\",\n    \"packmenu\",\n    \"packmodemenu\",\n    \"particle-effects\",\n    \"particle-effects-reforged\",\n    \"particle-rain\",\n    \"particular\",\n    \"particular-reforged\",\n    \"reauth\",\n    \"rebind-narrator\",\n    \"reblured\",\n    \"reeses-sodium-options\",\n    \"reforgium\",\n    \"resource-reloader\",\n    \"rubidium\",\n    \"rubidium-extra\",\n    \"ryoamiclights\",\n    \"schematica\",\n    \"seamless-loading-screen\",\n    \"seamless-loading-screen-forge\",\n    \"searchables\",\n    \"seasonhud\",\n    \"shulkerboxviewer\",\n    \"skin-layers-3d\",\n    \"smart-hud\",\n    \"smithing-template-viewer\",\n    \"smooth-font\",\n    \"smoothwater\",\n    \"sodium\",\n    \"sodium-extra\",\n    \"sodium-options-api\",\n    \"sodium-rubidium-occlusion-culling-fix\",\n    \"sound\",\n    \"sound-filters\",\n    \"sound-physics-remastered\",\n    \"sound-reloader\",\n    \"status-effect-bars-reforged\",\n    \"stellar-sky\",\n    \"swingthroughgrass\",\n    \"textrues-embeddium-options\",\n    \"textrues-rubidium-options\",\n    \"thaumic-jei\",\n    \"tips\",\n    \"toast-control\",\n    \"torohealth-damage-indicators\",\n    \"true-darkness\",\n    \"ungrab-mouse-mod\",\n    \"vanillafix\",\n    \"visuality\",\n    \"waila-harvestability\",\n    \"waila-stages\",\n    \"wakes-reforged\",\n    \"wawla\",\n    \"welcome-screen\",\n    \"xaeroplus\",\n    \"yungs-menu-tweaks\",\n    \"zume\"\n  ],\n  \"modpacks\": {\n    \"all-of-fabric-6\": {\n      \"forceIncludes\": [\"revelationary\"]\n    },\n    \"beyond-depth\": {\n      \"forceIncludes\": [\"particular-reforged\"]\n    },\n    \"create-arcane-engineering\": {\n      \"forceIncludes\": [\"just-enough-resources-jer\"]\n    },\n    \"ftb-stoneblock-4\": {\n      \"forceIncludes\": [\n        \"particular-reforged\",\n        \"ctm\"\n      ]\n    },\n    \"mc-eternal-2\": {\n      \"forceIncludes\": [\n        \"particular-reforged\",\n        \"mob-player-animator\"\n      ]\n    },\n    \"reclamation-reclaim-the-world\": {\n      \"forceIncludes\": [\n        \"more-overlays-updated\"\n      ]\n    },\n    \"skyfactory-5\": {\n      \"forceIncludes\": [\n        \"colored-torches\",\n        \"dye-mob-dye\",\n        \"openstairs\"\n      ]\n    },\n    \"valhelsia-5\": {\n      \"excludes\": [\"modernfix\"]\n    }\n  }\n}\n"
  },
  {
    "path": "files/knockd-config.cfg",
    "content": "[options]\n logfile = /dev/null\n[unpauseMCServer-server]\n sequence = 25565\n seq_timeout = 1\n command = /image/scripts/auto/resume.sh %IP%\n tcpflags = syn\n[unpauseMCServer-rcon]\n sequence = 25575\n seq_timeout = 1\n command = /image/scripts/auto/resume.sh %IP%\n tcpflags = syn\n[unpauseMCServer-bedrock]\n sequence = 19132:udp\n command = /image/scripts/auto/resume.sh %IP%\n seq_cooldown = 60\n"
  },
  {
    "path": "files/modrinth-exclude-include.json",
    "content": "{\n  \"globalExcludes\": [\n    \"3dskinlayers\",\n    \"ae2-emi-crafting\",\n    \"AmbientSounds\",\n    \"amecs\",\n    \"Animation_Overhaul\",\n    \"appleskin\",\n    \"auudio\",\n    \"axolotlbuckets\",\n    \"BadOptimizations\",\n    \"BetterAdvancements\",\n    \"betterbeds\",\n    \"BetterF3\",\n    \"BetterGrassify\",\n    \"BetterThirdPerson\",\n    \"BHMenu\",\n    \"blur\",\n    \"Boat-Item-View\",\n    \"bobby\",\n    \"cat_jam\",\n    \"chat_heads\",\n    \"chatanimation\",\n    \"cherishedworlds\",\n    \"citresewn\",\n    \"clickadv\",\n    \"cobblemon-ui-tweaks\",\n    \"compass-coords\",\n    \"colorwheel\",\n    \"connectedness\",\n    \"connector\",\n    \"continuity\",\n    \"controlling\",\n    \"craftpresence\",\n    \"CrashAssistant\",\n    \"Cull Less Leaves\",\n    \"cwb\",\n    \"DisableCustomWorldsAdvice\",\n    \"distraction_free_recipes\",\n    \"drippyloadingscreen\",\n    \"eating-animation\",\n    \"emi\",\n    \"emi_loot\",\n    \"emi_trade\",\n    \"emiffect\",\n    \"emitrades\",\n    \"entity_model_features\",\n    \"entity_texture_features\",\n    \"entityculling\",\n    \"euphoriapatcher\",\n    \"fallingleaves\",\n    \"fancymenu\",\n    \"fast-ip-ping\",\n    \"fastquit\",\n    \"FauxCustomEntityData\",\n    \"feytweaks\",\n    \"figura\",\n    \"ForgeConfigScreens\",\n    \"GeckoLibIrisCompat\",\n    \"gpumemleakfix\",\n    \"Highlighter\",\n    \"ImmediatelyFast\",\n    \"indium\",\n    \"inventory-profiles-next\",\n    \"iris\",\n    \"iris-flywheel\",\n    \"ItemBorders\",\n    \"ItemLocks\",\n    \"justzoom\",\n    \"language-reload\",\n    \"lazy-language-loader\",\n    \"LegendaryTooltips\",\n    \"loadmyresources\",\n    \"lootbeams\",\n    \"MindfulDarkness\",\n    \"miningspeedtooltips\",\n    \"MouseTweaks\",\n    \"nicer-skies\",\n    \"notenoughanimations\",\n    \"oculus\",\n    \"ok_zoomer\",\n    \"OverflowingBars\",\n    \"particlerain\",\n    \"PickUpNotifier\",\n    \"PresenceFootsteps\",\n    \"Prism\",\n    \"reeses_sodium_options\",\n    \"reforgium\",\n    \"ResourcePackOverrides\",\n    \"roughly-enough-items\",\n    \"ryoamiclights\",\n    \"screenshot_viewer\",\n    \"Searchables\",\n    \"seasonhud\",\n    \"ShoulderSurfing\",\n    \"skinlayers3d\",\n    \"sodium\",\n    \"sorted_enchantments\",\n    \"tooltipfix\",\n    \"visuality\",\n    \"VR-Combat\",\n    \"WailaStages\",\n    \"welcomescreen\",\n    \"Xaeros_Minimap\",\n    \"XaerosWorldMap\",\n    \"YeetusExperimentus\",\n    \"yungsmenutweaks\",\n    \"Zoomify\",\n    \"zume\"\n  ],\n  \"globalForceIncludes\": [],\n  \"modpacks\": {\n    \"cobbleverse\": {\n      \"excludes\": [\n        \"cloth-config\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "files/nanolimbo-settings-patch.json",
    "content": "{\n  \"file\": \"/data/settings.yml\",\n  \"ops\": [\n    {\n      \"$set\": {\n        \"path\": \"$.bind.port\",\n        \"value\": \"${SERVER_PORT}\",\n        \"value-type\": \"int\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "files/property-definitions.json",
    "content": "{\r\n  \"accepts-transfers\": {\"env\": \"ACCEPTS_TRANSFERS\"},\r\n  \"allow-flight\": {\"env\": \"ALLOW_FLIGHT\"},\r\n  \"allow-nether\": {\"env\": \"ALLOW_NETHER\"},\r\n  \"announce-player-achievements\": {\"env\": \"ANNOUNCE_PLAYER_ACHIEVEMENTS\"},\r\n  \"broadcast-console-to-ops\": {\"env\": \"BROADCAST_CONSOLE_TO_OPS\"},\r\n  \"broadcast-rcon-to-ops\": {\"env\": \"BROADCAST_RCON_TO_OPS\"},\r\n  \"bug-report-link\": {\"env\": \"BUG_REPORT_LINK\"},\r\n  \"difficulty\": {\"env\": \"DIFFICULTY\"},\r\n  \"enable-command-block\": {\"env\": \"ENABLE_COMMAND_BLOCK\"},\r\n  \"enable-jmx-monitoring\": {\"env\": \"ENABLE_JMX\"},\r\n  \"enable-query\": {\"env\": \"ENABLE_QUERY\"},\r\n  \"enable-rcon\": {\"env\": \"ENABLE_RCON\"},\r\n  \"enable-status\": {\"env\": \"ENABLE_STATUS\"},\r\n  \"enforce-secure-profile\": {\"env\": \"ENFORCE_SECURE_PROFILE\"},\r\n  \"enforce-whitelist\": {\"env\": \"ENFORCE_WHITELIST\"},\r\n  \"entity-broadcast-range-percentage\": {\"env\": \"ENTITY_BROADCAST_RANGE_PERCENTAGE\"},\r\n  \"force-gamemode\": {\"env\": \"FORCE_GAMEMODE\"},\r\n  \"function-permission-level\": {\"env\": \"FUNCTION_PERMISSION_LEVEL\"},\r\n  \"gamemode\": {\"env\": \"MODE\"},\r\n  \"generate-structures\": {\"env\": \"GENERATE_STRUCTURES\"},\r\n  \"generator-settings\": {\"env\": \"GENERATOR_SETTINGS\"},\r\n  \"hardcore\": {\"env\": \"HARDCORE\"},\r\n  \"hide-online-players\": {\"env\": \"HIDE_ONLINE_PLAYERS\"},\r\n  \"initial-disabled-packs\": {\"env\": \"INITIAL_DISABLED_PACKS\"},\r\n  \"initial-enabled-packs\": {\"env\": \"INITIAL_ENABLED_PACKS\"},\r\n  \"level-name\": {\"env\": \"LEVEL\"},\r\n  \"level-seed\": {\"env\": \"SEED\"},\r\n  \"level-type\": {\"env\": \"LEVEL_TYPE\"},\r\n  \"log-ips\": {\"env\": \"LOG_IPS\"},\r\n  \"management-server-allowed-origins\": {\"env\": \"MANAGEMENT_SERVER_ALLOWED_ORIGINS\"},\r\n  \"management-server-enabled\": {\"env\": \"MANAGEMENT_SERVER_ENABLED\"},\r\n  \"management-server-host\": {\"env\": \"MANAGEMENT_SERVER_HOST\"},\r\n  \"management-server-port\": {\"env\": \"MANAGEMENT_SERVER_PORT\"},\r\n  \"management-server-secret\": {\"env\": \"MANAGEMENT_SERVER_SECRET\"},\r\n  \"management-server-tls-enabled\": {\"env\": \"MANAGEMENT_SERVER_TLS_ENABLED\"},\r\n  \"management-server-tls-keystore\": {\"env\": \"MANAGEMENT_SERVER_TLS_KEYSTORE\"},\r\n  \"management-server-tls-keystore-password\": {\"env\": \"MANAGEMENT_SERVER_TLS_KEYSTORE_PASSWORD\"},\r\n  \"max-build-height\": {\"env\": \"MAX_BUILD_HEIGHT\"},\r\n  \"max-chained-neighbor-updates\": {\"env\": \"MAX_CHAINED_NEIGHBOR_UPDATES\"},\r\n  \"max-players\": {\"env\": \"MAX_PLAYERS\"},\r\n  \"max-tick-time\": {\"env\": \"MAX_TICK_TIME\"},\r\n  \"max-world-size\": {\"env\": \"MAX_WORLD_SIZE\"},\r\n  \"motd\": {\"env\": \"MOTD\"},\r\n  \"network-compression-threshold\": {\"env\": \"NETWORK_COMPRESSION_THRESHOLD\"},\r\n  \"online-mode\": {\"env\": \"ONLINE_MODE\"},\r\n  \"op-permission-level\": {\"env\": \"OP_PERMISSION_LEVEL\"},\r\n  \"pause-when-empty-seconds\": {\"env\": \"PAUSE_WHEN_EMPTY_SECONDS\"},\r\n  \"player-idle-timeout\": {\"env\": \"PLAYER_IDLE_TIMEOUT\"},\r\n  \"prevent-proxy-connections\": {\"env\": \"PREVENT_PROXY_CONNECTIONS\"},\r\n  \"previews-chat\": {\"env\": \"PREVIEWS_CHAT\"},\r\n  \"pvp\": {\"env\": \"PVP\"},\r\n  \"query.port\": {\"env\": \"QUERY_PORT\"},\r\n  \"rate-limit\": {\"env\": \"RATE_LIMIT\"},\r\n  \"rcon.password\": {\"env\": \"RCON_PASSWORD\"},\r\n  \"rcon.port\": {\"env\": \"RCON_PORT\"},\r\n  \"region-file-compression\": {\"env\": \"REGION_FILE_COMPRESSION\"},\r\n  \"require-resource-pack\": {\"env\": \"RESOURCE_PACK_ENFORCE\"},\r\n  \"resource-pack-id\": {\"env\": \"RESOURCE_PACK_ID\"},\r\n  \"resource-pack-prompt\": {\"env\": \"RESOURCE_PACK_PROMPT\"},\r\n  \"resource-pack-sha1\": {\"env\": \"RESOURCE_PACK_SHA1\"},\r\n  \"resource-pack\": {\"env\": \"RESOURCE_PACK\"},\r\n  \"server-ip\": {\"env\": \"SERVER_IP\"},\r\n  \"server-name\": {\"env\": \"SERVER_NAME\"},\r\n  \"server-port\": {\"env\": \"SERVER_PORT\"},\r\n  \"simulation-distance\": {\"env\": \"SIMULATION_DISTANCE\"},\r\n  \"snooper-enabled\": {\"env\": \"SNOOPER_ENABLED\"},\r\n  \"spawn-animals\": {\"env\": \"SPAWN_ANIMALS\"},\r\n  \"spawn-monsters\": {\"env\": \"SPAWN_MONSTERS\"},\r\n  \"spawn-npcs\": {\"env\": \"SPAWN_NPCS\"},\r\n  \"spawn-protection\": {\"env\": \"SPAWN_PROTECTION\"},\r\n  \"status-heartbeat-interval\": {\"env\": \"STATUS_HEARTBEAT_INTERVAL\"},\r\n  \"sync-chunk-writes\": {\"env\": \"SYNC_CHUNK_WRITES\"},\r\n  \"use-native-transport\": {\"env\": \"USE_NATIVE_TRANSPORT\"},\r\n  \"view-distance\": {\"env\": \"VIEW_DISTANCE\"},\r\n  \"white-list\": {\"env\": \"WHITELIST_PROP\"}\r\n}"
  },
  {
    "path": "files/server.properties",
    "content": "op-permission-level=4\nallow-nether=true\nlevel-name=world\nenable-query=false\nallow-flight=false\nannounce-player-achievements=true\nserver-port=25565\nrcon.port=25575\nquery.port=25565\nlevel-type=DEFAULT\nenable-rcon=false\nforce-gamemode=false\nlevel-seed=\nserver-ip=\nmax-tick-time=60000\nmax-build-height=256\nspawn-npcs=true\nwhite-list=false\nspawn-animals=true\nhardcore=false\nsnooper-enabled=true\ntexture-pack=\nonline-mode=true\nresource-pack=\nresource-pack-sha1=\npvp=true\ndifficulty=1\nenable-command-block=true\nplayer-idle-timeout=0\ngamemode=0\nmax-players=20\nspawn-monsters=true\ngenerate-structures=true\nview-distance=10\nspawn-protection=16\nmotd=A Minecraft Server powered by Docker\ngenerator-settings=\nrcon.password=\nmax-world-size=29999984\nbroadcast-console-to-ops=true\nbroadcast-rcon-to-ops=true\nenable-jmx-monitoring=false\nsync-chunk-writes=true\nenable-status=true\nentity-broadcast-range-percentage=100\nfunction-permission-level=2\nnetwork-compression-threshold=256\nop-permission-level=4\nprevent-proxy-connections=false\nuse-native-transport=true\nenforce-whitelist=false\n"
  },
  {
    "path": "images.json",
    "content": "[\r\n  {\r\n    \"tag\": \"latest\",\r\n    \"notes\": \"Always tracks the most recent commit on the main branch\",\r\n    \"java\": \"25\",\r\n    \"distribution\": \"ubuntu\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\"],\r\n    \"lts\": true\r\n  },\r\n  {\r\n    \"tag\": \"stable\",\r\n    \"notes\": \"Always tracks the most recent release\",\r\n    \"java\": \"25\",\r\n    \"distribution\": \"ubuntu\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\"],\r\n    \"lts\": true\r\n  },\r\n  {\r\n    \"tag\": \"java25\",\r\n    \"java\": \"25\",\r\n    \"distribution\": \"ubuntu\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\"]\r\n  },\r\n  {\r\n    \"tag\": \"java25-alpine\",\r\n    \"java\": \"25\",\r\n    \"distribution\": \"alpine\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\"]\r\n  },\r\n  {\r\n    \"tag\": \"java25-graalvm\",\r\n    \"java\": \"25\",\r\n    \"distribution\": \"oracle\",\r\n    \"jvm\": \"graalvm\",\r\n    \"architectures\": [\"amd64\", \"arm64\"],\r\n    \"deprecated\": true\r\n  },\r\n  {\r\n    \"tag\": \"java24\",\r\n    \"java\": \"24\",\r\n    \"distribution\": \"ubuntu\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\"],\r\n    \"deprecated\": true\r\n  },\r\n  {\r\n    \"tag\": \"java24-graalvm\",\r\n    \"java\": \"24\",\r\n    \"distribution\": \"oracle\",\r\n    \"jvm\": \"graalvm\",\r\n    \"architectures\": [\"amd64\", \"arm64\"],\r\n    \"deprecated\": true\r\n  },\r\n  {\r\n    \"tag\": \"java21\",\r\n    \"java\": \"21\",\r\n    \"distribution\": \"ubuntu\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\"],\r\n    \"lts\": true\r\n  },\r\n  {\r\n    \"tag\": \"java21-alpine\",\r\n    \"java\": \"21\",\r\n    \"distribution\": \"alpine\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\"]\r\n  },\r\n  {\r\n    \"tag\": \"java21-graalvm\",\r\n    \"java\": \"21\",\r\n    \"distribution\": \"oracle\",\r\n    \"jvm\": \"graalvm\",\r\n    \"architectures\": [\"amd64\", \"arm64\"],\r\n    \"deprecated\": true\r\n  },\r\n  {\r\n    \"tag\": \"java21-jdk\",\r\n    \"java\": \"21\",\r\n    \"distribution\": \"ubuntu\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\"],\r\n    \"jdk\": true\r\n  },\r\n  {\r\n    \"tag\": \"java17\",\r\n    \"java\": \"17\",\r\n    \"distribution\": \"ubuntu\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\"],\r\n    \"lts\": true\r\n  },\r\n  {\r\n    \"tag\": \"java17-alpine\",\r\n    \"java\": \"17\",\r\n    \"distribution\": \"alpine\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\"],\r\n    \"deprecated\": true\r\n  },\r\n  {\r\n    \"tag\": \"java17-graalvm\",\r\n    \"java\": \"17\",\r\n    \"distribution\": \"oracle\",\r\n    \"jvm\": \"graalvm\",\r\n    \"architectures\": [\"amd64\", \"arm64\"]\r\n  },\r\n  {\r\n    \"tag\": \"java16\",\r\n    \"java\": \"16\",\r\n    \"distribution\": \"ubuntu\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\", \"armv7\"]\r\n  },\r\n  {\r\n    \"tag\": \"java11\",\r\n    \"java\": \"11\",\r\n    \"distribution\": \"ubuntu\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\", \"armv7\"],\r\n    \"lts\": true\r\n  },\r\n  {\r\n    \"tag\": \"java8\",\r\n    \"java\": \"8\",\r\n    \"distribution\": \"ubuntu\",\r\n    \"jvm\": \"hotspot\",\r\n    \"architectures\": [\"amd64\", \"arm64\", \"armv7\"],\r\n    \"lts\": true\r\n  },\r\n  {\"tag\": \"adopt13\", \"deprecated\": true},\r\n  {\"tag\": \"adopt14\", \"deprecated\": true},\r\n  {\"tag\": \"adopt15\", \"deprecated\": true},\r\n  {\"tag\": \"openj9-nightly\", \"deprecated\": true},\r\n  {\"tag\": \"multiarch-latest\", \"deprecated\": true},\r\n  {\"tag\": \"java16-openj9\", \"deprecated\": true},\r\n  {\"tag\": \"java17-graalvm-ce\", \"deprecated\": true},\r\n  {\"tag\": \"java17-openj9\", \"deprecated\": true},\r\n  {\"tag\": \"java19\", \"deprecated\": true},\r\n  {\"tag\": \"java20-graalvm\", \"deprecated\": true},\r\n  {\"tag\": \"java20\", \"deprecated\": true},\r\n  {\"tag\": \"java20-alpine\", \"deprecated\": true},\r\n  {\"tag\": \"java23\", \"deprecated\": true},\r\n  {\"tag\": \"java8-multiarch\", \"deprecated\": true},\r\n  {\"tag\": \"java8-alpine\", \"deprecated\": true},\r\n  {\"tag\": \"java8-jdk\", \"deprecated\": true},\r\n  {\"tag\": \"java8-openj9\", \"deprecated\": true},\r\n  {\"tag\": \"java8-graalvm-ce\", \"deprecated\": true}\r\n\r\n]\r\n"
  },
  {
    "path": "kustomize/README.md",
    "content": "This directory provides a base to use with [kubectl kustomize](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/).\r\n\r\n## Example overlay content\r\n\r\n### kustomization.yml\r\n```yaml\r\nnameSuffix: \"-forge\"\r\ncommonLabels:\r\n  server: forge\r\nbases:\r\n  - https://github.com/itzg/docker-minecraft-server.git/kustomize/base\r\nconfigMapGenerator:\r\n  - name: mc\r\n    envs:\r\n      - mc.env\r\npatches:\r\n  # Example of using a patch to set external service name for mc-router to pick up\r\n  - path: set-external-servername.yml\r\n```\r\n\r\n### mc.env\r\n```\r\nEULA=true\r\nTYPE=FORGE\r\n```\r\n\r\n###\r\n\r\n```yaml\r\napiVersion: v1\r\nkind: Service\r\nmetadata:\r\n  name: mc\r\n  annotations:\r\n    mc-router.itzg.me/externalServerName: forge.example.com\r\n```"
  },
  {
    "path": "kustomize/base/kustomization.yml",
    "content": "apiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\nresources:\n- statefulset.yml\n- service.yml"
  },
  {
    "path": "kustomize/base/service.yml",
    "content": "apiVersion: v1\r\nkind: Service\r\nmetadata:\r\n  labels:\r\n    service: mc\r\n  annotations: {}\r\n  # Such as\r\n  #   mc-router.itzg.me/externalServerName: $(EXTERNAL_SERVICE_NAME)\r\n  name: mc\r\nspec:\r\n  ports:\r\n    - name: minecraft\r\n      port: 25565\r\n      targetPort: 25565\r\n  type: NodePort\r\n  selector:\r\n    server: mc"
  },
  {
    "path": "kustomize/base/statefulset.yml",
    "content": "apiVersion: apps/v1\r\nkind: StatefulSet\r\nmetadata:\r\n  name: mc\r\nspec:\r\n  replicas: 1\r\n  serviceName: mc\r\n  selector:\r\n    matchLabels:\r\n      server: mc\r\n  template:\r\n    metadata:\r\n      labels:\r\n        server: mc\r\n    spec:\r\n      containers:\r\n        - name: mc\r\n          envFrom:\r\n            - configMapRef:\r\n                name: mc\r\n                optional: true\r\n          env: []\r\n          image: itzg/minecraft-server\r\n          stdin: true\r\n          tty: true\r\n          volumeMounts:\r\n            - mountPath: /data\r\n              name: data\r\n          resources:\r\n            requests:\r\n              cpu: 150m\r\n          livenessProbe:\r\n            exec:\r\n              command: [\"mc-health\"]\r\n            initialDelaySeconds: 120\r\n            periodSeconds: 60\r\n          readinessProbe:\r\n            exec:\r\n              command: [\"mc-health\"]\r\n            initialDelaySeconds: 20\r\n            periodSeconds: 10\r\n            failureThreshold: 12\r\n  volumeClaimTemplates:\r\n    - metadata:\r\n        name: data\r\n      spec:\r\n        accessModes:\r\n          - ReadWriteOnce\r\n        resources:\r\n          requests:\r\n            storage: 500Mi"
  },
  {
    "path": "notes/.gitignore",
    "content": "/*.bkp"
  },
  {
    "path": "notes/Minecraft server containment.xml",
    "content": "<mxfile userAgent=\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36\" version=\"6.4.4\" editor=\"www.draw.io\" type=\"github\"><diagram name=\"Page-1\">1VVNc5swEP01HDMDUiDOMbGd5pK2Ux96VkGAxgJRIRvcX9+VWD40ODOZ1j0Uz9jS2w+t3ts1Ad1W/SfNmvJNZVwGJMz6gO4CQjZRCN8WuAxA8vgwAIUW2QBFM3AQvziCGFecRMZbz9EoJY1ofDBVdc1T42FMa9X5brmS/qkNK/gKOKRMrtHvIjMlolEYzoZXLooSj97EaPjB0mOh1anG8wJCc/cM5oqNudC/LVmmugVE9wHdaqXMsKr6LZeW2pG2Ie7lHetUt+a1+UgAGQLOTJ74WHEiIfQ5E2dbn7kgJ8nPky3q2fDe3DEpijqgT+AheW5mK6wK/HVZ2obVI7ZT6ZFrCHlVrRkdoLSlzwJ2BYwo8WohXSkMPzQstfsOWhCcSlNJ2EX21JFVu8mFlFsllXahdJ/YD+Bnro0AyZ+Gu+yMslnwZjt3LVd/KurCpgptLlWbA5aBXNs0vH+X/mgSFWaFq4obfQEXDKDYBhd/2809RzeIlYt2mxwZ9nkxZZ61hgXKfV16upK+EjBMmuXmruX67KSCATMMYL2SwDU5z5DkvxAE52NNvdHqyBeOoXs8UWIbplP8A3ES3UCVja/K41oVEl5RJYpuoMr9SpU34B+Qr0rD0ITD0JE4TuI/nopwLcKLexYi6OFet6CT3Pt8TjwtCI3IFUJv0eXxis9v2y+fr/D58P/yGf87PmE7v4ycbfHCp/vf</diagram></mxfile>"
  },
  {
    "path": "notes/rbac-processing.mmd",
    "content": "[Scia Reto](https://sciareto.org) mind map   \r\n> __version__=`1.1`,showJumps=`true`\r\n---\r\n\r\n# RBAC processing\r\n\r\n## inputs\r\n\r\n### whitelist\r\n\r\n#### command or space limited\r\n\r\n##### uuid\r\n\r\n##### username\r\n\r\n### ops\r\n\r\n#### username\r\n\r\n#### uuid\r\n\r\n### whitelist file\r\n\r\n#### url?\r\n\r\n##### yes\r\n\r\n###### download\r\n\r\n##### no\r\n\r\n###### copy\r\n\r\n### override whitelist?\r\n\r\n#### yes\r\n\r\n##### replace all with given input list\r\n\r\n#### no\r\n\r\n##### append only\r\n\r\n## format\r\n\r\n### version \\< 1\\.7\\.6?\r\n\r\n#### yes\r\n\r\n##### text file listing usernames\r\n\r\n###### white\\-list\\.txt\r\n\r\n###### ops\\.txt\r\n\r\n#### no\r\n> leftSide=`true`\r\n\r\n\r\n##### json file\r\n\r\n###### array of objects\r\n\r\n####### name\r\n\r\n######## can be any string, even an empty one\r\n\r\n####### uuid\r\n> leftSide=`true`\r\n\r\n\r\n######## username to UUID API\r\n- LINK\r\n<pre>https://wiki.vg/Mojang_API#Username_to_UUID</pre>\r\n\r\n######## needs to be \"dashed\" UUID syntax\r\n> leftSide=`true`\r\n\r\n\r\n####### ops?\r\n\r\n######## yes\r\n\r\n######### also includes\r\n\r\n########## level\r\n\r\n########### integer, usually a 4\r\n\r\n########## bypassesPlayerLimit\r\n\r\n########### boolean\r\n"
  },
  {
    "path": "renovate.json5",
    "content": "{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"enabledManagers\": [\n    \"custom.regex\"\n  ],\n  \"customManagers\": [\n    {\n      \"customType\": \"regex\",\n      \"managerFilePatterns\": [\"/^Dockerfile$/\"],\n      \"matchStrings\": [\n        \"# renovate: datasource=(?<datasource>[a-z-]+?)(?: packageName=(?<packageName>.+?))?(?: versioning=(?<versioning>[a-z-]+))?\\\\s(?:ENV|ARG) .+?_VERSION=(?<currentValue>.+?)\\\\s\"\n      ]\n    }\n  ],\n  \"labels\": [\"dependencies\"]\n}\n"
  },
  {
    "path": "scripts/auto/autopause-daemon.sh",
    "content": "#!/bin/bash\n\n. \"$(dirname \"$0\")/autopause-fcns.sh\"\n\n# shellcheck source=../start-utils\n. \"$(dirname \"$0\")/../start-utils\"\nif isTrue \"${DEBUG_AUTOPAUSE}\"; then\n  set -x\nfi\n\nautopause_error_loop() {\n  logAutopause \"Available interfaces within the docker container:\"\n  logAutopause \"  $(mc-image-helper network-interfaces)\"\n  logAutopause \"Please set the environment variable AUTOPAUSE_KNOCK_INTERFACE to the interface that handles incoming connections.\"\n  logAutopause \"If unsure which interface to choose, run the ifconfig command in the container.\"\n  logAutopause \"Autopause failed to initialize. This log entry will be printed every 30 minutes.\"\n  while :\n  do\n    sleep 1800\n    logAutopause \"Autopause failed to initialize.\"\n  done\n}\n\n# wait for java process to be started\nwhile :\ndo\n  if java_process_exists ; then\n    break\n  fi\n  sleep 0.1\ndone\n\n# check for interface existence\nif [[ -z \"$AUTOPAUSE_KNOCK_INTERFACE\" ]] ; then\n  logAutopause \"AUTOPAUSE_KNOCK_INTERFACE variable must not be empty!\"\n  autopause_error_loop\nfi\nif ! mc-image-helper network-interfaces --check=\"$AUTOPAUSE_KNOCK_INTERFACE\" ; then\n  logAutopause \"Selected interface \\\"$AUTOPAUSE_KNOCK_INTERFACE\\\" does not exist!\"\n  autopause_error_loop\nfi\n\nknockdArgs=(-c /tmp/knockd-config.cfg -d -i \"$AUTOPAUSE_KNOCK_INTERFACE\")\nif isTrue \"${DEBUG_AUTOPAUSE}\"; then\n  knockdArgs+=(-D)\nfi\n\n/usr/local/sbin/knockd \"${knockdArgs[@]}\"\n\nif [ $? -ne 0 ] ; then\n  logAutopause \"Failed to start knockd daemon.\"\n  logAutopause \"Probable cause: Unable to attach to interface \\\"$AUTOPAUSE_KNOCK_INTERFACE\\\".\"\n  autopause_error_loop\nfi\n\nSTATE=INIT\n\nwhile :\ndo\n  isTrue \"${DEBUG_AUTOPAUSE}\" && log \"DEBUG: autopause state = $STATE\"\n  case X$STATE in\n  XINIT)\n    # Server startup\n    if mc_server_listening ; then\n      TIME_THRESH=$(($(current_uptime)+$AUTOPAUSE_TIMEOUT_INIT))\n\n      if [ -e /data/.skip-pause ] ; then\n        logAutopause \"'/data/.skip-pause' file is present - skipping pausing\"\n      else\n        logAutopause \"MC Server listening for connections - pausing in $AUTOPAUSE_TIMEOUT_INIT seconds\"\n      fi\n\n      STATE=K\n    fi\n    ;;\n  XK)\n    # Knocked\n    if java_clients_connected ; then\n      logAutopause \"Client connected - waiting for disconnect\"\n      STATE=E\n    elif [ -e /data/.skip-pause ] ; then\n      logAutopause \"'/data/.skip-pause' file is present - skipping pausing\"\n      STATE=E\n    else\n      if [[ $(current_uptime) -ge $TIME_THRESH ]] ; then\n        logAutopause \"No client connected since startup / knocked - pausing\"\n        \"$(dirname \"$0\")/pause.sh\"\n        STATE=S\n      fi\n    fi\n    ;;\n  XE)\n    # Established\n    if ! java_clients_connected ; then\n      TIME_THRESH=$(($(current_uptime)+$AUTOPAUSE_TIMEOUT_EST))\n      logAutopause \"All clients disconnected - pausing in $AUTOPAUSE_TIMEOUT_EST seconds\"\n      STATE=I\n    fi\n    ;;\n  XI)\n    # Idle\n    if java_clients_connected ; then\n      logAutopause \"Client reconnected - waiting for disconnect\"\n      STATE=E\n    elif [ -e /data/.skip-pause ] ; then\n      TIME_THRESH=$(($(current_uptime)+$AUTOPAUSE_TIMEOUT_EST))\n      logAutopause \"'/data/.skip-pause' file is present - skipping pausing\"\n      STATE=E\n    else\n      if [[ $(current_uptime) -ge $TIME_THRESH ]] ; then\n        logAutopause \"No client reconnected - pausing\"\n        \"$(dirname \"$0\")/pause.sh\"\n        STATE=S\n      fi\n    fi\n    ;;\n  XS)\n    # Stopped\n    if rcon_client_exists ; then\n      \"$(dirname \"$0\")/resume.sh\"\n    fi\n    if java_running ; then\n      if java_clients_connected ; then\n        logAutopause \"Client connected - waiting for disconnect\"\n        STATE=E\n      else\n        TIME_THRESH=$(($(current_uptime)+$AUTOPAUSE_TIMEOUT_KN))\n        logAutopause \"Server was knocked - waiting for clients or timeout\"\n        STATE=K\n      fi\n    fi\n    ;;\n  *)\n    logAutopause \"Error: invalid state: $STATE\"\n    ;;\n  esac\n  if [[ \"$STATE\" == \"S\" ]] ; then\n    # before rcon times out\n    sleep 5\n  else\n    sleep \"$AUTOPAUSE_PERIOD\"\n  fi\ndone\n"
  },
  {
    "path": "scripts/auto/autopause-fcns.sh",
    "content": "#!/bin/bash\n\n# shellcheck source=../scripts/start-utils\n. \"$(dirname \"$0\")/../start-utils\"\ncurrent_uptime() {\n  awk '{print $1}' /proc/uptime | cut -d . -f 1\n}\n\njava_running() {\n  [[ $( ps -ax -o stat,comm | grep 'java' | awk '{ print $1 }') =~ ^S.*$ ]]\n}\n\njava_process_exists() {\n  [[ -n \"$(ps -ax -o comm | grep 'java')\" ]]\n}\n\nrcon_client_exists() {\n  [[ -n \"$(ps -ax -o comm | grep 'rcon-cli')\" ]]\n}\n\nuse_proxy() {\n  if isTrue \"$USES_PROXY_PROTOCOL\"; then\n    echo \"--use-proxy\"\n  fi\n}\n\nuse_server_list_ping() {\n  if [[ \"${VERSION^^}\" == \"LATEST\" || \"${VERSION^^}\" == \"SNAPSHOT\" ]]; then\n    # Don't use server-list ping for unknown version\n    return 1\n  fi\n\n  if versionLessThan 1.7; then\n    echo \"--use-server-list-ping\"\n  fi\n}\n\n\nmc_server_listening() {\n  mc-monitor status $(use_proxy) --host \"${SERVER_HOST:-localhost}\" --port \"$SERVER_PORT\" $(use_server_list_ping) --timeout 10s >&/dev/null\n}\n\njava_clients_connections() {\n  local connections\n  if java_running; then\n    if ! connections=$(mc-monitor status \\\n        --host \"${SERVER_HOST:-localhost}\" \\\n        --port \"$SERVER_PORT\" \\\n        --retry-limit \"${AUTOPAUSE_STATUS_RETRY_LIMIT:-10}\" --retry-interval \"${AUTOPAUSE_STATUS_RETRY_INTERVAL:-2s}\" \\\n        $(use_proxy) $(use_server_list_ping) \\\n        --show-player-count); then\n      # consider it a non-zero player count if the ping fails\n      # otherwise a laggy server with players connected could get paused\n      connections=1\n    fi\n  else\n    connections=0\n  fi\n  echo $connections\n}\n\njava_clients_connected() {\n  (( $(java_clients_connections) > 0 ))\n}\n"
  },
  {
    "path": "scripts/auto/autostop-daemon.sh",
    "content": "#!/bin/bash\n\n# needed for the clients connected function residing in autopause\n. \"$(dirname \"$0\")/autopause-fcns.sh\"\n\n# shellcheck source=../../scripts/start-utils\n. \"$(dirname \"$0\")/../start-utils\"\nif isTrue \"${DEBUG_AUTOSTOP}\"; then\n  set -x\nfi\n\n# wait for java process to be started\nwhile :\ndo\n  if java_process_exists ; then\n    break\n  fi\n  sleep 0.1\ndone\n\nSTATE=INIT\n\nwhile :\ndo\n  isTrue \"${DEBUG_AUTOSTOP}\" && log \"DEBUG: autostop state = $STATE\"\n  case X$STATE in\n  XINIT)\n    # Server startup\n    if mc_server_listening ; then\n      TIME_THRESH=$(($(current_uptime)+AUTOSTOP_TIMEOUT_INIT))\n\n      if [ -e /data/.skip-stop ] ; then\n        logAutostop \"'/data/.skip-stop' file is present - skipping stopping\"\n      else\n        logAutostop \"MC Server listening for connections - stopping in $AUTOSTOP_TIMEOUT_INIT seconds\"\n      fi\n\n      STATE=II\n    fi\n    ;;\n  XII)\n    # Initial idle\n    if java_clients_connected ; then\n      logAutostop \"Client connected - waiting for disconnect\"\n      STATE=E\n    elif [ -e /data/.skip-stop ] ; then\n      logAutostop \"'/data/.skip-stop' file is present - skipping stopping\"\n      STATE=E\n    else\n      if [[ $(current_uptime) -ge $TIME_THRESH ]] ; then\n        logAutostop \"No client connected since startup - stopping server\"\n        \"$(dirname \"$0\")\"/stop.sh\n        exit 0\n      fi\n    fi\n    ;;\n  XE)\n    # Established\n    if ! java_clients_connected ; then\n      TIME_THRESH=$(($(current_uptime)+$AUTOSTOP_TIMEOUT_EST))\n      logAutostop \"All clients disconnected - stopping in $AUTOSTOP_TIMEOUT_EST seconds\"\n      STATE=I\n    fi\n    ;;\n  XI)\n    # Idle\n    if java_clients_connected ; then\n      logAutostop \"Client reconnected - waiting for disconnect\"\n      STATE=E\n    elif [ -e /data/.skip-stop ] ; then\n      TIME_THRESH=$(($(current_uptime)+$AUTOSTOP_TIMEOUT_EST))\n      logAutostop \"'/data/.skip-stop' file is present - skipping stopping\"\n      STATE=E\n    else\n      if [[ $(current_uptime) -ge $TIME_THRESH ]] ; then\n        logAutostop \"No client reconnected - stopping\"\n        \"$(dirname \"$0\")\"/stop.sh\n        exit 0\n      fi\n    fi\n    ;;\n  *)\n    logAutostop \"Error: invalid state: $STATE\"\n    ;;\n  esac\n  sleep $AUTOSTOP_PERIOD\ndone\n"
  },
  {
    "path": "scripts/auto/pause.sh",
    "content": "#!/bin/bash\n\n. \"$(dirname \"$0\")/../start-utils\"\nif isTrue \"${DEBUG_AUTOPAUSE}\"; then\n  set -x\nfi\n\nif [[ $( ps -ax -o stat,comm | grep 'java' | awk '{ print $1 }') =~ ^S.*$ ]] ; then\n  # save world\n  rcon-cli save-all >/dev/null\n\n  # wait until mc-monitor is no longer connected to the server\n  while :\n  do\n    if [[ -z \"$(netstat -nt | grep \"127.0.0.1:$SERVER_PORT\" | grep 'ESTABLISHED')\" ]]; then\n      break\n    fi\n    sleep 0.1\n  done\n\n  # finally pause the process\n  logAutopauseAction \"Pausing Java process\"\n  pkill -STOP java\n\n  # create .paused file in data directory\n  touch /data/.paused\nfi\n"
  },
  {
    "path": "scripts/auto/rcon-cmds-daemon.sh",
    "content": "#!/bin/bash\n\n: \"${RCON_CMDS_STARTUP:=}\"\n: \"${RCON_CMDS_ON_CONNECT:=}\"\n: \"${RCON_CMDS_ON_DISCONNECT:=}\"\n: \"${RCON_CMDS_FIRST_CONNECT:=}\"\n: \"${RCON_CMDS_LAST_DISCONNECT:=}\"\n: \"${RCON_CMDS_PERIOD:=10}\"\n\n# needed for the clients connected function residing in autopause\n# shellcheck source=../auto/autopause-fcns.sh\n. /image/scripts/auto/autopause-fcns.sh\n\n# shellcheck source=start-utils\n. /image/scripts/start-utils\n\nrun_command(){\n  rcon_cmd=\"$1\"\n  logRcon \"running - $rcon_cmd\"\n  output=$(rcon-cli \"$rcon_cmd\")\n  logRcon \"$output\"\n}\n\n\n# wait for java process to be started\nwhile :\ndo\n  if java_process_exists ; then\n    break\n  fi\n  sleep 0.1\ndone\n\nCLIENTCONNECTIONS=0\nSTATE=INIT\n\nwhile :\ndo\n  case X$STATE in\n  XINIT)\n    # Server startup\n    if mc_server_listening ; then\n      logRcon \"MCServer is listening, running startup\"\n      if [[ \"$RCON_CMDS_STARTUP\" ]]; then\n        while read -r cmd; do\n          run_command \"$cmd\"\n        done <<< \"$(echo -e \"$RCON_CMDS_STARTUP\")\"\n      fi\n      if \n        [[ -z \"$RCON_CMDS_ON_CONNECT\" ]] && \n        [[ -z \"$RCON_CMDS_ON_DISCONNECT\" ]] && \n        [[ -z \"$RCON_CMDS_FIRST_CONNECT\" ]] && \n        [[ -z \"$RCON_CMDS_LAST_DISCONNECT\" ]]\n      then\n        logRcon \"No addition rcon commands are given, stopping rcon cmd service\"\n        exit 0\n      fi\n      STATE=II\n    fi\n    ;;\n  XII)\n    CURR_CLIENTCONNECTIONS=$(java_clients_connections)\n    # First client connection\n    # Setting priority run order: on first client connection is usually to STOP maintence, aka DO THIS FIRST\n    if (( CURR_CLIENTCONNECTIONS > 0 )) && (( CLIENTCONNECTIONS == 0 )) && [[ \"$RCON_CMDS_FIRST_CONNECT\" ]]; then\n        logRcon \"First Clients has Connected, running first connect cmds\"\n        while read -r cmd; do\n          run_command \"$cmd\"\n        done <<< \"$(echo -e \"$RCON_CMDS_FIRST_CONNECT\")\"\n    fi\n\n    # When a client joins\n    if (( CURR_CLIENTCONNECTIONS > CLIENTCONNECTIONS )) && [[ \"$RCON_CMDS_ON_CONNECT\" ]]; then\n        logRcon \"Clients have Connected, running connect cmds\"\n        while read -r cmd; do\n          run_command \"$cmd\"\n        done <<< \"$(echo -e \"$RCON_CMDS_ON_CONNECT\")\"\n    # When a client leaves\n    elif (( CURR_CLIENTCONNECTIONS < CLIENTCONNECTIONS )) && [[ \"$RCON_CMDS_ON_DISCONNECT\" ]]; then\n        logRcon \"Clients have Disconnected, running disconnect cmds\"\n        while read -r cmd; do\n          run_command \"$cmd\"\n        done <<< \"$(echo -e \"$RCON_CMDS_ON_DISCONNECT\")\"\n    fi\n\n    # Last client connection\n    # Setting priority run order: on last client connection is usually to START maintence, aka DO THIS LAST\n    if (( CURR_CLIENTCONNECTIONS == 0 )) && (( CLIENTCONNECTIONS > 0 )) && [[ \"$RCON_CMDS_LAST_DISCONNECT\" ]]; then\n        logRcon \"ALL Clients have Disconnected, running last disconnect cmds\"\n        while read -r cmd; do\n          run_command \"$cmd\"\n        done <<< \"$(echo -e \"$RCON_CMDS_LAST_DISCONNECT\")\"\n    fi\n    CLIENTCONNECTIONS=$CURR_CLIENTCONNECTIONS\n    ;;\n  *)\n    logRcon \"Error: invalid state: $STATE\"\n    ;;\n  esac\n  sleep \"$RCON_CMDS_PERIOD\"\ndone\n"
  },
  {
    "path": "scripts/auto/resume.sh",
    "content": "#!/bin/bash\n\n. \"$(dirname \"$0\")/../start-utils\"\nif isTrue \"${DEBUG_AUTOPAUSE}\"; then\n  set -x\nfi\n\nif [[ $( ps -ax -o stat,comm | grep 'java' | awk '{ print $1 }') =~ ^T.*$ ]] ; then\n  pkill -CONT java\n\n  # remove .paused file from data directory\n  rm -f /data/.paused\nfi\n"
  },
  {
    "path": "scripts/auto/stop.sh",
    "content": "#!/bin/bash\n\n. \"$(dirname \"$0\")/../start-utils\"\nif isTrue \"${DEBUG_AUTOSTOP}\"; then\n  set -x\nfi\n\nlogAutostopAction \"Stopping Java process\"\npkill -f --signal SIGTERM mc-server-runner\n\n"
  },
  {
    "path": "scripts/shims/mc-health",
    "content": "#!/bin/bash\n\n. \"/image/scripts/start-utils\"\nif [ -f /data/.mc-health.env ]; then\n  . /data/.mc-health.env\nfi\n\nif isTrue \"${DISABLE_HEALTHCHECK}\"; then\n  echo \"Healthcheck disabled\"\n  exit 0\nelif isTrue \"${ENABLE_AUTOPAUSE}\" && [[ \"$( ps -ax -o stat,comm | grep 'java' | awk '{ print $1 }')\" =~ ^T.*$ ]]; then\n  echo \"Java process suspended by Autopause function\"\n  exit 0\nelse\n  mc-monitor status \"${MC_HEALTH_EXTRA_ARGS[@]}\" --host \"${SERVER_HOST:-localhost}\" --port \"${SERVER_PORT:-25565}\"\n  exit $?\nfi\n"
  },
  {
    "path": "scripts/shims/mc-send-to-console",
    "content": "#!/bin/bash\n. \"/image/scripts/start-utils\"\n: \"${CONSOLE_IN_NAMED_PIPE:=/tmp/minecraft-console-in}\"\n\nif isFalse \"${CREATE_CONSOLE_IN_PIPE:-false}\"; then\n  error \"Console pipe needs to be enabled by setting CREATE_CONSOLE_IN_PIPE to true\"\nfi\n\n\nif [ $# = 0 ]; then\n  error \"Pass console commands as arguments\"\n  exit 1\nfi\n\nif [ ! -p \"${CONSOLE_IN_NAMED_PIPE}\" ]; then\n  error \"Named pipe ${CONSOLE_IN_NAMED_PIPE} is missing\"\n  exit 1\nfi\n\nif [[ \"$(id -u)\" = 0 ]] && [[ $UID != 0 ]]; then\n  error \"Exec needs to be run with user ID $UID\"\n  exit 2\nelse\n  echo \"$@\" >\"${CONSOLE_IN_NAMED_PIPE}\"\nfi\n"
  },
  {
    "path": "scripts/shims/mcstatus",
    "content": "#!/bin/bash\n\nwarning \"mcstatus is deprecated; calling mc-monitor instead\"\n\n##### mcstatus shim for mc-monitor\n# handles translating calls to\n#   mcstatus (host:port) (command)\n# where the actual command is ignore, but is typically ping or status\n\naddr=\"$1\"\n\nIFS=':'\nread -a parts <<< \"${addr}\"\nargs=(--host ${parts[0]}) \nif [[ ${#parts[*]} -gt 1 ]]; then\n    args+=(--port ${parts[1]})\nfi\nexec mc-monitor ${args[@]}"
  },
  {
    "path": "scripts/start",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\n# The Dockerfile ENVs take precedence here, but defaulting for testing consistency\n: \"${UID:=1000}\"\n: \"${GID:=1000}\"\n: \"${SKIP_CHOWN_DATA:=false}\"\n\numask \"${UMASK:=0002}\"\n\n# Remove from previous run and do this as elevated user since file used to be created before demoting\nrm -f \"$HOME/.rcon-cli.env\"\n\nif ! isTrue \"${SKIP_SUDO:-false}\" && [ \"$(id -u)\" = 0 ]; then\n  runAsUser=minecraft\n  runAsGroup=minecraft\n\n  if [[ -v UID ]]; then\n    if [[ $UID != 0 ]]; then\n      if [[ $UID != $(id -u minecraft) ]]; then\n        log \"Changing uid of minecraft to $UID\"\n        usermod -u $UID minecraft\n      fi\n    else\n      runAsUser=root\n    fi\n  fi\n\n  if [[ -v GID ]]; then\n    if [[ $GID != 0 ]]; then\n      if [[ $GID != $(id -g minecraft) ]]; then\n        log \"Changing gid of minecraft to $GID\"\n        groupmod -o -g \"$GID\" minecraft\n      fi\n    else\n      runAsGroup=root\n    fi\n  fi\n\n  if isFalse \"${SKIP_CHOWN_DATA}\" && [[ $(stat -c \"%u\" /data) != \"$UID\" ]]; then\n    log \"Changing ownership of /data to $UID ...\"\n    chown -R ${runAsUser}:${runAsGroup} /data\n  fi\n\n  if [[ ${SKIP_NSSWITCH_CONF^^} != TRUE ]]; then\n    echo 'hosts: files dns' > /etc/nsswitch.conf\n  fi\n\n  exec gosu ${runAsUser}:${runAsGroup} \"$(dirname \"$0\")/start-configuration\" \"$@\"\nelse\n  exec \"$(dirname \"$0\")/start-configuration\" \"$@\"\nfi\n"
  },
  {
    "path": "scripts/start-autopause",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\n: \"${SERVER_PORT:=25565}\"\n: \"${ENABLE_AUTOPAUSE:=false}\"\n: \"${AUTOPAUSE_TIMEOUT_EST:=3600}\"\n: \"${AUTOPAUSE_TIMEOUT_KN:=120}\"\n: \"${AUTOPAUSE_TIMEOUT_INIT:=600}\"\n: \"${AUTOPAUSE_PERIOD:=10}\"\n: \"${AUTOPAUSE_KNOCK_INTERFACE:=eth0}\"\n: \"${DEBUG_AUTOPAUSE:=false}\"\nexport SERVER_PORT\nexport ENABLE_AUTOPAUSE\nexport AUTOPAUSE_TIMEOUT_EST\nexport AUTOPAUSE_TIMEOUT_KN\nexport AUTOPAUSE_TIMEOUT_INIT\nexport AUTOPAUSE_PERIOD\nexport AUTOPAUSE_KNOCK_INTERFACE\nexport DEBUG_AUTOPAUSE\n\nlog \"Autopause functionality enabled\"\n\nisDebugging && set -x\n\ncp /image/knockd-config.cfg /tmp/knockd-config.cfg\n\nfunction updatePort() {\n  regseq=\"^\\s*sequence\\s*=\\s*$1\\s*$\"\n  linenum=$(grep -nm${2} sequence /tmp/knockd-config.cfg | cut -d : -f 1 | tail -n1)\n  if ! [[ $(awk \"NR==$linenum\" /tmp/knockd-config.cfg) =~ $regseq ]]; then\n    sed -i \"${linenum}s/sequence.*/sequence = $1/\" /tmp/knockd-config.cfg\n    log \"Updated $3 port in knockd config\"\n  fi\n}\n\n# update server port to listen to\nupdatePort $SERVER_PORT 1 \"server\"\n\n# update rcon port to listen to\nupdatePort $RCON_PORT 2 \"rcon\"\n\nisNumericElseSetToDefault \"AUTOPAUSE_PERIOD\" 10\ncheckIfNotZeroElseSetToDefault \"AUTOPAUSE_PERIOD\" 10\nisNumericElseSetToDefault \"AUTOPAUSE_TIMEOUT_KN\" 120\nisNumericElseSetToDefault \"AUTOPAUSE_TIMEOUT_EST\" 3600\nisNumericElseSetToDefault \"AUTOPAUSE_TIMEOUT_INIT\" 600\n\nif [[ \"$AUTOPAUSE_KNOCK_INTERFACE\" == \"lo\" ]] ; then\n  logWarning \"AUTOPAUSE_KNOCK_INTERFACE is set to the local loopback interface.\"\n  logWarning \"         This is not advisable, as incoming connections are likely not picked up there.\"\n  logWarning \"         Continuing with this setting.\"\nfi\n\nif [[ -n \"$MAX_TICK_TIME\" && \"$MAX_TICK_TIME\" != \"-1\" ]] ; then\n  logWarning \"MAX_TICK_TIME is non-default, for autopause to work properly, this check should be disabled (-1 for versions >= 1.8.1)\"\nelif [[ -z \"$MAX_TICK_TIME\" ]] ; then\n  MAX_TICK_TIME=-1\n  if versionLessThan 1.8.1; then\n    # 10 years\n    MAX_TICK_TIME=315360000000\n  fi\n  export MAX_TICK_TIME\nfi\n\n# seq_cooldown cannot be larger than AUTOPAUSE_TIMEOUT_KN, otherwise the server may\n# become paused while knockd is still ignoring packets, preventing players from joining.\nlet COOLDOWN=$AUTOPAUSE_TIMEOUT_KN/2\nsed -i \"s/\\(seq_cooldown *= *\\).*/\\1$COOLDOWN/\" /tmp/knockd-config.cfg\n\n\"$(dirname \"$0\")/auto/autopause-daemon.sh\" &\n"
  },
  {
    "path": "scripts/start-autostop",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\n: \"${SERVER_PORT:=25565}\"\n: \"${AUTOSTOP_TIMEOUT_EST:=3600}\"\n: \"${AUTOSTOP_TIMEOUT_INIT:=1800}\"\n: \"${AUTOSTOP_PERIOD:=10}\"\n: \"${DEBUG_AUTOSTOP:=false}\"\nexport SERVER_PORT\nexport AUTOSTOP_TIMEOUT_EST\nexport AUTOSTOP_TIMEOUT_INIT\nexport AUTOSTOP_PERIOD\nexport DEBUG_AUTOSTOP\n\nlog \"Autostop functionality enabled\"\n\nisDebugging && set -x\n\nisNumericElseSetToDefault \"AUTOSTOP_PERIOD\" 10\ncheckIfNotZeroElseSetToDefault \"AUTOSTOP_PERIOD\" 10\nisNumericElseSetToDefault \"AUTOSTOP_TIMEOUT_EST\" 3600\nisNumericElseSetToDefault \"AUTOSTOP_TIMEOUT_INIT\" 1800\n\n\"$(dirname \"$0\")/auto/autostop-daemon.sh\" &\n"
  },
  {
    "path": "scripts/start-configuration",
    "content": "#!/bin/bash\nset -euo pipefail\nIFS=$'\\n\\t'\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\n: \"${EULA:=}\"\n: \"${PROXY:=}\"\n: \"${ENABLE_AUTOPAUSE:=false}\"\n: \"${ENABLE_AUTOSTOP:=false}\"\n: \"${RCON_CMDS_STARTUP:=}\"\n: \"${RCON_CMDS_ON_CONNECT:=}\"\n: \"${RCON_CMDS_ON_DISCONNECT:=}\"\n: \"${RCON_CMDS_FIRST_CONNECT:=}\"\n: \"${RCON_CMDS_LAST_DISCONNECT:=}\"\n: \"${RCON_CMDS_PERIOD:=10}\"\n: \"${ENABLE_RCON:=true}\"\n: \"${RCON_PORT:=25575}\"\nexport ENABLE_RCON RCON_PORT\n\n: \"${MEMORY=1G}\"\n: \"${INIT_MEMORY=${MEMORY}}\"\n: \"${MAX_MEMORY=${MEMORY}}\"\nexport MEMORY INIT_MEMORY MAX_MEMORY\n\nshopt -s nullglob\n\nisDebugging && set -x\n\nif (( $(mc-image-helper java-release) >= 17 )); then\n  MC_IMAGE_HELPER_OPTS+=\" --enable-native-access=ALL-UNNAMED\"\n  export MC_IMAGE_HELPER_OPTS\nfi\n\nexport HOME=/data\n\nlog \"Running as uid=$(id -u) gid=$(id -g) with /data as '$(ls -lnd /data)'\"\nlog \"Image info: $(paste -d, -s /etc/image.properties)\"\n\nif [ ! -e /data/eula.txt ]; then\n  if ! isTrue \"$EULA\"; then\n    log \"\"\n    logError \"Please accept the Minecraft EULA at\"\n    logError \"  https://account.mojang.com/documents/minecraft_eula\"\n    logError \"by adding the following immediately after 'docker run':\"\n    logError \"  -e EULA=TRUE\"\n    log \"\"\n    exit 1\n  fi\n\n  writeEula\nfi\n\nif isTrue \"${DEBUG_MEMORY:-false}\"; then\n  log \"Memory usage and availability (in MB)\"\n  uname -pars | applyLogPrefix\n  free -m | applyLogPrefix\nfi\n\n##########################################\n# Setup RCON password\n\nif isTrue \"${ENABLE_RCON:-true}\"; then\n  # turn off debug output\n  set +x\n\n  if [[ -v RCON_PASSWORD_FILE ]]; then\n    if [ ! -e \"${RCON_PASSWORD_FILE}\" ]; then\n      log \"\"\n      logError \"Initial RCON password file ${RCON_PASSWORD_FILE} does not seems to exist.\"\n      logError \"Please ensure your configuration.\"\n      logError \"If you are using Docker Secrets feature, please check this for further information: \"\n      logError \" https://docs.docker.com/engine/swarm/secrets\"\n      log \"\"\n      exit 1\n    else\n      RCON_PASSWORD=$(cat \"${RCON_PASSWORD_FILE}\")\n      export RCON_PASSWORD\n    fi\n  elif ! [[ -v RCON_PASSWORD ]]; then\n    RCON_PASSWORD=$(openssl rand -hex 12)\n    export RCON_PASSWORD\n  fi\n\n  # For rcon-cli access running via exec, which by default is running as root\n  echo \"password=${RCON_PASSWORD}\" > \"$HOME/.rcon-cli.env\"\n  echo \"password: \\\"${RCON_PASSWORD}\\\"\" > \"$HOME/.rcon-cli.yaml\"\n\n  isDebugging && set -x\nelse\n  rm -f \"$HOME/.rcon-cli.env\" \"$HOME/.rcon-cli.yaml\"\nfi\n\n##########################################\n# Auto-pause/stop\n\nif isTrue \"${ENABLE_AUTOPAUSE}\" && isTrue \"${ENABLE_AUTOSTOP}\"; then\n    logError \"ENABLE_AUTOPAUSE=true is incompatible with ENABLE_AUTOSTOP=true\"\n    exit 1\nfi\n\nproxyArgs=()\nif [[ $PROXY ]]; then\n    export http_proxy=\"$PROXY\"\n    export https_proxy=\"$PROXY\"\n\n    IFS=\":\"\n    read -ra parts <<< \"$PROXY\"\n    IFS=\" \"\n    : \"${PROXY_HOST=$(firstArrayElement parts)}\"\n    shiftArray parts\n    : \"${PROXY_PORT=$(firstArrayElement parts)}\"\nfi\n\n# https://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html\nproxyArgs=()\nfunction addToProxyArgs() {\n  if [[ $2 ]]; then\n    proxyArgs+=(\"-D$1=$2\")\n  fi\n}\naddToProxyArgs http.proxyHost \"${PROXY_HOST:-}\"\naddToProxyArgs http.proxyPort \"${PROXY_PORT:-}\"\naddToProxyArgs http.nonProxyHosts \"${PROXY_NON_PROXY_HOSTS:-}\"\nexport MC_IMAGE_HELPER_OPTS+=\" ${proxyArgs[*]}\"\n\nfunction fixJavaPath() {\n  # Some Docker management UIs grab all the image declared variables and present them for configuration.\n  # When upgrading images across Java versions, that creates parts mismatch in PATH's expected by base image.\n  if ! which java > /dev/null; then\n    logError \"       Your Docker provider has an annoying flaw where it\"\n    logError \"       tries to set PATH even though the container establishes\"\n    logError \"       parts very specific value.\"\n    sleep 2\n    # now find where java might be\n    for d in /opt/java/openjdk/bin /usr/bin; do\n      if [ -x \"${d}/java\" ]; then\n        PATH=\"${PATH}:${d}\"\n        return 0\n      fi\n    done\n    return 1\n  fi\n}\n\n\nif ! fixJavaPath; then\n  logError \"could not locate path that contains java\"\n  exit 1\nfi\n\ncd /data || exit 1\n\nexport DECLARED_TYPE=${TYPE^^}\nexport DECLARED_VERSION=\"$VERSION\"\n\nif isTrue \"${VERSION_FROM_MODRINTH_PROJECTS:-}\" && [[ ${MODRINTH_PROJECTS:-} ]]; then\n  if ! VERSION=$(mc-image-helper version-from-modrinth-projects --projects \"${MODRINTH_PROJECTS}\"); then\n    logError \"failed to resolve version from MODRINTH_PROJECTS: ${MODRINTH_PROJECTS}\"\n    exit 1\n  fi\n  log \"Resolved Minecraft version $VERSION from Modrinth projects\"\n  export VERSION\nfi\n\nif isTrue \"${ENABLE_AUTOPAUSE}\"; then\n  \"$(dirname \"$0\")/start-autopause\"\nelif isTrue \"${ENABLE_AUTOSTOP}\"; then\n  \"$(dirname \"$0\")/start-autostop\"\nfi\n\nif\n  [[ \"$RCON_CMDS_STARTUP\" ]] ||\n  [[ \"$RCON_CMDS_ON_CONNECT\" ]] ||\n  [[ \"$RCON_CMDS_ON_DISCONNECT\" ]] ||\n  [[ \"$RCON_CMDS_FIRST_CONNECT\" ]] ||\n  [[ \"$RCON_CMDS_LAST_DISCONNECT\" ]]\nthen\n  log \"Starting RCON commands\"\n  # shellcheck source=start-rconcmds\n  \"$(dirname \"$0\")/start-rconcmds\"\nfi\n\n: \"${MODPACK_PLATFORM:=${MOD_PLATFORM:-}}\"\n\nif [[ $MODPACK_PLATFORM && $TYPE && $TYPE != VANILLA ]]; then\n  logWarning \"Avoid setting TYPE and MODPACK_PLATFORM\"\nfi\n\ncase \"${TYPE^^}\" in\n  AUTO_CURSEFORGE|MODRINTH|CURSEFORGE|FTB|FTBA|GTNH)\n    MODPACK_PLATFORM=\"$TYPE\"\n  ;;\nesac\n\nif [[ $MODPACK_PLATFORM ]]; then\n  case \"${MODPACK_PLATFORM^^}\" in\n    FTB|CURSEFORGE)\n      exec \"$(dirname \"$0\")/start-deployCF\" \"$@\"\n    ;;\n\n    FTBA)\n      exec \"$(dirname \"$0\")/start-deployFTBA\" \"$@\"\n    ;;\n\n    AUTO_CURSEFORGE)\n      exec \"$(dirname \"$0\")/start-deployAutoCF\" \"$@\"\n      ;;\n\n    MODRINTH)\n      exec \"$(dirname \"$0\")/start-deployModrinth\" \"$@\"\n      ;;\n\n    GTNH)\n      exec \"$(dirname \"$0\")/start-deployGTNH\" \"$@\"\n      ;;\n\n    *)\n      logError \"Invalid MODPACK_PLATFORM: '$MODPACK_PLATFORM'\"\n      exit 1\n      ;;\n  esac\nfi\n\nlog \"Resolving type given ${TYPE}\"\ncase \"${TYPE^^}\" in\n  *BUKKIT|SPIGOT)\n    exec \"$(dirname \"$0\")/start-deployBukkitSpigot\" \"$@\"\n  ;;\n\n  PAPER)\n    exec \"$(dirname \"$0\")/start-deployPaper\" \"$@\"\n  ;;\n\n  FOLIA)\n    exec \"$(dirname \"$0\")/start-deployFolia\" \"$@\"\n  ;;\n\n  FORGE)\n    exec \"$(dirname \"$0\")/start-deployForge\" \"$@\"\n  ;;\n\n  NEOFORGE|NEOFORGED)\n    exec \"$(dirname \"$0\")/start-deployNeoForge\" \"$@\"\n  ;;\n\n  FABRIC)\n    exec \"$(dirname \"$0\")/start-deployFabric\" \"$@\"\n  ;;\n\n  QUILT)\n    exec \"$(dirname \"$0\")/start-deployQuilt\" \"$@\"\n  ;;\n\n  VANILLA)\n    exec \"$(dirname \"$0\")/start-deployVanilla\" \"$@\"\n  ;;\n\n  SPONGEVANILLA)\n    exec \"$(dirname \"$0\")/start-deploySpongeVanilla\" \"$@\"\n  ;;\n\n  CUSTOM)\n    exec \"$(dirname \"$0\")/start-deployCustom\" \"$@\"\n  ;;\n\n  MAGMA)\n    exec \"$(dirname \"$0\")/start-deployMagma\" \"$@\"\n  ;;\n\n  MAGMA_MAINTAINED)\n    exec \"$(dirname \"$0\")/start-deployMagmaMaintained\" \"$@\"\n  ;;\n\n  KETTING)\n    exec \"$(dirname \"$0\")/start-deployKetting\" \"$@\"\n  ;;\n\n  MOHIST|YOUER|BANNER)\n    exec \"$(dirname \"$0\")/start-deployMohist\" \"$@\"\n  ;;\n\n  PURPUR)\n    exec \"$(dirname \"$0\")/start-deployPurpur\" \"$@\"\n  ;;\n\n  PUFFERFISH)\n    exec \"$(dirname \"$0\")/start-deployPufferfish\" \"$@\"\n  ;;\n\n  CANYON)\n    exec \"$(dirname \"$0\")/start-deployCanyon\" \"$@\"\n  ;;\n\n  LIMBO)\n    exec \"$(dirname \"$0\")/start-deployLimbo\" \"$@\"\n  ;;\n\n  NANOLIMBO)\n    exec \"$(dirname \"$0\")/start-deployNanoLimbo\" \"$@\"\n  ;;\n\n  CRUCIBLE)\n    exec \"$(dirname \"$0\")/start-deployCrucible\" \"$@\"\n  ;;\n\n  LEAF)\n    exec \"$(dirname \"$0\")/start-deployLeaf\" \"$@\"\n  ;;\n\n  ARCLIGHT)\n    exec \"$(dirname \"$0\")/start-deployArcLight\" \"$@\"\n  ;;\n\n  POSEIDON)\n    exec \"$(dirname \"$0\")/start-deployPoseidon\" \"$@\"\n  ;;\n\n  *)\n      logError \"Invalid TYPE: '$TYPE'\"\n      logError \"Must be: VANILLA, FORGE, BUKKIT, SPIGOT, PAPER, FOLIA, PURPUR, FABRIC, QUILT,\"\n      logError \"         SPONGEVANILLA, CUSTOM, MAGMA, MOHIST, GTNH, AIRPLANE, PUFFERFISH,\"\n      logError \"         CANYON, LIMBO, NANOLIMBO, CRUCIBLE, LEAF, YOUER, BANNER, NEOFORGE\"\n      exit 1\n  ;;\n\nesac\n"
  },
  {
    "path": "scripts/start-deployArcLight",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"${SCRIPTS:-$(dirname \"$0\")}/start-utils\"\nset -o pipefail\nset -e\nisDebugging && set -x\n\nresolveVersion\n: \"${ARCLIGHT_RELEASE:=latest}\"\n: \"${ARCLIGHT_TYPE:=neoforge}\"\n\narclightReleasesUrl=https://api.github.com/repos/IzzelAliz/Arclight/releases\nif [[ ${ARCLIGHT_RELEASE^^} = LATEST ]]; then\n  arclightReleaseUrl=${arclightReleasesUrl}/latest\nelse\n  arclightReleaseUrl=${arclightReleasesUrl}/tags/${ARCLIGHT_RELEASE}\nfi\n\nif ! downloadUrl=$(get --json-path \"$.assets[?(@.name =~ /arclight-${ARCLIGHT_TYPE,,}-${VERSION}-.*\\.jar/)].browser_download_url\" \\\n    --accept \"application/vnd.github.v3+json\" \"$arclightReleaseUrl\"); then\n  logError \"Failed to access ${ARCLIGHT_RELEASE} release of Arclight\"\n  exit 1\nfi\n\nif [[ $downloadUrl = null ]]; then\n  logError \"Failed to locate Arclight jar for $VERSION from ${ARCLIGHT_RELEASE}\"\n  exit 1\nfi\n\nlog \"Downloading Arclight from $downloadUrl\"\nif ! SERVER=$(get --skip-existing --output-filename -o /data \"$downloadUrl\"); then\n  logError \"Arclight jar from $downloadUrl\"\n  exit 1\nfi\n\nexport SERVER\nexport FAMILY=HYBRID\nexport HYBRIDTYPE=\"${ARCLIGHT_TYPE,,}\"\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployAutoCF",
    "content": "#!/bin/bash\nset -eu\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\n: \"${CF_PAGE_URL:=}\"\n: \"${CF_SLUG:=}\"\n: \"${CF_FILE_ID:=}\"\n: \"${CF_FILENAME_MATCHER:=}\"\n: \"${CF_PARALLEL_DOWNLOADS:=4}\"\n: \"${CF_FORCE_SYNCHRONIZE:=false}\"\n: \"${CF_FORCE_REINSTALL_MODLOADER:=false}\"\n: \"${CF_IGNORE_MISSING_FILES:=}\"\n: \"${CF_EXCLUDE_INCLUDE_FILE=/image/cf-exclude-include.json}\"\n: \"${CF_EXCLUDE_MODS:=}\"\n: \"${CF_EXCLUDE_ALL_MODS:=}\"\n: \"${CF_FORCE_INCLUDE_MODS:=}\"\n: \"${CF_SET_LEVEL_FROM:=}\" # --set-level-from\n: \"${CF_OVERRIDES_SKIP_EXISTING:=false}\" # --overrides-skip-existing\n: \"${CF_OVERRIDES_EXCLUSIONS:=}\" # --overrides-exclusions\n: \"${CF_DOWNLOADS_REPO=$([ -d /downloads ] && echo '/downloads' || echo '')}\"\n: \"${CF_MODPACK_MANIFEST:=}\"\n: \"${CF_API_CACHE_DEFAULT_TTL:=}\" # as ISO-8601 duration, such as P2D or PT12H\n: \"${CF_API_KEY_FILE:=}\" # Path to file containing CurseForge API key\n: \"${CF_MOD_LOADER_VERSION:=}\" # Override mod loader version\n\nresultsFile=/data/.install-curseforge.env\n\nif [[ -n ${CF_API_KEY_FILE} ]]; then\n  if [[ -r \"${CF_API_KEY_FILE}\" ]]; then\n    CF_API_KEY=\"$(cat \"${CF_API_KEY_FILE}\")\"\n    export CF_API_KEY\n  else\n    logError \"CF_API_KEY_FILE is not readable: ${CF_API_KEY_FILE}\"\n    exit 1\n  fi\nfi\n\nisDebugging && set -x\n\nensureRemoveAllModsOff \"MODPACK_PLATFORM=AUTO_CURSEFORGE\"\n\nargs=(\n  --results-file=\"$resultsFile\"\n  --force-synchronize=\"$CF_FORCE_SYNCHRONIZE\"\n  --force-reinstall-modloader=\"$CF_FORCE_REINSTALL_MODLOADER\"\n  --overrides-skip-existing=\"$CF_OVERRIDES_SKIP_EXISTING\"\n)\nsetArg() {\n  arg=\"${1?}\"\n  var=\"${2?}\"\n\n  if [[ ${!var} ]]; then\n      args+=(\"${arg}=${!var}\")\n  fi\n}\nsetArg --modpack-page-url CF_PAGE_URL\nsetArg --file-id CF_FILE_ID\nsetArg --slug CF_SLUG\nsetArg --modpack-manifest CF_MODPACK_MANIFEST\nsetArg --filename-matcher CF_FILENAME_MATCHER\nsetArg --set-level-from CF_SET_LEVEL_FROM\nsetArg --overrides-exclusions CF_OVERRIDES_EXCLUSIONS\nsetArg --ignore-missing-files CF_IGNORE_MISSING_FILES\nsetArg --api-cache-default-ttl CF_API_CACHE_DEFAULT_TTL\nsetArg --exclude-mods CF_EXCLUDE_MODS\nsetArg --exclude-all-mods CF_EXCLUDE_ALL_MODS\nsetArg --force-include-mods CF_FORCE_INCLUDE_MODS\nsetArg --exclude-include-file CF_EXCLUDE_INCLUDE_FILE\nsetArg --mod-loader-version CF_MOD_LOADER_VERSION\nsetArg --downloads-repo CF_DOWNLOADS_REPO\n\nif ! mc-image-helper install-curseforge \"${args[@]}\"; then\n    logError \"Failed to auto-install CurseForge modpack\"\n    exit 1\nfi\n\napplyResultsFile ${resultsFile}\nresolveFamily\n\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployBukkitSpigot",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\n\nset -eo pipefail\n\nspigotBuildLog=\"/data/spigot_build.log\"\n\nfunction handleFailedSpigotBuild {\n  logError \"Failed to build Spigot\"\n  cat ${spigotBuildLog}\n  exit 1\n}\n\n\nfunction buildSpigotFromSource {\n  local tempDir=\"/data/temp\"\n\n  if [[ ${TYPE^^} = *BUKKIT ]] && ! versionLessThan \"1.14\"; then\n    logError \"Craftbukkit build is only supported for versions less than 1.14\"\n    exit 1\n  fi\n\n  log \"Building Spigot $VANILLA_VERSION from source, might take a while, get some coffee\"\n  rm -rf ${tempDir}\n  mkdir ${tempDir}\n  cd ${tempDir}\n\n  jvmOpts=\"\"\n\n  if isPercentage \"${INIT_MEMORY:-$MEMORY}\"; then\n    jvmOpts+=\"-XX:InitialRAMPercentage=$(getPercentageValue \"${INIT_MEMORY:-$MEMORY}\") \"\n  else\n    jvmOpts+=\"-Xms${INIT_MEMORY:-$MEMORY} \"\n  fi\n\n  if isPercentage \"${MAX_MEMORY:-$MEMORY}\"; then\n    jvmOpts+=\"-XX:MaxRAMPercentage=$(getPercentageValue \"${MAX_MEMORY:-$MEMORY}\")\"\n  else\n    jvmOpts+=\"-Xmx${MAX_MEMORY:-$MEMORY}\"\n  fi\n\n  logn ''\n  curl -sSL -o ${tempDir}/BuildTools.jar https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar && \\\n    java $jvmOpts -jar ${tempDir}/BuildTools.jar --rev \"$VERSION\" 2>&1 |tee ${spigotBuildLog}| while read l; do echo -n .; done; log \"done\"\n\n  case ${TYPE^^} in\n    SPIGOT)\n      if ! mv spigot-*.jar \"/data/${SERVER}\"; then\n        handleFailedSpigotBuild\n      fi\n      ;;\n    *BUKKIT)\n      if ! mv craftbukkit-*.jar \"/data/${SERVER}\"; then\n        handleFailedSpigotBuild\n      fi\n      ;;\n  esac\n\n  log \"Cleaning up\"\n  rm -rf ${tempDir}\n  cd /data\n}\n\nfunction downloadSpigot {\n  local match\n  local getBukkitBaseUrl=\"https://getbukkit.org/download/\"\n  local getBukkitSpigotUrl=\"${getBukkitBaseUrl}spigot\" \n  case \"$TYPE\" in\n    *BUKKIT|*bukkit)\n      match=\"CraftBukkit\"\n      downloadUrl=${BUKKIT_DOWNLOAD_URL}\n      getbukkitFlavor=craftbukkit\n      ;;\n    *)\n      match=\"Spigot\"\n      downloadUrl=${SPIGOT_DOWNLOAD_URL}\n      getbukkitFlavor=spigot\n      ;;\n  esac\n\n  if [[ ${VERSION^^} = LATEST ]]; then\n    if ! downloadUrl=$(restify \"${getBukkitBaseUrl}/${getbukkitFlavor}\" --id='downloadr' | jq -r '.[0] | .href '); then\n      logError \"Failed to retrieve latest version from ${getBukkitSpigotUrl} -- site might be down\"\n      exit 1\n    fi\n  fi\n\n  if [[ -z $downloadUrl ]]; then\n    downloadUrl=\"https://cdn.getbukkit.org/${getbukkitFlavor}/${getbukkitFlavor}-${VERSION}.jar\"\n  fi\n\n  log \"Downloading $match from $downloadUrl if missing ...\"\n\n  if ! SERVER=$(get -o /data --skip-existing --output-filename \"$downloadUrl\"); then\n\n    cat <<EOF\n\nERROR: failed to download from $downloadUrl\n       Visit ${getBukkitBaseUrl}${getbukkitFlavor} to lookup the\n       exact version or see if download site is unavailable.\n       Click into the version entry to find the **exact** version.\n\nEOF\n      exit 1\n    fi\n    export SERVER\n}\n\nfunction setServerVar {\n  case \"$TYPE\" in\n    *BUKKIT|*bukkit)\n      export SERVER=craftbukkit_server-${VERSION}.jar\n      ;;\n    *)\n      export SERVER=spigot_server-${VERSION}.jar\n      ;;\n  esac\n}\n\nif isTrue \"$BUILD_SPIGOT_FROM_SOURCE\" || isTrue \"$BUILD_FROM_SOURCE\"; then\n  resolveVersion\n  setServerVar\n  if [ ! -f \"$SERVER\" ] || isTrue \"$FORCE_REDOWNLOAD\"; then\n    buildSpigotFromSource\n  fi\nelse\n  downloadSpigot\nfi\n\nJVM_OPTS=\"${JVM_OPTS} -DIReallyKnowWhatIAmDoingISwear\"\nexport JVM_OPTS\n# Normalize on Spigot for operations below\nexport FAMILY=SPIGOT\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployCF",
    "content": "#!/bin/bash\n\nset -e\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\nloadForgeVars() {\n  cfgFile=${1?}\n  pat='^([^#;][^=]+)=[:space:]*([^;]*)'\n  while read -r line || [[ -n \"$line\" ]] ; do\n      if [[ $line =~ $pat ]]; then\n          #echo \"MATCHED $line\"\n          k=${BASH_REMATCH[1]}\n          v=${BASH_REMATCH[2]}\n          case $k in\n            FORGEURL)\n              forgeInstallerUrl=\"$v\"\n              ;;\n          esac\n      fi\n  done < \"$cfgFile\"\n}\n\nisDebugging && set -x\n\nif [[ ${VERSION^^} == LATEST ]]; then\n  logWarning \"For the old TYPE=CURSEFORGE mechanism it is best to set VERSION to a specific value\"\nfi\nresolveVersion\n\n: \"${FTB_BASE_DIR:=${CF_BASE_DIR:-/data/FeedTheBeast}}\"\nexport FTB_BASE_DIR\n\nlegacyJavaFixerUrl=https://files.minecraftforge.net/maven/net/minecraftforge/lex/legacyjavafixer/1.0/legacyjavafixer-1.0.jar\nexport TYPE=CURSEFORGE\n\nFTB_SERVER_MOD=${FTB_SERVER_MOD:-$CF_SERVER_MOD}\n\nlog \"Looking for Feed-The-Beast / CurseForge server modpack.\"\nif [[ ! $FTB_SERVER_MOD ]]; then\n    logError \"CF_SERVER_MOD or FTB_SERVER_MOD is required to be set\"\n    exit 1\nfi\n\ndownloadModpack() {\n  srv_modpack=${FTB_SERVER_MOD}\n  if isURL \"${srv_modpack}\"; then\n    log \"Downloading modpack from ${srv_modpack}...\"\n    if ! srv_modpack=$(get -o /data --output-filename --skip-existing \"${srv_modpack}\"); then\n      logError \"Failed to download modpack\"\n      exit 1\n    fi\n  fi\n  if [[ \"${srv_modpack:0:5}\" == \"data/\" ]]; then\n      # Prepend with \"/\"\n      srv_modpack=\"/${srv_modpack}\"\n  fi\n  if [[ ! \"${srv_modpack:0:1}\" == \"/\" ]]; then\n      # If not an absolute path, assume file is in \"/data\"\n      srv_modpack=/data/${srv_modpack}\n  fi\n  if [[ ! -f \"${srv_modpack}\" ]]; then\n      logError \"FTB server modpack: ${srv_modpack} not found.\"\n      exit 2\n  fi\n  if [[ ! \"${srv_modpack: -4}\" == \".zip\" ]]; then\n      logError \"FTB server modpack: ${srv_modpack} is not a zip archive.\"\n      logError \"Please set FTB_SERVER_MOD to a file with a .zip extension.\"\n      exit 2\n  fi\n  FTB_SERVER_MOD=${srv_modpack}\n}\n\nif ! isTrue \"${USE_MODPACK_START_SCRIPT:-true}\"; then\n  downloadModpack\n\n  needsInstall=true\n  installMarker=/data/.curseforge-installed\n  if [ -f $installMarker ]; then\n    if [ \"$(cat $installMarker)\" != \"${FTB_SERVER_MOD}\" ]; then\n      log \"Upgrading modpack\"\n\n      serverJar=$(find \"${FTB_BASE_DIR}\" -type f \\( -path \"*/libraries/*\" -o -path \"*/mods/*\" \\) -prune -o -name \"forge*.jar\" -not -name \"forge*installer.jar\" -print)\n      if [[ \"${serverJar}\" ]]; then\n        rm -rf \"$(dirname \"${serverJar}\")\"/{mods,*.jar,libraries,resources,scripts,config}\n      fi\n    else\n      needsInstall=false\n    fi\n  fi\n\n  if $needsInstall; then\n    log \"Unpacking FTB server modpack ${FTB_SERVER_MOD} ...\"\n    mkdir -p \"${FTB_BASE_DIR}\"\n    unzip -o \"${FTB_SERVER_MOD}\" -d \"${FTB_BASE_DIR}\" | awk '{printf \".\"} END {print \"\"}'\n\n    serverJar=$(find \"${FTB_BASE_DIR}\" -type f \\( -path \"*/libraries/*\" -o -path \"*/mods/*\" \\) -prune -o -name \"forge*.jar\" -not -name \"forge*installer.jar\" -print)\n    if [[ -z \"$serverJar\" ]]; then\n\n      if [ -f \"${FTB_BASE_DIR}/settings.cfg\" ]; then\n        loadForgeVars \"${FTB_BASE_DIR}/settings.cfg\"\n\n        if [[ $forgeInstallerUrl ]]; then\n          forgeInstallerJar=\"${FTB_BASE_DIR}/forge-installer.jar\"\n          if ! curl -fsSL -o \"$forgeInstallerJar\" \"$forgeInstallerUrl\" ; then\n            logError \"Failed to download Forge installer from $forgeInstallerUrl\"\n            exit 2\n          fi\n        fi\n      else\n        forgeInstallerJar=$(find \"${FTB_BASE_DIR}\" -name \"forge*installer.jar\")\n      fi\n\n      if [[ -z \"${forgeInstallerJar}\" ]]; then\n        logError \"Unable to find forge installer in modpack\"\n        logError \"      or download using modpack config.\"\n        logError \"      Make sure you downloaded the server files.\"\n        exit 2\n      fi\n\n      log \"Installing forge server\"\n      dirOfInstaller=$(dirname \"${forgeInstallerJar}\")\n      (cd \"${dirOfInstaller}\"; java -jar \"$(basename \"${forgeInstallerJar}\")\" --installServer)\n    fi\n\n    echo \"${FTB_SERVER_MOD}\" > $installMarker\n  fi\n\n  SERVER=$(find \"${FTB_BASE_DIR}\" -maxdepth 2 -type f \\( -path \"/libraries/*\" -o -path \"/mods/*\" \\) -prune -o -name \"forge*.jar\" -not -name \"forge*installer.jar\" -print)\n  if [[ -z \"${SERVER}\" || ! -f \"${SERVER}\" ]]; then\n    logError \"Unable to locate installed forge server jar\"\n    isDebugging && find \"${FTB_BASE_DIR}\" -name \"forge*.jar\"\n    exit 2\n  fi\n  export SERVER\n\n  FTB_DIR=$(dirname \"${SERVER}\")\n  export FTB_DIR\n\n  exec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\nfi\n\n\nfindStartScript() {\n  entryScriptExpr=(\n    -name ServerStart.sh\n    -o -name serverstart.sh\n    -o -name ServerStartLinux.sh\n    -o -name LaunchServer.sh\n    -o -name server-start.sh\n    -o -name SERVER-START.sh\n    -o -name start-server.sh\n    -o -name startserver.sh\n    -o -name StartServer.sh\n    -o -name run.sh\n    -o -name start.sh\n    -o -name launch.sh\n  )\n\n  if [ -d \"${FTB_BASE_DIR}\" ]; then\n    find \"${FTB_BASE_DIR}\" \\( \"${entryScriptExpr[@]}\" \\) -print -quit\n  fi\n}\n\nstartScript=$(findStartScript)\n\n# only download and install if a mod pack isn't already installed\n# also check for the start script rather than just the folder\n# this allows saving just the world separate from the rest of the data directory\nif [[ ! $startScript ]]; then\n  downloadModpack\n  srv_modpack=${FTB_SERVER_MOD}\n  \n  log \"Unpacking FTB server modpack ${srv_modpack} ...\"\n  mkdir -p \"${FTB_BASE_DIR}\"\n  unzip -o \"${srv_modpack}\" -d \"${FTB_BASE_DIR}\" | awk '{printf \".\"} END {print \"\"}'\n\n  installScriptExpr=(\n    -name install.sh\n    -o -name FTBInstall.sh\n    -o -name Install.sh\n  )\n\n  installScript=$(find \"${FTB_BASE_DIR}\" -maxdepth 2 -type f \\( \"${installScriptExpr[@]}\" \\) -print -quit)\n  if [[ \"$installScript\" ]]; then\n    (\n      cd \"$(dirname \"${installScript}\")\"\n      chmod +x \"${installScript}\"\n      log \"Running included $(basename \"${installScript}\"). This might take a minute or two...\"\n      \"${installScript}\" > install.log\n    )\n  fi\n\n  startScript=$(findStartScript)\nfi\n\n# start script provided by unzipped+installed modpack?\nif [[ ! $startScript ]]; then\n  # no, then look for a forge jar to run\n\n  # Allow up to 2 levels since some modpacks have a top-level directory named for the modpack\n  forgeJar=$(find \"${FTB_BASE_DIR}\" -maxdepth 2 -type f \\( -path \"/libraries/*\" -o -path \"/mods/*\" \\) -prune -o -name \"forge*.jar\" -not -name \"forge*installer.jar\" -print)\n  if [[ \"$forgeJar\" ]]; then\n    FTB_BASE_DIR=$(dirname \"${forgeJar}\")\n    export FTB_BASE_DIR\n    log \"No entry script found, so building one for ${forgeJar}\"\n    cat > \"${FTB_BASE_DIR}/ServerStart.sh\" <<EOF\n#!/bin/sh\n. ./settings-local.sh\njava \\${JAVA_PARAMETERS} -Xmx\\${MAX_RAM} -jar $(basename \"${forgeJar}\") nogui\nEOF\n    startScript=\"${FTB_BASE_DIR}/ServerStart.sh\"\n    chmod +x \"$startScript\"\n  else\n    logError \"Modpack missing start script and unable to find Forge jar to generate one\"\n    exit 2\n  fi\nfi\n\n# Modpacks that use https://github.com/BloodyMods/ServerStarter will sometimes specify an\n# extra subpath where all the server files get installed. Need to transplant EULA file there.\nserverSetupConfig=$(find \"${FTB_BASE_DIR}\" -type f -name server-setup-config.yaml)\nif [[ $serverSetupConfig && $serverSetupConfig != \"~\" ]]; then\n  if baseInstallPath=$(mc-image-helper yaml-path --file \"$serverSetupConfig\" \".install.baseInstallPath\"); then\n    resolvedBaseInstallPath=\"$(dirname \"$serverSetupConfig\")/${baseInstallPath}\"\n    mkdir -p \"$resolvedBaseInstallPath\"\n\n    cp -n /data/eula.txt \"${resolvedBaseInstallPath}/eula.txt\"\n  fi\nfi\n\nFTB_SERVER_START=\"$startScript\"\nexport FTB_SERVER_START\n\nFTB_DIR=$(dirname \"${FTB_SERVER_START}\")\nexport FTB_DIR\nchmod a+x \"${FTB_SERVER_START}\"\ngrep fml.queryResult=confirm \"${FTB_SERVER_START}\" > /dev/null || \\\n    sed -i 's/-jar/-Dfml.queryResult=confirm -jar/' \"${FTB_SERVER_START}\"\nsed -i 's/.*read.*Restart now/#\\0/' \"${FTB_SERVER_START}\"\nlegacyJavaFixerPath=\"${FTB_DIR}/mods/legacyjavafixer.jar\"\n\nif isTrue \"${FTB_LEGACYJAVAFIXER}\" && [ ! -e \"${legacyJavaFixerPath}\" ]; then\n  log \"Installing legacy java fixer to ${legacyJavaFixerPath}\"\n  if ! get -o \"${legacyJavaFixerPath}\" ${legacyJavaFixerUrl}; then\n    logError \"Failed to download legacy java fixer from ${legacyJavaFixerUrl}\"\n    exit 1\n  fi\nfi\n\nexport FAMILY=FORGE\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployCanyon",
    "content": "#!/bin/bash\nset -euo pipefail\nIFS=$'\\n\\t'\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\n\n: \"${CANYON_BUILD:=lastSuccessfulBuild}\"\n\nif [ \"${VERSION}\" != \"b1.7.3\" ]; then\n  logError \"Canyon server type only supports VERSION=b1.7.3\"\n  exit 1\nfi\n\ncanyonJob=\"https://canyonmodded.p0rtal.gay/job/Canyonmodded\"\ngithubUrl=\"https://github.com/KoboDev/SupplyAndDemand/releases/download\"\ncanyonBuildJSON=$(curl -fsSL \"${canyonJob}/${CANYON_BUILD}/api/json\")\n\nbuildRelPath=$(\n    jq -r '.artifacts[0].relativePath' <<< \"$canyonBuildJSON\"\n) \nbuildNumber=$(\n    jq -r '.number'<<< \"$canyonBuildJSON\"\n)\nbaseName=$(basename \"${buildRelPath}\")\n\n# Add build tag to non-tagged builds for version detection\nif [ ${baseName%.*} = 'canyon-server' ]; then\n    export SERVER=${baseName%.*}_build${buildNumber}.jar\nelse\n    export SERVER=\"${baseName}\"\nfi\n\nif [ ! -f \"$SERVER\" ]; then\n    # If CANYON_BUILD is old artifact, then download from GitHub\n    if [ \"${CANYON_BUILD}\" = \"final\" ]; then\n        downloadUrl=\"${githubUrl}/finalcanyon/canyon-server.jar\"\n        log \"Downloading final Canyon build from $downloadUrl ...\"\n    elif [ \"${CANYON_BUILD}\" = \"6\" ]; then\n        downloadUrl=\"${githubUrl}/CanyonRelease/Canyon-Build-6.jar\"\n        log \"Downloading Canyon build 6 from $downloadUrl ...\"\n    elif [ \"${CANYON_BUILD}\" = \"26\" ]; then\n        downloadUrl=\"${githubUrl}/CanyonRelease/canyon-build-26.jar\"\n        log \"Downloading Canyon build 26 from $downloadUrl ...\"\n    # Builds greater than 32 are on Jenkins\n    else\n        downloadUrl=\"${canyonJob}/${CANYON_BUILD}/artifact/${buildRelPath}\"\n        log \"Downloading Canyon build ${buildNumber} from $downloadUrl ...\"\n    fi\n    curl -fsSL -o \"$SERVER\" \"$downloadUrl\"\n    if [ ! -f \"$SERVER\" ]; then\n      logError \"Failed to download from $downloadUrl\"\n      exit 3\n    fi\nfi\n\n# Normalize on Spigot for later operations\nexport FAMILY=SPIGOT\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployCrucible",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"${SCRIPTS:-$(dirname \"$0\")}/start-utils\"\nset -o pipefail\nset -e\nisDebugging && set -x\n\nresolveVersion\n: \"${CRUCIBLE_RELEASE:=latest}\"\n\ncrucibleReleasesUrl=https://api.github.com/repos/CrucibleMC/Crucible/releases\nif [[ ${CRUCIBLE_RELEASE^^} = LATEST ]]; then\n  crucibleReleaseUrl=${crucibleReleasesUrl}/latest\nelse\n  crucibleReleaseUrl=${crucibleReleasesUrl}/tags/${CRUCIBLE_RELEASE}\nfi\n\nif ! downloadUrl=$(get --json-path \"$.assets[?(@.name =~ /Crucible-${VERSION}-.*\\.jar/)].browser_download_url\" \\\n    --accept \"application/vnd.github.v3+json\" \"$crucibleReleaseUrl\"); then\n  logError \"Failed to access ${CRUCIBLE_RELEASE} release of Crucible\"\n  exit 1\nfi\n\nif [[ $downloadUrl = null ]]; then\n  logError \"Failed to locate Crucible jar for $VERSION from ${CRUCIBLE_RELEASE}\"\n  exit 1\nfi\n\nlog \"Downloading Crucible from $downloadUrl\"\nif ! SERVER=$(get --skip-existing --output-filename -o /data \"$downloadUrl\"); then\n  logError \"Crucible jar from $downloadUrl\"\n  exit 1\nfi\n\nlibrariesDir=/data/libraries\nif [ ! -d \"$librariesDir\" ]; then\n  if ! librariesUrl=$(get --json-path \"$.assets[?(@.name == 'libraries.zip')].browser_download_url\" \\\n      --accept \"application/vnd.github.v3+json\" \"$crucibleReleaseUrl\"); then\n    logError \"Failed to access ${CRUCIBLE_RELEASE} release of Crucible for libraries\"\n    exit 1\n  fi\n\n  log \"Downloading Crucible libraries\"\n  if ! get -o /tmp/libraries.zip \"$librariesUrl\"; then\n    logError \"Failed to download Crucible libraries from $librariesUrl\"\n    exit 1\n  fi\n\n  if ! unzip /tmp/libraries.zip -d \"$librariesDir\"; then\n    logError \"Failed to unzip Crucible libraries\"\n    exit 1\n  fi\n  rm /tmp/libraries.zip\nfi\n\nexport SERVER\nexport FAMILY=HYBRID\nexport HYBRIDTYPE=forge\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployCustom",
    "content": "#!/bin/bash\n\n: \"${CUSTOM_SERVER:=}\"\n: \"${GENERIC_PACK:=}\"\n: \"${CUSTOM_JAR_EXEC:=}\"\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\n\nif isURL \"${CUSTOM_SERVER}\"; then\n  filename=$(basename \"${CUSTOM_SERVER}\")\n  export SERVER=/data/${filename}\n\n  if [[ -f ${SERVER} ]] && ! isTrue \"$FORCE_REDOWNLOAD\"; then\n    log \"Using previously downloaded jar at ${SERVER}\"\n  else\n   log \"Downloading custom server jar from ${CUSTOM_SERVER} ...\"\n   if ! curl -sSL -o \"${SERVER}\" \"${CUSTOM_SERVER}\"; then\n      log \"Failed to download from ${CUSTOM_SERVER}\"\n      exit 2\n    fi\n  fi\n\nelif [[ -f ${CUSTOM_SERVER} ]]; then\n  export SERVER=${CUSTOM_SERVER}\n\nelif [[ ${GENERIC_PACK} ]]; then\n  log \"Using custom server jar from generic pack at ${CUSTOM_SERVER} ...\"\n  export SERVER=${CUSTOM_SERVER}\n\nelif [[ ${CUSTOM_JAR_EXEC} ]]; then\n  log \"CUSTOM_JAR_EXEC is in use, so \\$SERVER will not be set\"\n\nelse\n  log \"CUSTOM_SERVER is not properly set to a URL or existing jar file\"\n  exit 2\n\nfi\n\n# Allow for overriding Family on custom for testing.\nexport FAMILY=\"${FAMILY:-HYBRID}\"\nexport HYBRIDTYPE=\"${HYBRIDTYPE:-any}\"\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployFTBA",
    "content": "#!/bin/bash\n\n: \"${FTB_FORCE_REINSTALL:=false}\"\n: \"${FTB_BASE_URL:=https://api.feed-the-beast.com/v1/modpacks}\"\n# Legacy base URL was https://api.modpacks.ch\n\nftbInstallMarker=\".ftb-installed\"\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\nset -e\n\n#\n#{\n#  \"id\": 119,\n#  \"name\": \"FTB Presents Direwolf20 1.20\",\n#  \"versionName\": \"1.16.0\",\n#  \"versionId\": 12252,\n#  \"modPackTargets\": {\n#    \"modLoader\": {\n#      \"name\": \"neoforge\",\n#      \"version\": \"47.1.84\"\n#    },\n#    \"javaVersion\": \"17.0.7+7\",\n#    \"mcVersion\": \"1.20.1\"\n#  },\n\nftbManifest=.manifest.json\nfunction getModLoaderName() {\n  jq -r \".modPackTargets.modLoader.name\" ${ftbManifest}\n}\nfunction getModLoaderVersion() {\n  jq -r \".modPackTargets.modLoader.version\" ${ftbManifest}\n}\nfunction getMinecraftVersion() {\n  jq -r \".modPackTargets.mcVersion\" ${ftbManifest}\n}\nfunction getModpackName() {\n  jq -r \".name\" ${ftbManifest}\n}\nfunction getModpackVersion() {\n  jq -r \".versionName\" ${ftbManifest}\n}\n\nif [[ $(getDistro) = alpine ]]; then\n  logError \"The FTBA installer is not supported on Alpine. Use the java8-multiarch image tag instead.\"\n  exit 1\nfi\n\nif ! [[ -v FTB_MODPACK_ID ]]; then\n  logError \"FTB_MODPACK_ID is required with TYPE=FTB\"\n  exit 1\nfi\n\nif ! [[ ${FTB_MODPACK_ID} =~ [0-9]+ ]]; then\n  logError \"FTB_MODPACK_ID needs to be numeric\"\n  exit 1\nfi\n\nif [[ ! $FTB_MODPACK_VERSION_ID ]]; then\n  if ! FTB_MODPACK_VERSION_ID=$(curl -fsSL \"${FTB_BASE_URL}/public/modpack/${FTB_MODPACK_ID}\" | jq -r '.versions | sort_by(.updated)[-1].id'); then\n    logError \"Unable to resolve latest modpack version ID for modpack ${FTB_MODPACK_ID}\"\n    exit 1\n  fi\nelif ! [[ ${FTB_MODPACK_VERSION_ID} =~ [0-9]+ ]]; then\n  logError \"FTB_MODPACK_VERSION_ID needs to be numeric\"\n  exit 1\nfi\n\nif isTrue \"$FTB_FORCE_REINSTALL\" ||\n  ! [ -f \"${ftbManifest}\" ] ||\n  ! [ -f \"${ftbInstallMarker}\" ] ||\n  [ \"$(cat \"${ftbInstallMarker}\")\" != \"${FTB_MODPACK_ID}=${FTB_MODPACK_VERSION_ID}\" ]; then\n  ftbInstaller=/data/ftb-installer-v2\n  arm=\n  if ! [[ -f \"${ftbInstaller}\" ]]; then\n    if [ \"$(uname -m)\" == \"aarch64\" ]; then\n      log \"Downloading FTB installer for ARM\"\n      arm=\"/arm\"\n    else\n      log \"Downloading FTB installer for x86\"\n    fi\n    # Example: https://api.feed-the-beast.com/v1/modpacks/public/modpack/119/12252/server/linux\n    #          https://api.feed-the-beast.com/v1/modpacks/public/modpack/119/12252/server/arm/linux\n    # 1-1 is a placeholder modpack for just grabbing the installer\n    if ! get -o \"${ftbInstaller}\" \"${FTB_BASE_URL}/public/modpack/1/1/server${arm}/linux\"; then\n      logError \"Failed to download FTB installer\"\n      exit 1\n    fi\n    chmod +x \"${ftbInstaller}\"\n  fi\n\n  log \"Installing modpack ID ${FTB_MODPACK_ID}, version ID ${FTB_MODPACK_VERSION_ID}\"\n  log \"This could take a while...\"\n  ${ftbInstaller} -pack \"${FTB_MODPACK_ID}\" -version \"${FTB_MODPACK_VERSION_ID}\" -auto -force -no-java | tee ftb-installer.log\n  rm -f forge*installer.jar\n\n  echo \"${FTB_MODPACK_ID}=${FTB_MODPACK_VERSION_ID}\" > ${ftbInstallMarker}\n\n  writeEula\n\nelse\n  log \"FTB modpack ID ${FTB_MODPACK_ID}, version ID ${FTB_MODPACK_VERSION_ID} is ready to go\"\nfi\n\nmodLoader=\"$(getModLoaderName)\"\nmodLoaderVersion=\"$(getModLoaderVersion)\"\nmcVersion=$(getMinecraftVersion)\nVERSION=\"$mcVersion\"\nMODPACK_NAME=$(getModpackName)\nMODPACK_VERSION=$(getModpackVersion)\nexport VERSION MODPACK_NAME MODPACK_VERSION\n\nvariants=(\n  run.sh\n  fabric-server-launch.jar\n  \"${modLoader}-${mcVersion}-${modLoaderVersion}-universal.jar\"\n  \"${modLoader}-${mcVersion}-${modLoaderVersion}-${mcVersion}-universal.jar\"\n  \"${modLoader}-${mcVersion}-${modLoaderVersion}-server-launch.jar\"\n  \"${modLoader}-${mcVersion}-${modLoaderVersion}.jar\"\n)\nfor f in \"${variants[@]}\"; do\n  if [ -f $f ]; then\n    export SERVER=$f\n    break\n  fi\ndone\nif ! [ -v SERVER ]; then\n  logError \"Unable to locate the installed FTB server jar\"\n  logError \"      Tried looking for ${variants[*]}\"\n  exit 2\nfi\n\n# Remap the TYPE and FAMILY based on discovered server jar\nif [[ $SERVER = run.sh ]]; then\n  if grep -q neoforge \"$SERVER\"; then\n    export FAMILY=FORGE\n    export TYPE=NEOFORGE\n  elif grep -q forge \"$SERVER\"; then\n    export FAMILY=FORGE\n    export TYPE=FORGE\n  elif grep -q fabric run.s; then\n      export FAMILY=FABRIC\n      export TYPE=FABRIC\n  else\n    logError \"Unrecognized loader type in $SERVER\"\n    cat \"$SERVER\"\n    exit 1\n  fi\n\nelif [[ $SERVER = forge* ]]; then\n  export FAMILY=FORGE\n  export TYPE=FORGE\nelif [[ $SERVER = fabric* ]]; then\n  export FAMILY=FABRIC\n  export TYPE=FABRIC\nelse\n  logError \"Unrecognized loader type from $SERVER\"\n  exit 1\nfi\n\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployFabric",
    "content": "#!/bin/bash\nset -eu\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\nexport TYPE=FABRIC\n: \"${FABRIC_LAUNCHER_VERSION:=${FABRIC_INSTALLER_VERSION:-LATEST}}\"\n: \"${FABRIC_LAUNCHER:=}\"\n: \"${FABRIC_LAUNCHER_URL:=}\"\n: \"${FABRIC_LOADER_VERSION:=LATEST}\"\n: \"${FABRIC_FORCE_REINSTALL:=false}\"\n\nresultsFile=/data/.install-fabric.env\n\nisDebugging && set -x\n\ncommonArgs=(\n  --results-file=\"${resultsFile}\"\n)\nif isTrue \"${FABRIC_FORCE_REINSTALL}\"; then\n  log \"Forcing re-install of Fabric\"\n  commonArgs+=(--force-reinstall)\nfi\n\n# Custom fabric jar\nif [[ $FABRIC_LAUNCHER ]]; then\n  if !   mc-image-helper install-fabric-loader \\\n    \"${commonArgs[@]}\" \\\n    --from-local-file=\"$FABRIC_LAUNCHER\"; then\n    logError \"Failed to use provided Fabric launcher\"\n    exit 1\n  fi\n\n# Custom fabric jar url\nelif [[ $FABRIC_LAUNCHER_URL ]]; then\n  if !   mc-image-helper install-fabric-loader \\\n    \"${commonArgs[@]}\" \\\n    --from-url=\"$FABRIC_LAUNCHER_URL\"; then\n    logError \"Failed to installFabric launcher from $FABRIC_LAUNCHER_URL\"\n    exit 1\n  fi\n\n# Official fabric launcher\nelse\n  if !   mc-image-helper install-fabric-loader \\\n    \"${commonArgs[@]}\" \\\n    --minecraft-version=\"${VERSION}\" \\\n    --installer-version=\"${FABRIC_LAUNCHER_VERSION}\" \\\n    --loader-version=\"${FABRIC_LOADER_VERSION}\"; then\n    logError \"Failed to installFabric launcher given $VERSION, $FABRIC_LAUNCHER_VERSION, $FABRIC_LOADER_VERSION\"\n    exit 1\n  fi\nfi\n\napplyResultsFile ${resultsFile}\n\nexport FAMILY=FABRIC\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployFolia",
    "content": "#!/bin/bash\n\n: \"${FOLIA_CHANNEL:=default}\"\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nset -o pipefail\nhandleDebugMode\n\nif [[ $FOLIA_DOWNLOAD_URL ]]; then\n  export PAPER_DOWNLOAD_URL=\"$FOLIA_DOWNLOAD_URL\"\nfi\n\nif [[ $FOLIABUILD ]]; then\n  export PAPERBUILD=\"$FOLIABUILD\"\nfi\n\nPAPER_PROJECT=\"folia\" \\\nPAPER_NAME=\"FoliaMC\" \\\nPAPER_CHANNEL=\"${FOLIA_CHANNEL}\" \\\n  exec \"$(dirname \"$0\")/start-deployPaper\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployForge",
    "content": "#!/bin/bash\n\n: \"${FORGE_VERSION:=${FORGEVERSION:-RECOMMENDED}}\"\n: \"${FORGE_FORCE_REINSTALL:=false}}\"\n\n# shellcheck source=start-utils\n. \"${SCRIPTS:-$(dirname \"$0\")}/start-utils\"\nisDebugging && set -x\nresultsFile=/data/.run-forge.env\n\nfunction mc-image-helper-forge() {\n  mc-image-helper install-forge \\\n    --output-directory=/data \\\n    --results-file=\"${resultsFile}\" \\\n    --minecraft-version=\"${VERSION}\" \\\n    --force-reinstall=\"${FORGE_FORCE_REINSTALL}\" \"$@\"\n}\n\nif [[ ${FORGE_INSTALLER} ]]; then\n  if ! mc-image-helper-forge --forge-installer=\"${FORGE_INSTALLER}\" ; then\n      logError \"Failed to installForge given installer ${FORGE_INSTALLER}\"\n      exit 1\n  fi\nelif [[ ${FORGE_INSTALLER_URL:-} ]]; then\n  mkdir -p tmp\n  if ! installer=$(get -o tmp --output-filename \"${FORGE_INSTALLER_URL}\"); then\n    logError \"Failed to download installer from $FORGE_INSTALLER_URL\"\n    exit 1\n  fi\n\n  # shellcheck disable=SC2064\n  trap \"rm $installer\" EXIT\n\n  if ! mc-image-helper-forge --forge-installer=\"${installer}\" ; then\n      logError \"Failed to install forge from ${FORGE_INSTALLER_URL}\"\n      exit 1\n  fi\nelse\n  if ! mc-image-helper-forge --forge-version=\"${FORGE_VERSION}\"; then\n      logError \"Failed to install Forge\"\n      exit 1\n  fi\nfi\n\napplyResultsFile ${resultsFile}\n\nexport FAMILY=FORGE\n\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployGTNH",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\n# Define setup functions\nfunction getGTNHdownloadPath(){\n  gtnh_download_path=\"\"\n  current_java_version=$(mc-image-helper java-release)\n    \n  if ! packs_data=\"$(\n    curl -fsSL \"https://downloads.gtnewhorizons.com/versions.json\" \\\n      | jq -r '.versions[]?.server? | .[]? | select(type==\"string\" and test(\"Server\"))'\n  )\"; then\n    logError \"Failed to retrieve data from https://downloads.gtnewhorizons.com/versions.json\"\n    exit 1\n  fi\n  mapfile -t packs <<< \"$packs_data\"\n\n  log \"Start locating server files...\"\n  for pack in \"${packs[@]}\"; do\n    # Extract the Java version(s) from the pack filename\n    if ! pack_java_version=$(basename \"$pack\" | grep -Eo 'Java_[0-9]+(-[0-9]+)?' | sed 's/Java_//'); then\n    logWarning \"Could not parse java version of $pack\"\n    fi\n  \n    # Skip the pack if the current Java version is not compatible\n    if [[ \"$pack_java_version\" == *-* ]]; then\n      # Handle range of Java versions (e.g., \"17-21\")\n      java_min_version=$(echo \"$pack_java_version\" | cut -d'-' -f1)\n      java_max_version=$(echo \"$pack_java_version\" | cut -d'-' -f2)\n      if (( current_java_version < java_min_version || current_java_version > java_max_version )); then\n        debug \"Skipping $pack due to incompatible Java version: $current_java_version not in range $java_min_version-$java_max_version\"\n        continue\n      fi\n    else\n      # Handle single Java version (e.g., \"8\")\n      if (( current_java_version != pack_java_version )); then\n        debug \"Skipping $pack due to incompatible Java version: $current_java_version != $pack_java_version\"\n        continue\n      fi\n    fi\n  \n    # Extract version numbers and release type (beta or RC) from the file names\n        \n    if ! pack_version=$(basename \"$pack\" | grep -Eo '[0-9]+(\\.[0-9]+)+'); then\n    logWarning \"Could not parse version of $pack\"\n    fi\n    if ! pack_release_type=$(basename \"$pack\" | grep -Eo '(beta|RC)(-[0-9]+)?' || echo \"\"); then\n    logWarning \"Could not parse release type of $pack\"\n    fi\n    if ! current_version=$(basename \"$gtnh_download_path\" | grep -Eo '[0-9]+(\\.[0-9]+)+'); then\n    debug \"Could not parse version of selected download path. String might be empty.\"\n    fi\n    if ! current_release_type=$(basename \"$gtnh_download_path\" | grep -Eo '(beta|RC)(-[0-9]+)?' || echo \"\"); then\n    debug \"Could not parse release type of selected download path. String might be empty.\"\n    fi\n    # Check if the pack matches the desired type based on GTNH_PACK_VERSION:\n    # - If GTNH_PACK_VERSION is \"latest-dev\", only consider beta packs (path contains \"/betas/\").\n    # - If GTNH_PACK_VERSION is \"latest\", only consider non-beta packs (path does not contain \"/betas/\").\n    if [[ ($pack == *\"/betas/\"* && $GTNH_PACK_VERSION == \"latest-dev\") || ($pack != *\"/betas/\"* && $GTNH_PACK_VERSION == \"latest\") ]]; then\n      # Compare versions and update gtnh_download_path if pack is newer\n      # Check if the current version is unset or if the pack version is newer than the current version.\n      # This comparison uses version sorting to determine the latest version.\n      if [[ -z \"$current_version\" || \"$(printf '%s\\n' \"$pack_version\" \"$current_version\" | sort -V | tail -n 1)\" == \"$pack_version\" ]]; then\n        \n        # If the pack version is the same as the current version, prioritize based on release type.\n        # Full versions are preferred over RC (Release Candidate), and RC is preferred over beta.\n        # Within the same release type, higher numbered versions are preferred.\n        if [[ \"$pack_version\" == \"$current_version\" ]]; then\n        if [[ -z \"$pack_release_type\" || (\"$pack_release_type\" == \"RC\" && \"$current_release_type\" == \"beta\") || \n            (\"$pack_release_type\" == \"$current_release_type\" && \"$(printf '%s\\n' \"$pack_release_type\" \"$current_release_type\" | sort -V | tail -n 1)\" == \"$pack_release_type\") ]]; then\n          debug \"$current_version-$current_release_type is older than $pack_version-$pack_release_type! Update latest version to: $pack_version-$pack_release_type\"\n          gtnh_download_path=\"$pack\"\n        fi\n        else\n        # If the pack version is newer than the current version, set it as the download path.\n        debug \"$current_version is older than $pack_version! Update latest version to: $pack_version\"\n        gtnh_download_path=\"$pack\"\n        fi\n      fi\n    else\n      if [[ \"$pack_version\" == \"$GTNH_PACK_VERSION\" || \"$pack_version-$pack_release_type\" == \"$GTNH_PACK_VERSION\" ]]; then\n        log \"Found exact match $pack_version = $GTNH_PACK_VERSION! Select $pack_version for download.\"\n        gtnh_download_path=\"$pack\"\n        break\n      fi\n    fi\n  done\n}\n\nfunction deleteGTNHbackup(){\n  log \"Start deleting all config backups\"\n  if ! find . -maxdepth 1 -type d -name 'gtnh-upgrade-*' -exec rm -rf {} + ; then\n    logWarning \"Can not delete config backup!\"\n  fi\n}\n\nfunction updateGTNH(){\n  # Get the current date and time\n  current_datetime=$(date +%Y-%m-%dT%H:%M)\n  \n  # Define folders and files to update\n  folders_to_update=(\"libraries\" \"mods\" \"resources\" \"scripts\")\n  files_to_update=(\"lwjgl3ify-forgePatches.jar\" \"java9args.txt\" \"startserver-java9.bat\" \"startserver-java9.sh\" \"forge-1.7.10-10.13.4.1614-1.7.10-universal.jar\" \"startserver.bat\" \"startserver.sh\" \"server-icon.png\")\n  config_folder=\"config\"\n  backup_folder=\"/data/gtnh-upgrade-${current_version}${current_release_type:+-$current_release_type}-$current_datetime\"\n  journey_map_folder=\"JourneyMapServer\"\n\n  # Delete specified folders if they exist\n  for folder in \"${folders_to_update[@]}\"; do\n      folder_path=\"/data/$folder\"\n      if [[ -d \"$folder_path\" ]]; then\n          log \"Deleting folder: $folder_path\"\n          rm -rf \"$folder_path\"\n      fi\n  done\n\n  # Delete specific files if they exist\n  for file in \"${files_to_update[@]}\"; do\n      file_path=\"/data/$file\"\n      if [[ -f \"$file_path\" ]]; then\n          log \"Deleting file: $file_path\"\n          rm -f \"$file_path\"\n      fi\n  done\n\n  # Backup the config folder\n  if [[ -d \"/data/$config_folder\" ]]; then\n      log \"Creating backup of /data/$config_folder at $backup_folder\"\n      cp -r \"/data/$config_folder\" \"$backup_folder\"\n      log \"Deleting original /data/$config_folder\"\n      rm -rf \"/data/$config_folder\"\n  fi\n\n  # Updating the required folders in data directory\n  for folder in \"${folders_to_update[@]}\" \"$config_folder\"; do\n    if [[ -d \"$base_dir/$folder\" ]]; then\n      log \"Copying $folder to /data\"\n      cp -r \"$base_dir/$folder\" \"/data/\"\n    else\n      logWarning \"Folder $folder not found in the unzipped data!\"\n    fi\n  done\n        \n  # Copy specific files to the /data directory\n  for file in \"${files_to_update[@]}\"; do\n    if [[ -f \"$base_dir/$file\" ]]; then\n      log \"Copying $file to /data\"\n      cp \"$base_dir/$file\" \"/data/\"\n    else\n      logWarning \"File $file not found in the unzipped data!\"\n    fi\n  done\n\n  # Ensure the config folder exists\n  if [[ ! -d \"$config_folder\" ]]; then\n      log \"$config_folder does not exist. Creating it now.\"\n      mkdir -p \"$config_folder\"\n  fi\n\n  # Restore JourneyMapServer folder from backup\n  if [[ -d \"$backup_folder/$journey_map_folder\" ]]; then\n      log \"Restoring $journey_map_folder to $config_folder\"\n      cp -r \"$backup_folder/$journey_map_folder\" \"$config_folder/\"\n  else\n      logWarning \"$journey_map_folder not found in backup!\"\n  fi\n\n  # Copy the changelog file to /data\n  gtnh_changelog_file=$(mc-image-helper find --max-depth=1 --type=file --name=changelog*.md \"$base_dir\")\n  if [[ -n \"$gtnh_changelog_file\" ]]; then\n    log \"Copying changelog file to /data\"\n    cp -f \"$gtnh_changelog_file\" /data/\n  fi\n}\n\nfunction handleGTNH() {\n\n  : \"${GTNH_PACK_VERSION:=latest}\"\n  : \"${GTNH_DELETE_BACKUPS:=false}\"\n  : \"${SKIP_GTNH_UPDATE_CHECK:=false}\"\n  debug \"GTNH VAR CHECK: GTNH_DELETE_BACKUPS=$GTNH_DELETE_BACKUPS, GTNH_PACK_VERSION=$GTNH_PACK_VERSION, TYPE=$TYPE, SKIP_GTNH_UPDATE_CHECK=$SKIP_GTNH_UPDATE_CHECK\"\n\n  if isTrue \"$GTNH_DELETE_BACKUPS\"; then\n    deleteGTNHbackup\n  fi\n\n  if [[ -n $GTNH_PACK_VERSION ]] && isFalse \"$SKIP_GTNH_UPDATE_CHECK\" ; then\n    \n    getGTNHdownloadPath\n    \n    if [[ -z $gtnh_download_path ]]; then\n    logError \"Server files not found for GTNH_PACK_VERSION=$GTNH_PACK_VERSION! Download not possible!\"\n    exit 1\n    fi\n    log \"Server files located! Will proceed update $gtnh_download_path.\"\n\n    # Decide if update or install is needed or not.\n    if [[ ! -f /data/.gtnh-version || \"$(basename \"$gtnh_download_path\")\" != \"$(cat /data/.gtnh-version)\" ]]; then\n      log \"Update/Install required: /data/.gtnh-version is missing or does not match the selected version $(basename \"$gtnh_download_path\").\"\n\n      mkdir -p /data/packs\n      log \"Downloading $gtnh_download_path.\"\n      if ! gtnh_download=$(mc-image-helper get -o /data/packs --output-filename --skip-up-to-date \"$gtnh_download_path\"); then\n        logError \"Failed to download $gtnh_download_path\"\n        exit 1\n      fi\n      \n      # Unpacking Server files into temporary directory\n      log \"Unpacking Server Files...\"\n      original_base_dir=/data/.tmp/gtnh_base\n      base_dir=$original_base_dir\n      rm -rf \"${base_dir}\"\n      mkdir -p \"${base_dir}\"\n      extract \"${gtnh_download}\" \"${base_dir}\"\n      trap 'rm -rf /data/.tmp' EXIT\n      # Removing downloaded zip\n      rm -f \"$gtnh_download\"\n\n      # Remove any eula file since container manages it\n      rm -f \"${base_dir}/eula.txt\"\n\n      # recalculate the actual base directory of content\n      if ! base_dir=$(mc-image-helper find \\\n          --max-depth=3 --type=directory --name=mods,config \\\n          --only-shallowest --fail-no-matches --format '%h' \\\n          \"$base_dir\"); then\n        logError \"Unable to find content base of downloaded Server Files\"\n        exit 1\n      fi\n\n      # Split installation from update path. Check for version file.\n      if [[ -f /data/.gtnh-version ]]; then\n        log \".gtnh-version file detected! Assuming old version already exists. Proceed updating existing server...\"\n        updateGTNH\n      else\n        log \"No .gtnh-version file detected! Assuming no old server exists. Proceed installing new server...\"\n        cp -R -f \"${base_dir}\"/* /data\n      fi\n      # Update .gtnh-version\n      basename \"$gtnh_download_path\" > /data/.gtnh-version\n      # Cleaning up\n      rm -rf \"$original_base_dir\"\n    \n    else\n      log \"No update required: /data/.gtnh-version matches the selected version $(basename \"$gtnh_download_path\").\"\n    fi\n  else\n    log \"SKIP_GTNH_UPDATE_CHECK=$SKIP_GTNH_UPDATE_CHECK ... Skipping GTNH Update/Install\"\n  fi\n}\n\n# Set server.properties defaults suitable for gtnh servers\nlog \"Applying GTNH optimized server defaults\"\nexport ALLOW_FLIGHT=\"${ALLOW_FLIGHT:=true}\"\nexport LEVEL_TYPE=\"${LEVEL_TYPE:=rwg}\"\nexport DIFFICULTY=\"${DIFFICULTY:=3}\"\nexport ENABLE_COMMAND_BLOCK=\"${ENABLE_COMMAND_BLOCK:=true}\"\nexport MOTD=\"${MOTD:=\"Greg Tech New Horizons $GTNH_PACK_VERSION\"}\"\ndebug \"Set MOTD=$MOTD, ENABLE_COMMAND_BLOCK=$ENABLE_COMMAND_BLOCK, DIFFICULTY=$DIFFICULTY, LEVEL_TYPE=$LEVEL_TYPE, ALLOW_FLIGHT=$ALLOW_FLIGHT\"\n\nisDebugging && set -x\n\nensureRemoveAllModsOff \"MODPACK_PLATFORM=GTNH\"\n\njava_version=$(mc-image-helper java-release)\nif (( java_version == 8 )); then\n  export SERVER=/data/forge-1.7.10-10.13.4.1614-1.7.10-universal.jar\nelif (( java_version >= 17 )); then\n  export SERVER=/data/lwjgl3ify-forgePatches.jar\nelse \n  logError \"Greg Tech New Horizons only supports the following Java versions: 8, 17 or later\"\n  exit 1\nfi\n\n  log \"TYPE=GTNH, setting Minecraft version to 1.7.10\"\n  export VERSION=1.7.10\n\n# Start setup gtnh server files\nhandleGTNH\n\nexport USES_MODS=true\n\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployKetting",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\n\nif ! SERVER=$(mc-image-helper github download-latest-asset \\\n  --output-directory=/data \\\n  --name-pattern=\"kettinglauncher-.+?(?<!-sources)\\.jar\" \\\n  kettingpowered/kettinglauncher\n  ); then\n    logError \"Failed to download Ketting launcher\"\n    exit 1\nfi\n\nexport SERVER\n\nresolveVersion\n\nEXTRA_ARGS+=\" -minecraftVersion $VERSION\"\nif [[ ${KETTING_VERSION:-} ]]; then\n  EXTRA_ARGS+=\" -kettingVersion $KETTING_VERSION\"\nfi\nif [[ ${FORGE_VERSION:-} ]]; then\n  EXTRA_ARGS+=\" -forgeVersion $FORGE_VERSION\"\nfi\nexport EXTRA_ARGS\n\nexport SERVER\nexport FAMILY=HYBRID\nexport HYBRIDTYPE=forge\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployLeaf",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"${SCRIPTS:-$(dirname \"$0\")}/start-utils\"\nset -o pipefail\nset -e\nisDebugging && set -x\n\nresolveVersion\n: \"${LEAF_BUILD:=LATEST}\"\n\n# Docs at https://api.leafmc.one/docs/swagger-ui/index.html\nleafApiUrl=\"https://api.leafmc.one/v2/projects/leaf\"\nleafDownloadsPage=\"https://www.leafmc.one/download\"\n\nif ! get --exists \"${leafApiUrl}/versions/${VERSION}/builds\"; then\n  logError \"Leaf builds do not exist for ${VERSION}\"\n  logError \"       check ${leafDownloadsPage} for available versions\"\n  logError \"       and set VERSION accordingly\"\n  exit 1\nfi\n\nif [[ \"${LEAF_BUILD^^}\" == \"LATEST\" ]]; then\n  # Get the latest build number from the API, which will be the last object in the builds array\n  if ! buildNumber=$(\n    get --json-path '$.builds[-1].build' \"${leafApiUrl}/versions/${VERSION}/builds\"\n  ); then\n    logError \"failed to list Leaf builds for ${VERSION}\"\n    exit 1\n  fi\n  LEAF_BUILD=\"${buildNumber}\"\nfi\n\nif ! filename=$(\n  get --json-path='$.downloads.primary.name' --json-value-when-missing=\"\" \"${leafApiUrl}/versions/${VERSION}/builds/${LEAF_BUILD}\"\n); then\n  logError \"Failed to retrieve download filename\"\n  exit 1\nfi\n\nSERVER=\"/data/$filename\"\n\nif ! get --skip-existing --log-progress-each -o \"${SERVER}\" \"${leafApiUrl}/versions/${VERSION}/builds/${LEAF_BUILD}/downloads/${filename}\"; then\n  logError \"Failed to download\"\n  exit 1\nfi\n\nexport FAMILY=SPIGOT\nexport SERVER\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\""
  },
  {
    "path": "scripts/start-deployLimbo",
    "content": "#!/bin/bash\nset -euo pipefail\nIFS=$'\\n\\t'\n\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\n\n: \"${LIMBO_BUILD:=LATEST}\"\n: \"${FORCE_REDOWNLOAD:=false}\"\n: \"${LIMBO_SCHEMA_FILENAME:=default.schem}\"\n: \"${LEVEL:=default;${LIMBO_SCHEMA_FILENAME}}\"\n# defaults to localhost, if this is not set\n: \"${SERVER_IP:=0.0.0.0}\"\n\nexport LEVEL SERVER_IP\n\nif [[ ${LIMBO_BUILD^^} == LATEST ]]; then\n  LIMBO_BUILD=lastStableBuild\nfi\n\nbaseUrl=\"https://ci.loohpjames.com/job/Limbo/${LIMBO_BUILD}\"\nbuildInfoUrl=\"${baseUrl}/api/json\"\nbuildJson=$(curl -fsSL \"${buildInfoUrl}\")\nif [ $? != 0 ]; then\n  logError \"Failed to get build info from ${buildInfoUrl} (status=$?)\"\n  exit 1\nfi\n\nif [[ ${LIMBO_BUILD} = lastStableBuild ]]; then\n  LIMBO_BUILD=$(jq -r '.number' <<<${buildJson})\n  log \"Resolved latest Limbo build to ${LIMBO_BUILD}\"\nfi\n\ngetRelativePath() {\n  local pattern=$1\n  jq -r --arg pattern \"$pattern\" '.artifacts[] | select(.fileName|test($pattern)) | .relativePath' <<<${buildJson}\n}\n\nartifactPath=$(getRelativePath \"^Limbo-\")\ndefaultSchemaPath=$(getRelativePath \".*\\\\.schem\")\n\nexport SERVER=\"limbo-${LIMBO_BUILD}.jar\"\n\nif [ ! -f \"$SERVER\" ] || isTrue \"$FORCE_REDOWNLOAD\"; then\n    downloadUrl=\"${baseUrl}/artifact/${artifactPath}\"\n    log \"Downloading Limbo from $downloadUrl ...\"\n    if ! curl -fsSL -o \"$SERVER\" \"$downloadUrl\"; then\n      logError \"Failed to download from $downloadUrl (status=$?)\"\n      exit 3\n    fi\n\nfi\n\nif [ ! -f \"${LIMBO_SCHEMA_FILENAME}\" ]; then\n  log \"Downloading default schem file\"\n  if ! curl -o \"${LIMBO_SCHEMA_FILENAME}\" -fsSL \"${baseUrl}/artifact/${defaultSchemaPath}\"; then\n      logError \"Failed to download schema file $baseUrl (status=$?)\"\n      exit 3\n  fi\nfi\n\nif [[ ${LEVEL} != *\\;* ]]; then\n  LEVEL=\"${LEVEL};${LIMBO_SCHEMA_FILENAME}\"\nfi\nexport LEVEL\n\nexport FAMILY=LIMBO\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployMagma",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\n\n: \"${MAGMA_VERSION:=}\"\n\nresolveVersion\n\n# Neo Magma currently supports just 1.21.x\nif [[ ! $VERSION = 1.21.* ]]; then\nlogError \"Magma does not support $VERSION (expected 1.21.x)\"\nexit 1\nfi\n\nif ! SERVER=$(get --output-filename --skip-up-to-date --output /data \"https://magmafoundation.org/api/versions/${MAGMA_VERSION}/download\"); then\n  logError \"Failed to download Magma server jar for $MAGMA_VERSION\"\n  exit 1\nfi\n\nexport SERVER\nexport FAMILY=HYBRID\nexport HYBRIDTYPE=forge\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployMagmaMaintained",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\n\n: \"${FORGE_VERSION:=}\"\n: \"${MAGMA_MAINTAINED_TAG:=}\"\n\nresolveVersion\n\nif [ -z $MAGMA_MAINTAINED_TAG ]; then\n  logError \"The variable MAGMA_MAINTAINED_TAG is not specified\"\n  exit 1\nfi\n\n# Magma-1.12.2/releases/download/88659fb/Magma-1.12.2-88659fb-server.jar\n# Magma-1.20.1/releases/download/adec9ce/magma-1.20.1-47.2.17-adec9ce-server.jar\nif [[ ${VERSION} = \"1.12.2\" ]]; then\n  fileName=\"Magma-${VERSION}-${MAGMA_MAINTAINED_TAG}-server.jar\"\nelse\n  if [ -z $FORGE_VERSION ]; then\n    logError \"The variable FORGE_VERSION is not specified\"\n    exit 1\n  fi\n  fileName=\"magma-${VERSION}-${FORGE_VERSION}-${MAGMA_MAINTAINED_TAG}-server.jar\"\nfi\ndownloadUrl=\"https://github.com/magmamaintained/Magma-${VERSION}/releases/download/${MAGMA_MAINTAINED_TAG}/${fileName}\"\n\nif ! SERVER=$(get --output-filename --skip-up-to-date --output /data \"$downloadUrl\"); then\n  logError \"Failed to download Magma Maintained server jar from $downloadUrl\"\n  exit 1\nfi\n\nexport SERVER\nexport FAMILY=HYBRID\nexport HYBRIDTYPE=forge\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployModrinth",
    "content": "#!/bin/bash\nset -eu\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\nresultsFile=/data/.install-modrinth.env\n\n: \"${MODRINTH_MODPACK:=${MODRINTH_PROJECT:-}}\"\n: \"${MODRINTH_LOADER:=}\"\n: \"${MODRINTH_VERSION:=${MODRINTH_VERSION_ID:-}}\"\n: \"${MODRINTH_IGNORE_MISSING_FILES=}\"\n: \"${MODRINTH_EXCLUDE_FILES=}\"\n: \"${MODRINTH_FORCE_INCLUDE_FILES=}\"\n: \"${MODRINTH_OVERRIDES_EXCLUSIONS=}\"\n: \"${MODRINTH_DEFAULT_EXCLUDE_INCLUDES=/image/modrinth-exclude-include.json}\"\n: \"${MODRINTH_FORCE_MODLOADER_REINSTALL:=${FORCE_MODLOADER_REINSTALL:-false}}\"\n\nif [[ ! $MODRINTH_MODPACK ]]; then\n  logError \"MODRINTH_MODPACK must be set when using TYPE/MODPACK_PLATFORM/MOD_PLATFORM of MODRINTH\"\n  exit 1\nfi\n\nisDebugging && set -x\n\nensureRemoveAllModsOff \"MODPACK_PLATFORM=MODRINTH\"\n\nargs=(\n  --results-file=\"$resultsFile\"\n  --project=\"${MODRINTH_MODPACK}\"\n  --output-directory=/data\n)\n\nif [[ $MODRINTH_IGNORE_MISSING_FILES ]]; then\n  args+=(--ignore-missing-files \"$MODRINTH_IGNORE_MISSING_FILES\")\nfi\nif isTrue \"${MODRINTH_FORCE_MODLOADER_REINSTALL}\"; then\n  args+=(--force-modloader-reinstall)\nfi\n\ncase \"${VERSION^^}\" in\n  LATEST)\n    # need to be backward compatible with old variable name, MODRINTH_DEFAULT_VERSION_TYPE\n    : \"${MODRINTH_MODPACK_VERSION_TYPE:=${MODRINTH_DEFAULT_VERSION_TYPE:-release}}\"\n    ;;\n  SNAPSHOT)\n    : \"${MODRINTH_MODPACK_VERSION_TYPE:=${MODRINTH_DEFAULT_VERSION_TYPE:-beta}}\"\n    ;;\n  *)\n    : \"${MODRINTH_MODPACK_VERSION_TYPE:=${MODRINTH_DEFAULT_VERSION_TYPE:-release}}\"\n    args+=(\"--game-version=$VERSION\")\n    ;;\nesac\n\nsetArg() {\n  arg=\"${1?}\"\n  var=\"${2?}\"\n\n  if [[ ${!var} ]]; then\n      args+=(\"${arg}=${!var}\")\n  fi\n}\nsetArg --loader MODRINTH_LOADER\nsetArg --version MODRINTH_VERSION\nsetArg --default-version-type MODRINTH_MODPACK_VERSION_TYPE\nsetArg --exclude-files MODRINTH_EXCLUDE_FILES\nsetArg --force-include-files MODRINTH_FORCE_INCLUDE_FILES\nsetArg --overrides-exclusions MODRINTH_OVERRIDES_EXCLUSIONS\nsetArg --default-exclude-includes MODRINTH_DEFAULT_EXCLUDE_INCLUDES\n\nif ! mc-image-helper install-modrinth-modpack \"${args[@]}\"; then\n    logError \"Failed to installModrinth modpack\"\n    exit 1\nfi\n\napplyResultsFile ${resultsFile}\nresolveFamily\n\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployMohist",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"${SCRIPTS:-$(dirname \"$0\")}/start-utils\"\nset -o pipefail\nset -e\nisDebugging && set -x\n\nresolveVersion\n: \"${MOHIST_BUILD:=lastSuccessfulBuild}\"\n\n# Docs at https://mohistmc.com/mohistmc-api\nmohistType=\"${TYPE,,}\"\nmohistApiUrl=\"https://api.mohistmc.com/project/${mohistType}\"\nmohistDownloadsPage=\"https://mohistmc.com/downloadSoftware?project=${mohistType}\"\n\nif [[ \"${MOHIST_BUILD}\" != \"lastSuccessfulBuild\" ]] && [[ \"${VERSION,,}\" != latest ]] && [[ -f \"/data/${mohistType}-${VERSION}-${MOHIST_BUILD}-server.jar\" ]]; then\n  log \"Skipping Mohist build lookup since server jar exists\"\n  SERVER=\"/data/${mohistType}-${VERSION}-${MOHIST_BUILD}-server.jar\"\nelse\n  if ! get --exists \"${mohistApiUrl}/${VERSION}/builds\"; then\n    logError \"${mohistType} builds do not exist for ${VERSION}\"\n    logError \"       check ${mohistDownloadsPage} for available versions\"\n    logError \"       and set VERSION accordingly\"\n    exit 1\n  fi\n\n  if [[ \"${MOHIST_BUILD}\" == \"lastSuccessfulBuild\" ]]; then\n    # Get the latest build number from the API\n    if ! buildNumber=$(\n      get --json-path '$[0].id' \"${mohistApiUrl}/${VERSION}/builds\"\n    ); then\n      logError \"failed to list ${mohistType} builds for ${VERSION}\"\n      exit 1\n    fi\n    MOHIST_BUILD=\"${buildNumber}\"\n  fi\n\n  downloadUrl=\"${mohistApiUrl}/${VERSION}/builds/${MOHIST_BUILD}/download\"\n\n  if [[ -z \"${downloadUrl}\" ]]; then\n    logError \"Could not find build ${MOHIST_BUILD} for version ${VERSION}\"\n    logError \"       check ${mohistDownloadsPage} for available versions\"\n    logError \"       and set VERSION accordingly\"\n    exit 1\n  fi\n\n  SERVER=\"/data/${mohistType}-${VERSION}-${MOHIST_BUILD}-server.jar\"\n  get --skip-existing -o \"${SERVER}\" \"${downloadUrl}\"\nfi\n\nif [[ \"${mohistType}\" == \"mohist\" ]]; then\n    export HYBRIDTYPE=forge\nelif [[ \"${mohistType}\" == \"youer\" ]]; then\n    export HYBRIDTYPE=neoforge\nelif [[ \"${mohistType}\" == \"banner\" ]]; then\n    export HYBRIDTYPE=fabric\nelse\n    log \"Unknown server type\"\nfi\n\nexport SERVER\nexport FAMILY=HYBRID\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployNanoLimbo",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\n\nif ! SERVER=$(mc-image-helper github download-latest-asset \\\n  --output-directory=/data \\\n  --name-pattern=\"NanoLimbo(-.+?(?<!-sources))?\\.jar\" \\\n  BoomEaro/NanoLimbo\n  ); then\n    logError \"Failed to download NanoLimbo\"\n    exit 1\nfi\n\n: \"${SERVER_PORT:=25565}\"\n\nif [ ! -f /data/settings.yml ]; then\n  cat <<EOF > /data/settings.yml\n#\n# NanoLimbo configuration\n#\n# Server's host address and port. Set ip empty to use public address\nbind:\n  ip: '0.0.0.0'\n  port: ${SERVER_PORT}\n\n# Max number of players can join to server\n# Set -1 to make it infinite\nmaxPlayers: 100\n\n# Server's data in servers list\nping:\n  description: '{\"text\": \"&9NanoLimbo\"}'\n  version: 'NanoLimbo'\n  # Return static protocol version number in ping result\n  # By default, its -1 to return the client version if it supported\n  # https://wiki.vg/Protocol_version_numbers\n  protocol: -1\n\n# Available dimensions: OVERWORLD, NETHER, THE_END\ndimension: THE_END\n\n# Whether to display the player in the player list\n# For 1.16.5 clients, the player list will be sent even if disabled, to avoid crash\nplayerList:\n  enable: false\n  username: 'NanoLimbo'\n\n# Whether to display header and footer in player list\n# For 1.8+ clients\nheaderAndFooter:\n  enable: false\n  header: '{\"text\": \"&eWelcome!\"}'\n  footer: '{\"text\": \"&9NanoLimbo\"}'\n\n# Setup player's game mode\n# 0 - Survival\n# 1 - Creative (hide HP and food bar)\n# 2 - Adventure\n# 3 - Spectator (hide all UI bars)\n# Spectator works on 1.8+ clients\ngameMode: 3\n\n# Remove secure-chat toast\n# For 1.20.5+ clients\nsecureProfile: false\n\n# Server name which is shown under F3\n# For 1.13+ clients\nbrandName:\n  enable: true\n  content: 'NanoLimbo'\n\n# Message sends when player joins to the server\njoinMessage:\n  enable: true\n  text: '{\"text\": \"&eWelcome to the Limbo!\"}'\n\n# BossBar displays when player joins to the server\n# For 1.9+ clients\nbossBar:\n  enable: true\n  text: '{\"text\": \"Welcome to the Limbo!\"}'\n  health: 1.0\n  # Available colors: PINK, BLUE, RED, GREEN, YELLOW, PURPLE, WHITE\n  color: PINK\n  # Available divisions: SOLID, DASHES_6, DASHES_10, DASHES_12, DASHES_20\n  division: SOLID\n\n# Display title and subtitle\n# For 1.8+ clients\ntitle:\n  enable: true\n  # Set title text value empty, if you need only subtitle\n  title: '{\"text\": \"&9&lWelcome!\"}'\n  # Set subtitle text value empty, if you need only title\n  subtitle: '{\"text\": \"&6NanoLimbo\"}'\n  # Fade in time in ticks (1 sec = 20 ticks)\n  fadeIn: 10\n  # Stay time in ticks\n  stay: 100\n  # Fade out time in ticks\n  fadeOut: 10\n\n# Player info forwarding support.\n# Available types:\n#  - NONE\n#  - LEGACY\n#  - MODERN\n#  - BUNGEE_GUARD\n# Don't use secret if you do not use MODERN type\ninfoForwarding:\n  type: NONE\n  secret: '<YOUR_SECRET_HERE>'\n  tokens:\n    - '<BUNGEE_GUARD_TOKEN>'\n\n# Read timeout for connections in milliseconds\nreadTimeout: 30000\n\n# Define log level. For production, I'd recommend to use level 2\n# Log levels:\n# 0 - Display only errors\n# 1 - Display errors, warnings\n# 2 - Display errors, warnings, info\n# 3 - Display errors, warnings, info, debug\ndebugLevel: 2\n\n# Warning! Do not touch params of this block if you are not completely sure what is this!\nnetty:\n  # Use a Linux native transport type, if it possible\n  useEpoll: true\n  # EventLoopGroup threads count\n  threads:\n    bossGroup: 1\n    workerGroup: 4\n\n# Options to check incoming traffic and kick potentially malicious connections.\n# Take into account that player can send many small packets, for example, just moving mouse.\ntraffic:\n  # If true, then additional handler will be added to the channel pipeline\n  enable: true\n  # Max packet size in bytes\n  # Unlimited if -1\n  maxPacketSize: 8192\n  # The interval to measure packets over\n  # Lowering this value will limit peak packets from players which would target people with bad connections\n  # Raising this value will allow higher peak packet rates, which will help with people who have poor connections\n  # Ignored if -1.0\n  interval: 7.0\n  # The maximum packets per second for players\n  # It is measured over the configured interval\n  # Ignored if -1.0\n  maxPacketRate: 500.0\n  # The maximum packet bytes per second for players\n  # It is measured over the configured interval as an average bytes/sec\n  # Ignored if -1.0\n  maxPacketBytesRate: 2048.0\nEOF\nfi\n\nmc-image-helper patch --patch-env-prefix \"\" /image/nanolimbo-settings-patch.json\n\nexport SERVER\nexport FAMILY=LIMBO\n\nexec \"$(dirname \"$0\")/start-setupMounts\" \"$@\""
  },
  {
    "path": "scripts/start-deployNeoForge",
    "content": "#!/bin/bash\n\n: \"${NEOFORGE_VERSION:=latest}\"\n: \"${NEOFORGE_FORCE_REINSTALL:=false}}\"\n: \"${NEOFORGE_INSTALLER:=}\"\n\n# shellcheck source=start-utils\n. \"${SCRIPTS:-$(dirname \"$0\")}/start-utils\"\nisDebugging && set -x\n\nresultsFile=/data/.run-neoforge.env\n\nif [[ ${NEOFORGE_INSTALLER} ]]; then\n  if ! mc-image-helper install-neoforge --neoforge-installer=\"${NEOFORGE_INSTALLER}\" --output-directory=/data --results-file=${resultsFile} --minecraft-version=\"${VERSION}\" --force-reinstall=\"${NEOFORGE_FORCE_REINSTALL}\"; then\n    logError \"Failed to installForge given installer ${NEOFORGE_INSTALLER}\"\n    exit 1\n  fi\nelif ! mc-image-helper install-neoforge \\\n  --output-directory=/data \\\n  --results-file=${resultsFile} \\\n  --minecraft-version=\"${VERSION}\" \\\n  --neoforge-version=\"${NEOFORGE_VERSION}\" \\\n  --force-reinstall=\"${NEOFORGE_FORCE_REINSTALL}\"; then\n  logError \"Failed to install NeoForge\"\n  exit 1\nfi\n\napplyResultsFile ${resultsFile}\n\nexport FAMILY=FORGE\n\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployPaper",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nset -o pipefail\nhandleDebugMode\n\n: \"${PAPER_PROJECT:=paper}\"\n: \"${PAPER_BUILD:=${PAPERBUILD:-}}\"\n: \"${PAPER_CHANNEL:=default}\"\n: \"${PAPER_DOWNLOAD_URL:=}\"\n: \"${PAPER_CUSTOM_JAR:=}\"\n: \"${PAPER_CONFIG_DEFAULTS_REPO:=${PAPER_CONFIG_REPO:=https://raw.githubusercontent.com/Shonz1/minecraft-default-configs/main}}\"\n\nresultsFile=/data/.paper.env\nif [[ $PAPER_CUSTOM_JAR ]]; then\n  export SERVER=\"$PAPER_CUSTOM_JAR\"\nelif [[ $PAPER_DOWNLOAD_URL ]]; then\n  if ! mc-image-helper install-paper \\\n    --output-directory=/data \\\n    --results-file=\"$resultsFile\" \\\n    --url=\"$PAPER_DOWNLOAD_URL\"; then\n      logError \"Failed to download from custom PaperMC URL\"\n      exit 1\n  fi\n  applyResultsFile ${resultsFile}\nelse\n  args=(\n    --output-directory=/data\n    --results-file=\"$resultsFile\"\n    --project=\"$PAPER_PROJECT\"\n    --version=\"$VERSION\"\n    --channel=\"$PAPER_CHANNEL\"\n  )\n  if [[ $PAPER_BUILD ]]; then\n    args+=(--build=\"$PAPER_BUILD\")\n  fi\n  if ! mc-image-helper install-paper \"${args[@]}\"; then\n      logError \"Failed to download $PAPER_PROJECT\"\n      exit 1\n  fi\n  applyResultsFile ${resultsFile}\nfi\n\ndefaultTopLevelConfigs=\"bukkit.yml spigot.yml\"\nif versionLessThan 1.19; then\n  defaultTopLevelConfigs+=\" paper.yml\"\nelse\n  # Download default configs to allow for consistent patching\n  DOWNLOAD_DEFAULT_CONFIGS=$(buildDownloadList \"$PAPER_CONFIG_DEFAULTS_REPO\" \"$VERSION\" paper-global.yml paper-world-defaults.yml)\n  export DOWNLOAD_DEFAULT_CONFIGS\nfi\n\n# Download top-level configs to allow for consistent patching\nDOWNLOAD_DEFAULTS=$(buildDownloadList \"$PAPER_CONFIG_DEFAULTS_REPO\" \"$VERSION\" $defaultTopLevelConfigs)\nexport DOWNLOAD_DEFAULTS\n\n# Normalize on Spigot for downstream operations\nexport FAMILY=SPIGOT\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployPoseidon",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"${SCRIPTS:-$(dirname \"$0\")}/start-utils\"\nset -o pipefail\nset -e\nisDebugging && set -x\n\nresolveVersion\n\nif [ \"${VERSION}\" != \"b1.7.3\" ]; then\n  logError \"Poseidon server type only supports VERSION=b1.7.3\"\n  exit 1\nfi\n\n# : \"${POSEIDON_RELEASE:=latest}\" placeholder until I figure out how to add version selection with mc-image-helper\n: \"${POSEIDON_TYPE:=poseidon}\"\n\nif [[ ${POSEIDON_TYPE^^} = UBERBUKKIT ]]; then\n  poseidonRepo=\"Moresteck/uberbukkit\"\nelse\n  poseidonRepo=\"retromcorg/Project-Poseidon\"\nfi\n\nif ! SERVER=$(mc-image-helper github download-latest-asset \\\n  --output-directory=/data \\\n  --name-pattern=\"^(?!original-).+\\.jar\" \\\n  ${poseidonRepo}\n  ); then\n    logError \"Failed to download ${POSEIDON_TYPE}\"\n    exit 1\nfi\n\nexport SERVER\nexport FAMILY=SPIGOT\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployPufferfish",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\nset -euo pipefail\nisDebugging && set -x\n\nIFS=$'\\n\\t'\n\nif versionLessThan 1.17; then\n  logError \"Pufferfish server type only supports versions 1.17, 1.18 or 1.19, use PUFFERFISH_BUILD to select the the correct build 47 => 1.18.1, 50 => 1.18.2 etc\"\n  exit 1\nfi\n\n: \"${PUFFERFISH_BUILD:=lastSuccessfulBuild}\"\n\nmajorVersion=$(get_major_version \"$VERSION\")\nPUFFERFISH_BUILD_JSON=$(curl -X GET -s \"https://ci.pufferfish.host/job/Pufferfish-${majorVersion}/${PUFFERFISH_BUILD}/api/json\")\n# Example: \"url\": \"https://ci.pufferfish.host/job/Pufferfish-1.18/50/\",\nPUFFERFISH_BUILD_URL=$(jq -n \"$PUFFERFISH_BUILD_JSON\" | jq -jc '.url // empty' )\n# Example: \"fileName\": \"pufferfish-paperclip-1.18.2-R0.1-SNAPSHOT-reobf.jar\",\nPUFFERFISH_BUILD_FILENAME=$(jq -n \"$PUFFERFISH_BUILD_JSON\" | jq -jc '.artifacts[].fileName // empty' )\n# Example: \"relativePath\": \"pufferfish-server/build/libs/pufferfish-paperclip-1.21.7-R0.1-SNAPSHOT-mojmap.jar\",\nPUFFERFISH_BUILD_PATH=$(jq -n \"$PUFFERFISH_BUILD_JSON\" | jq -jc '.artifacts[].relativePath // empty' )\nPUFFERFISH_BUILD_DOWNLOAD_URL=\"${PUFFERFISH_BUILD_URL}artifact/${PUFFERFISH_BUILD_PATH}\"\n\n# Setting server to the Jar filename for export.\nexport SERVER=$PUFFERFISH_BUILD_FILENAME\n\nlog \"Removing old Pufferfish versions ...\"\nshopt -s nullglob\nfor f in pufferfish-*.jar; do\n  [[ $f != \"$SERVER\" ]] && rm \"$f\"\ndone\n\nif [[ ! -f \"$SERVER\" ]] || isTrue \"${FORCE_REDOWNLOAD:-false}\"; then\n    log \"Downloading Pufferfish from $PUFFERFISH_BUILD_DOWNLOAD_URL ...\"\n    if ! get -o \"$SERVER\" \"$PUFFERFISH_BUILD_DOWNLOAD_URL\"; then\n      logError \"Failed to download from $PUFFERFISH_BUILD_DOWNLOAD_URL (status=$?)\"\n      exit 3\n    fi\nfi\nif [[ \"${PUFFERFISH_CONFIG_REPO}\" ]]; then\n  defaultTopLevelConfigs=(bukkit.yml spigot.yml pufferfish.yml)\n  DOWNLOAD_DEFAULTS=$(buildDownloadList \"$PUFFERFISH_CONFIG_REPO\" \"$VERSION\" \"${defaultTopLevelConfigs[@]}\")\n  export DOWNLOAD_DEFAULTS\nfi\n\n# Normalize on Spigot for later operations\nexport FAMILY=SPIGOT\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployPurpur",
    "content": "#!/bin/bash\nset -euo pipefail\nIFS=$'\\n\\t'\n\n: \"${PURPUR_BUILD:=LATEST}\"\n: \"${PURPUR_DOWNLOAD_URL:=}\"\n: \"${PURPUR_CONFIG_REPO:=}\"\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\n\nresultsFile=/data/.purpur.env\n\nif [[ $PURPUR_DOWNLOAD_URL ]]; then\n  if ! mc-image-helper install-purpur \\\n    --output-directory=/data \\\n    --results-file=\"$resultsFile\" \\\n    --url=\"${PURPUR_DOWNLOAD_URL}\"; then\n      logError \"Failed to download from custom Purpur URL\"\n      exit 1\n  fi\nelse\n  args=(\n    --output-directory=/data\n    --results-file=\"$resultsFile\"\n    --version=\"$VERSION\"\n  )\n  if [[ $PURPUR_BUILD ]]; then\n    args+=(--build=\"$PURPUR_BUILD\")\n  fi\n  if ! mc-image-helper install-purpur \"${args[@]}\"; then\n      logError \"Failed to download Purpur\"\n      exit 1\n  fi\nfi\n\napplyResultsFile ${resultsFile}\n\nif [[ \"${PURPUR_CONFIG_REPO}\" ]]; then\n  defaultTopLevelConfigs=(bukkit.yml spigot.yml purpur.yml)\n  DOWNLOAD_DEFAULTS=$(buildDownloadList \"$PURPUR_CONFIG_REPO\" \"$VERSION\" \"${defaultTopLevelConfigs[@]}\")\n  export DOWNLOAD_DEFAULTS\nfi\n\n# Normalize on Spigot for later operations\nexport FAMILY=SPIGOT\n\nexec \"$(dirname \"$0\")/start-spiget\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployQuilt",
    "content": "#!/bin/bash\nset -eu\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\n: \"${QUILT_LAUNCHER:=}\"\n: \"${QUILT_LAUNCHER_URL:=}\"\n: \"${QUILT_INSTALLER_URL:=}\"\n: \"${QUILT_INSTALLER_VERSION:=LATEST}\"\n: \"${QUILT_LOADER_VERSION:=LATEST}\"\n\nisDebugging && set -x\n\nfunction mc-image-helper-quilt(){\n  mc-image-helper install-quilt \\\n    --loader-version=\"$QUILT_LOADER_VERSION\" \\\n    --minecraft-version=\"$VERSION\" \\\n    --output-directory=/data \\\n    --results-file=\"$resultsFile\" \"$@\"\n}\n\nresultsFile=/data/.quilt.env\n\nif [[ $QUILT_LAUNCHER ]]; then\n  logWarning \"Use of QUILT_LAUNCHER is a deprecated feature.\"\n  SERVER=\"$QUILT_LAUNCHER\"\n  export SERVER\n  resolveVersion\n\n  export FAMILY=FABRIC\n  exec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n\nelif [[ $QUILT_LAUNCHER_URL ]]; then\n  logError \"QUILT_LAUNCHER_URL is not longer supported. Pre-download and use QUILT_LAUNCHER.\"\n  exit 2\n\nelif [[ $QUILT_INSTALLER_URL ]]; then\n  if ! mc-image-helper-quilt --installer-url=\"$QUILT_INSTALLER_URL\"; then\n    logError \"Failed to installQuilt given custom installer URL $QUILT_INSTALLER_URL\"\n    exit 2\n  fi\n\nelse\n  if ! mc-image-helper-quilt --installer-version=\"$QUILT_INSTALLER_VERSION\"; then\n    logError \"Failed to installQuilt given installer version $QUILT_INSTALLER_VERSION\"\n    exit 2\n  fi\n\nfi\n\napplyResultsFile ${resultsFile}\n\nexport FAMILY=FABRIC\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deploySpongeVanilla",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\nexport TYPE=spongevanilla\n: ${SPONGEBRANCH:=STABLE}\n: ${SPONGEVERSION:=}\n\n# Parse branch\nlog \"Choosing branch for Sponge\"\ncase \"$SPONGEBRANCH\" in\n\n  EXPERIMENTAL|experimental|BLEEDING|bleeding)\n    SPONGEBRANCH=bleeding\n  ;;\n\n  *)\n    SPONGEBRANCH=stable\n  ;;\n\nesac\n\n# If not SPONGEVERSION selected, detect last version on selected branch\nif [ -z $SPONGEVERSION ]; then\n  log \"Choosing Version for Sponge\"\n  SPONGEVERSION=$(curl -fsSL https://dl-api.spongepowered.org/v1/org.spongepowered/$TYPE | jq -r --arg SPONGEBRANCH \"$SPONGEBRANCH\" '.buildTypes.$SPONGEBRANCH.latest.version')\nfi\n\nVERSION=\"$SPONGEVERSION\"\nexport VERSION\nexport SERVER=\"spongevanilla-$SPONGEVERSION.jar\"\n\nif [ ! -e \"$SERVER\" ] || [ -n \"$FORCE_REDOWNLOAD\" ]; then\n  log \"Downloading $SERVER ...\"\n  curl -sSL -o \"$SERVER\" \"https://repo.spongepowered.org/maven/org/spongepowered/$TYPE/$SPONGEVERSION/$SERVER\"\nfi\n\nexport FAMILY=SPONGE\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-deployVanilla",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\nset -o pipefail\n\nresolveVersion\nexport SERVER=\"minecraft_server.${VERSION// /_}.jar\"\n\nif [ ! -e \"$SERVER\" ] || [ -n \"$FORCE_REDOWNLOAD\" ]; then\n  debug \"Finding version manifest for $VERSION\"\n  versionManifestUrl=$(get 'https://launchermeta.mojang.com/mc/game/version_manifest.json' | jq --arg VERSION \"$VERSION\" --raw-output '[.versions[]|select(.id == $VERSION)][0].url')\n  result=$?\n  if [ $result != 0 ]; then\n    logError \"Failed to obtain version manifest URL ($result)\"\n    exit 1\n  fi\n  if [ \"$versionManifestUrl\" = \"null\" ]; then\n    logError \"Couldn't find a matching manifest entry for $VERSION\"\n    exit 1\n  fi\n  debug \"Found version manifest at $versionManifestUrl\"\n\n  if ! serverDownloadUrl=$(get --json-path '$.downloads.server.url' \"${versionManifestUrl}\"); then\n    logError \"Failed to obtain version manifest from $versionManifestUrl ($result)\"\n    exit 1\n  elif [ \"$serverDownloadUrl\" = \"null\" ]; then\n    logError \"There is not a server download for version $VERSION\"\n    exit 1\n  fi\n\n  log \"Downloading $VERSION server...\"\n  debug \"Downloading server from $serverDownloadUrl\"\n  get -o \"$SERVER\" \"$serverDownloadUrl\"\n  result=$?\n  if [ $result != 0 ]; then\n    logError \"Failed to download server from $serverDownloadUrl ($result)\"\n    exit 1\n  fi\nfi\nminecraftServerJarPath=/data/minecraft_server.jar\n\nif versionLessThan 1.6; then\n  if ! [[ -L $minecraftServerJarPath && $minecraftServerJarPath -ef \"/data/$SERVER\" ]]; then\n    rm -f $minecraftServerJarPath\n    ln -s \"/data/$SERVER\" $minecraftServerJarPath\n  fi\n  SERVER=minecraft_server.jar\nelif [[ -L $minecraftServerJarPath ]]; then\n  rm -f $minecraftServerJarPath\nfi\n\nisDebugging && ls -l\nexport FAMILY=VANILLA\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-finalExec",
    "content": "#!/bin/bash\n\n: \"${DEBUG_EXEC:=false}\"\n: \"${SETUP_ONLY:=false}\"\n: \"${CUSTOM_JAR_EXEC:=}\"\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\nbaseDataDir=/data\ntmpServerIconPath=/tmp/icon.img\nserverIconPath=${baseDataDir}/server-icon.png\nmcHealthEnvPath=${baseDataDir}/.mc-health.env\nbootstrapPath=${baseDataDir}/bootstrap.txt\n\nif [ -n \"$ICON\" ]; then\n    if [ ! -e server-icon.png ] || isTrue \"${OVERRIDE_ICON}\"; then\n      log \"Using server icon from $ICON...\"\n      if isURL \"$ICON\"; then\n        # Not sure what it is yet...call it \"img\"\n        if ! get -o \"$tmpServerIconPath\" \"$ICON\"; then\n          logError \"Failed to download icon from $ICON\"\n          exit 1\n        fi\n        ICON=\"$tmpServerIconPath\"\n        iconSrc=\"url\"\n      elif [ -f \"$ICON\" ]; then\n        iconSrc=\"file\"\n      else\n        logError \"$ICON does not appear to be a URL or existing file\"\n        exit 1\n      fi\n      read -r -a specs < <(identify \"$ICON\" | awk 'NR == 1 { print $2, $3 }')\n      if [ \"${specs[0]} ${specs[1]}\" = \"PNG 64x64\" ]; then\n        if [ $iconSrc = url ]; then\n          mv -f \"$tmpServerIconPath\" \"$serverIconPath\"\n        else\n          cp -f \"$ICON\" \"$serverIconPath\"\n        fi\n      elif [ \"${specs[0]}\" = GIF ]; then\n        log \"Converting GIF image to 64x64 PNG...\"\n        convert \"$ICON\"[0] -resize 64x64! \"$serverIconPath\"\n      else\n        log \"Converting image to 64x64 PNG...\"\n        convert \"$ICON\" -resize 64x64! \"$serverIconPath\"\n      fi\n    fi\nfi\n\nuseGeneratedLogs=${GENERATE_LOG4J2_CONFIG:-${ENABLE_ROLLING_LOGS:-false}}\nuseFallbackJvmFlag=false\n\nSERVER_DIR=\"$baseDataDir\"\nif [[ ${FTB_DIR:-} ]]; then\n  SERVER_DIR=\"$FTB_DIR\"\nfi\n\n\npatchLog4jConfig() {\n  file=${1?}\n  url=${2?}\n  if ! get -o \"${SERVER_DIR}/${file}\" \"$url\"; then\n    logError \"Failed to download corrected log4j config, fallback to JVM flag\"\n    useFallbackJvmFlag=true\n    return 1\n  fi\n  JVM_OPTS=\"-Dlog4j.configurationFile=${file} ${JVM_OPTS}\"\n  useGeneratedLogs=false\n}\n\n# Temporarily disable debugging output\noldState=$(shopt -po xtrace || true)\nshopt -u -o xtrace\n# Patch Log4j remote code execution vulnerability\n# See https://www.minecraft.net/en-us/article/important-message--security-vulnerability-java-edition\nif versionLessThan 1.7; then\n  : # No patch required here.\nelif isFamily VANILLA && versionLessThan 1.12; then\n  patchLog4jConfig log4j2_17-111.xml https://launcher.mojang.com/v1/objects/dd2b723346a8dcd48e7f4d245f6bf09e98db9696/log4j2_17-111.xml\nelif isFamily VANILLA && versionLessThan 1.17; then\n  patchLog4jConfig log4j2_112-116.xml https://launcher.mojang.com/v1/objects/02937d122c86ce73319ef9975b58896fc1b491d1/log4j2_112-116.xml\n# See https://purpurmc.org/docs/Log4j/\nelif isType PURPUR && versionLessThan 1.17; then\n  patchLog4jConfig purpur_log4j2_1141-1165.xml https://purpurmc.org/docs/xml/purpur_log4j2_1141-1165.xml\nelif isType PURPUR && versionLessThan 1.18.1; then\n  patchLog4jConfig purpur_log4j2_117.xml https://purpurmc.org/docs/xml/purpur_log4j2_117.xml\nelif versionLessThan 1.18.1; then\n  useFallbackJvmFlag=true\nfi\neval \"$oldState\"\n\nif ${useFallbackJvmFlag}; then\n  JVM_OPTS=\"-Dlog4j2.formatMsgNoLookups=true ${JVM_OPTS}\"\nfi\n\nif versionLessThan 1.7; then\n  : # No patch required here.\nelif versionLessThan 1.18.1; then\n  if isTrue ${SKIP_LOG4J_PATCHER:-false}; then\n    log \"Skipping Log4jPatcher, make sure you are not affected\"\n  else\n    JVM_OPTS=\"-javaagent:/image/Log4jPatcher.jar ${JVM_OPTS}\"\n  fi\nfi\n\n# Set up log4j2 configuration with templating support\nLOGFILE=\"${SERVER_DIR}/log4j2.xml\"\nif ${useGeneratedLogs}; then\n  # Set up log configuration defaults\n  : \"${LOG_LEVEL:=info}\"\n  : \"${ROLLING_LOG_MAX_FILES:=1000}\"\n  # Note: Can't use ${VAR:=default} syntax for values containing } as it breaks parsing\n  if [ -z \"${ROLLING_LOG_FILE_PATTERN}\" ]; then\n    ROLLING_LOG_FILE_PATTERN='logs/%d{yyyy-MM-dd}-%i.log.gz'\n  fi\n\n  # Pattern format defaults (compatible with vanilla Minecraft)\n  # Note: Can't use ${VAR:=default} syntax because } in the value breaks parsing\n  if [ -z \"${LOG_CONSOLE_FORMAT}\" ]; then\n    LOG_CONSOLE_FORMAT='[%d{HH:mm:ss}] [%t/%level]: %msg%n'\n  fi\n  if [ -z \"${LOG_TERMINAL_FORMAT}\" ]; then\n    LOG_TERMINAL_FORMAT='[%d{HH:mm:ss} %level]: %msg%n'\n  fi\n  if [ -z \"${LOG_FILE_FORMAT}\" ]; then\n    LOG_FILE_FORMAT='[%d{HH:mm:ss}] [%t/%level]: %msg%n'\n  fi\n\n  export LOG_LEVEL ROLLING_LOG_FILE_PATTERN ROLLING_LOG_MAX_FILES\n  export LOG_CONSOLE_FORMAT LOG_TERMINAL_FORMAT LOG_FILE_FORMAT\n\n\n  # Always regenerate if file doesn't exist\n  if [ ! -e \"$LOGFILE\" ] || isTrue \"${REGENERATE_LOG4J2:-true}\"; then\n    log \"Generating log4j2.xml from template in ${LOGFILE}\"\n\n    # Generate log4j2.xml using heredoc for reliable variable substitution\n    cat > \"$LOGFILE\" <<EOF\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"WARN\">\n    <Appenders>\n        <Console name=\"SysOut\" target=\"SYSTEM_OUT\">\n            <PatternLayout pattern=\"${LOG_CONSOLE_FORMAT}\" />\n        </Console>\n        <Queue name=\"TerminalConsole\">\n            <PatternLayout pattern=\"${LOG_TERMINAL_FORMAT}\" />\n        </Queue>\n        <RollingRandomAccessFile name=\"File\" fileName=\"logs/latest.log\" filePattern=\"${ROLLING_LOG_FILE_PATTERN}\">\n            <PatternLayout pattern=\"${LOG_FILE_FORMAT}\" />\n            <Policies>\n                <TimeBasedTriggeringPolicy />\n                <OnStartupTriggeringPolicy />\n            </Policies>\n            <DefaultRolloverStrategy max=\"${ROLLING_LOG_MAX_FILES}\"/>\n        </RollingRandomAccessFile>\n    </Appenders>\n    <Loggers>\n        <Root level=\"${LOG_LEVEL}\">\n            <filters>\n                <MarkerFilter marker=\"NETWORK_PACKETS\" onMatch=\"DENY\" onMismatch=\"NEUTRAL\" />\n            </filters>\n            <AppenderRef ref=\"SysOut\"/>\n            <AppenderRef ref=\"File\"/>\n            <AppenderRef ref=\"TerminalConsole\"/>\n        </Root>\n    </Loggers>\n</Configuration>\nEOF\n  else\n    log \"log4j2.xml already exists and is up to date, skipping generation\"\n  fi\n\n  # Apply the log4j2 configuration\n  JVM_OPTS=\"-Dlog4j.configurationFile=log4j2.xml ${JVM_OPTS}\"\nelse\n  rm -f \"${LOGFILE}\"\nfi\n\n# Optional disable console\nif versionLessThan 1.14 && [[ ${CONSOLE,,} = false ]]; then\n  EXTRA_ARGS+=\" --noconsole\"\nfi\n\n# Optional disable GUI for headless servers\nif [[ ${GUI,,} = false ]]; then\n  EXTRA_ARGS+=\" nogui\"\nfi\n\nexpandedDOpts=\nif [ -n \"$JVM_DD_OPTS\" ]; then\n      for dopt in $JVM_DD_OPTS\n      do\n          expandedDOpts=\"${expandedDOpts} -D${dopt/:/=}\"\n      done\nfi\n\nif isTrue \"${ENABLE_JMX}\"; then\n  : \"${JMX_PORT:=7091}\"\n  JVM_OPTS=\"${JVM_OPTS}\n  -Dcom.sun.management.jmxremote.local.only=false\n  -Dcom.sun.management.jmxremote.port=${JMX_PORT}\n  -Dcom.sun.management.jmxremote.rmi.port=${JMX_PORT}\n  -Dcom.sun.management.jmxremote.authenticate=false\n  -Dcom.sun.management.jmxremote.ssl=false\n  -Dcom.sun.management.jmxremote.host=${JMX_BINDING:-0.0.0.0}\n  -Djava.rmi.server.hostname=${JMX_HOST:-localhost}\"\n\n  log \"JMX is enabled. Make sure you have port forwarding for ${JMX_PORT}\"\nfi\n\n: \"${USE_AIKAR_FLAGS:=false}\"\n: \"${USE_MEOWICE_FLAGS:=false}\"\n: \"${USE_MEOWICE_GRAALVM_FLAGS:=false}\"\n\nif isTrue \"${USE_MEOWICE_FLAGS}\"; then\n  java_major_version=$(mc-image-helper java-release)\n  if [[ $java_major_version -gt 16 ]]; then\n    log \"Java version $java_major_version using MeowIce's flags for Java 17+\"\n  else\n    log \"Your Java version is $java_major_version, MeowIce's flags are for Java 17+ falling back to Aikar's\"\n    USE_MEOWICE_FLAGS=FALSE\n    USE_AIKAR_FLAGS=TRUE\n  fi\nfi\n\nif isTrue \"${USE_AIKAR_FLAGS}\" || isTrue \"${USE_MEOWICE_FLAGS}\"; then\n  # From https://mcflags.emc.gs/\n  if isTrue \"${USE_MEOWICE_FLAGS}\"; then\n    log \"Using MeowIce's flags\"\n    G1NewSizePercent=28\n    G1MaxNewSizePercent=50\n    G1HeapRegionSize=16M\n    G1ReservePercent=15\n    InitiatingHeapOccupancyPercent=20\n    G1MixedGCCountTarget=3\n    G1RSetUpdatingPauseTimePercent=0\n  elif [[ $MAX_MEMORY ]] && (( $(normalizeMemSize \"${MAX_MEMORY}\") >= $(normalizeMemSize 12g) )); then\n    log \"Using Aikar's >12GB flags\"\n    G1NewSizePercent=40\n    G1MaxNewSizePercent=50\n    G1HeapRegionSize=16M\n    G1ReservePercent=15\n    InitiatingHeapOccupancyPercent=20\n    G1MixedGCCountTarget=4\n    G1RSetUpdatingPauseTimePercent=5\n  else\n    log \"Using Aikar's flags\"\n    G1NewSizePercent=30\n    G1MaxNewSizePercent=40\n    G1HeapRegionSize=8M\n    G1ReservePercent=20\n    InitiatingHeapOccupancyPercent=15\n    G1MixedGCCountTarget=4\n    G1RSetUpdatingPauseTimePercent=5\n  fi\n\n  JVM_XX_OPTS=\"${JVM_XX_OPTS}\n  -XX:+UseG1GC\n  -XX:+ParallelRefProcEnabled\n  -XX:MaxGCPauseMillis=200\n  -XX:+UnlockExperimentalVMOptions\n  -XX:+DisableExplicitGC\n  -XX:+AlwaysPreTouch\n  -XX:G1NewSizePercent=${G1NewSizePercent}\n  -XX:G1MaxNewSizePercent=${G1MaxNewSizePercent}\n  -XX:G1HeapRegionSize=${G1HeapRegionSize}\n  -XX:G1ReservePercent=${G1ReservePercent}\n  -XX:G1HeapWastePercent=5\n  -XX:G1MixedGCCountTarget=${G1MixedGCCountTarget}\n  -XX:InitiatingHeapOccupancyPercent=${InitiatingHeapOccupancyPercent}\n  -XX:G1MixedGCLiveThresholdPercent=90\n  -XX:G1RSetUpdatingPauseTimePercent=${G1RSetUpdatingPauseTimePercent}\n  -XX:SurvivorRatio=32\n  -XX:+PerfDisableSharedMem\n  -XX:MaxTenuringThreshold=1\n  \"\n  if isTrue \"${USE_AIKAR_FLAGS}\"; then\n    JVM_XX_OPTS=\"${JVM_XX_OPTS}\n    -Dusing.aikars.flags=https://mcflags.emc.gs\n    -Daikars.new.flags=true\n    \"\n  fi\nfi\n\nif isTrue \"${USE_MEOWICE_FLAGS}\"; then\n  JVM_XX_OPTS=\"${JVM_XX_OPTS}\n  -XX:+UnlockDiagnosticVMOptions\n  -XX:+UnlockExperimentalVMOptions\n  -XX:G1SATBBufferEnqueueingThresholdPercent=30\n  -XX:G1ConcMarkStepDurationMillis=5\n  -XX:+UseNUMA\n  -XX:-DontCompileHugeMethods\n  -XX:MaxNodeLimit=240000\n  -XX:NodeLimitFudgeFactor=8000\n  -XX:ReservedCodeCacheSize=400M\n  -XX:NonNMethodCodeHeapSize=12M\n  -XX:ProfiledCodeHeapSize=194M\n  -XX:NonProfiledCodeHeapSize=194M\n  -XX:NmethodSweepActivity=1\n  -XX:+UseFastUnorderedTimeStamps\n  -XX:+UseCriticalJavaThreadPriority\n  -XX:AllocatePrefetchStyle=3\n  -XX:+AlwaysActAsServerClassMachine\n  -XX:+UseTransparentHugePages\n  -XX:LargePageSizeInBytes=2M\n  -XX:+UseLargePages\n  -XX:+EagerJVMCI\n  -XX:+UseStringDeduplication\n  -XX:+UseAES\n  -XX:+UseAESIntrinsics\n  -XX:+UseFMA\n  -XX:+UseLoopPredicate\n  -XX:+RangeCheckElimination\n  -XX:+OptimizeStringConcat\n  -XX:+UseCompressedOops\n  -XX:+UseThreadPriorities\n  -XX:+OmitStackTraceInFastThrow\n  -XX:+RewriteBytecodes\n  -XX:+RewriteFrequentPairs\n  -XX:+UseFPUForSpilling\n  -XX:+UseVectorCmov\n  -XX:+UseXMMForArrayCopy\n  -XX:+EliminateLocks\n  -XX:+DoEscapeAnalysis\n  -XX:+AlignVector\n  -XX:+OptimizeFill\n  -XX:+EnableVectorSupport\n  -XX:+UseCharacterCompareIntrinsics\n  -XX:+UseCopySignIntrinsic\n  -XX:+UseVectorStubs\n  \"\n  if [[ $(arch) == \"x86_64\" ]]; then\n    JVM_XX_OPTS=\"${JVM_XX_OPTS}\n    -XX:+UseFastStosb\n    -XX:+UseNewLongLShift\n    -XX:+UseXmmI2D\n    -XX:+UseXmmI2F\n    -XX:+UseXmmLoadAndClearUpper\n    -XX:+UseXmmRegToRegMoveAll\n    -XX:UseAVX=2\n    -XX:UseSSE=4\n    \"\n  else\n    log \"cpu not x86_64, disabling architecture specific flags\"\n  fi\nfi\n\nif isTrue \"${USE_MEOWICE_GRAALVM_FLAGS}\"; then\n  if [[ $java_major_version -gt 23 ]]; then\n    log \"Java 24 or higher detected, using modified GraalVM flags\"\n    JVM_XX_OPTS=\"${JVM_XX_OPTS}\n    -XX:+UseFastJNIAccessors\n    -XX:+UseInlineCaches\n    -XX:+SegmentedCodeCache\n    -Djdk.nio.maxCachedBufferSize=262144\n    -Djdk.graal.UsePriorityInlining=true\n    -Djdk.graal.Vectorization=true\n    -Djdk.graal.OptDuplication=true\n    -Djdk.graal.DetectInvertedLoopsAsCounted=true\n    -Djdk.graal.LoopInversion=true\n    -Djdk.graal.VectorizeHashes=true\n    -Djdk.graal.EnterprisePartialUnroll=true\n    -Djdk.graal.VectorizeSIMD=true\n    -Djdk.graal.StripMineNonCountedLoops=true\n    -Djdk.graal.SpeculativeGuardMovement=true\n    -Djdk.graal.TuneInlinerExploration=1\n    -Djdk.graal.LoopRotation=true\n    -Djdk.graal.CompilerConfiguration=enterprise\n    --enable-native-access=ALL-UNNAMED\n    \"\n  else\n    log \"Using MeowIce's flags for Graalvm\"\n    JVM_XX_OPTS=\"${JVM_XX_OPTS}\n    -XX:+UseFastJNIAccessors\n    -XX:+UseInlineCaches\n    -XX:+SegmentedCodeCache\n    -Djdk.nio.maxCachedBufferSize=262144\n    -Dgraal.UsePriorityInlining=true\n    -Dgraal.Vectorization=true\n    -Dgraal.OptDuplication=true\n    -Dgraal.DetectInvertedLoopsAsCounted=true\n    -Dgraal.LoopInversion=true\n    -Dgraal.VectorizeHashes=true\n    -Dgraal.EnterprisePartialUnroll=true\n    -Dgraal.VectorizeSIMD=true\n    -Dgraal.StripMineNonCountedLoops=true\n    -Dgraal.SpeculativeGuardMovement=true\n    -Dgraal.TuneInlinerExploration=1\n    -Dgraal.LoopRotation=true\n    -Dgraal.OptWriteMotion=true\n    -Dgraal.CompilerConfiguration=enterprise\n    \"\n  fi\nfi\n\nif isTrue \"${USE_FLARE_FLAGS}\"; then\n  JVM_XX_OPTS=\"${JVM_XX_OPTS}\n  -XX:+UnlockDiagnosticVMOptions\n  -XX:+DebugNonSafepoints\n  \"\nfi\n\nif isTrue \"${USE_SIMD_FLAGS}\"; then\n  JVM_XX_OPTS=\"${JVM_XX_OPTS}\n  --add-modules=jdk.incubator.vector\n  \"\nfi\n\n# Handle GTNH args\nif isType \"GTNH\"; then\n  expandedDOpts=\"${expandedDOpts} -Dfml.readTimeout=180\"\n  java_major_version=$(mc-image-helper java-release)\n  if (( java_major_version == 8 )); then\n    debug \"Setting GTNH java8 args.\"\n    JVM_XX_OPTS=\"${JVM_XX_OPTS}\n    -XX:+UseStringDeduplication\n    -XX:+UseCompressedOops\n    -XX:+UseCodeCacheFlushing\n    \"\n  elif (( java_major_version >= 17 )); then\n    debug \"Setting GTNH java17+ args.\"\n    expandedDOpts=\"${expandedDOpts} @java9args.txt\"\n  fi\nfi\n\nif [[ ${INIT_MEMORY} || ${MAX_MEMORY} ]]; then\n  log \"Setting initial memory to ${INIT_MEMORY:=${MEMORY}} and max to ${MAX_MEMORY:=${MEMORY}}\"\n  if isPercentage \"$INIT_MEMORY\"; then\n    JVM_OPTS=\"-XX:InitialRAMPercentage=$(getPercentageValue \"${INIT_MEMORY}\") ${JVM_OPTS}\"\n  elif [[ ${INIT_MEMORY} ]]; then\n    JVM_OPTS=\"-Xms${INIT_MEMORY} ${JVM_OPTS}\"\n  fi\n\n  if isPercentage \"$MAX_MEMORY\"; then\n    JVM_OPTS=\"-XX:MaxRAMPercentage=$(getPercentageValue \"${MAX_MEMORY}\") ${JVM_OPTS}\"\n  elif [[ ${MAX_MEMORY} ]]; then\n    JVM_OPTS=\"-Xmx${MAX_MEMORY} ${JVM_OPTS}\"\n  fi\nfi\n\nfunction copyFilesForCurseForge() {\n  if [ ! -e \"${FTB_DIR}/server-icon.png\" ] && [ -e \"$serverIconPath\" ]; then\n    cp -f \"$serverIconPath\" \"${FTB_DIR}/\"\n  fi\n\n  cp -f ${baseDataDir}/eula.txt \"${FTB_DIR}/\"\n}\n\nif versionLessThan 'b1.8'; then\n  echo \"\nDISABLE_HEALTHCHECK=true\n  \" > \"$mcHealthEnvPath\"\nelif versionLessThan 1.3; then\n  echo \"\nMC_HEALTH_EXTRA_ARGS=(\n  --use-old-server-list-ping\n)\n  \" > \"$mcHealthEnvPath\"\nelif versionLessThan 1.7; then\n  echo \"\nMC_HEALTH_EXTRA_ARGS=(\n  --use-server-list-ping\n)\n  \" > \"$mcHealthEnvPath\"\nelif isTrue \"$USES_PROXY_PROTOCOL\"; then\n  echo \"\nMC_HEALTH_EXTRA_ARGS=(\n  --use-proxy\n)\n  \" > \"$mcHealthEnvPath\"\nelse\n  rm -f \"$mcHealthEnvPath\"\nfi\n\nmcServerRunnerArgs=(\n--stop-duration \"${STOP_DURATION:-60}s\"\n)\nif isTrue \"${CREATE_CONSOLE_IN_PIPE:-false}\"; then\n  mcServerRunnerArgs+=(--named-pipe \"${CONSOLE_IN_NAMED_PIPE:-/tmp/minecraft-console-in}\")\nfi\nif [[ ${STOP_SERVER_ANNOUNCE_DELAY} ]]; then\n  mcServerRunnerArgs+=(--stop-server-announce-delay \"${STOP_SERVER_ANNOUNCE_DELAY}s\")\nfi\nif isTrue \"${ENABLE_SSH}\"; then\n  mcServerRunnerArgs+=(--remote-console)\nfi\n\nif [[ ${TYPE} == \"CURSEFORGE\" && \"${SERVER}\" ]]; then\n  copyFilesForCurseForge\n\n  cd \"${FTB_DIR}\" || (logError \"Can't go into ${FTB_DIR}\"; exit 1)\n  log \"Starting CurseForge server in ${FTB_DIR}...\"\n  if isTrue \"${DEBUG_EXEC}\"; then\n    set -x\n  fi\n  exec mc-server-runner ${bootstrapArgs} \"${mcServerRunnerArgs[@]}\" java $JVM_XX_OPTS $JVM_OPTS $expandedDOpts -jar \"$(basename \"${SERVER}\")\" \"$@\" $EXTRA_ARGS\nelif [[ ${TYPE} == \"CURSEFORGE\" ]]; then\n  mcServerRunnerArgs+=(--shell bash)\n\n  copyFilesForCurseForge\n\nif isPercentage \"$INIT_MEMORY\" || isPercentage \"$MAX_MEMORY\"; then\n# Convert to bytes\nNORM_INIT_MEM=$(normalizeMemSize \"$INIT_MEMORY\")\nNORM_MAX_MEM=$(normalizeMemSize \"$MAX_MEMORY\")\n# Convert to MB\n((NORM_INIT_MEM*=1048576))\n((NORM_MAX_MEM*=1048576))\n\n  cat > \"${FTB_DIR}/settings-local.sh\" <<EOF\nexport MIN_RAM=\"${NORM_INIT_MEM}M\"\nexport MAX_RAM=\"${NORM_MAX_MEM}M\"\nexport JAVA_PARAMETERS=\"${JVM_XX_OPTS} ${JVM_OPTS} $expandedDOpts\"\nEOF\nelse\n  cat > \"${FTB_DIR}/settings-local.sh\" <<EOF\nexport MIN_RAM=\"${INIT_MEMORY}\"\nexport MAX_RAM=\"${MAX_MEMORY}\"\nexport JAVA_PARAMETERS=\"${JVM_XX_OPTS} ${JVM_OPTS} $expandedDOpts\"\nEOF\nfi\n\n  # patch CurseForge cfg file, if present\n  if [ -f \"${FTB_DIR}/settings.cfg\" ] && [[ ${MAX_MEMORY} ]]; then\n    if isPercentage \"$MAX_MEMORY\"; then\n      sed -i \"s/MAX_RAM=[^;]*/MAX_RAM=${NORM_MAX_MEM}M/\" \"${FTB_DIR}/settings.cfg\"\n    else\n      sed -i \"s/MAX_RAM=[^;]*/MAX_RAM=${MAX_MEMORY}/\" \"${FTB_DIR}/settings.cfg\"\n    fi\n  fi\n\n  cd \"${FTB_DIR}\" || (logError \"Can't go into ${FTB_DIR}\"; exit 1)\n  log \"Running FTB ${FTB_SERVER_START} in ${FTB_DIR} ...\"\n\n  finalArgs=\"${FTB_SERVER_START}\"\n\n  if isTrue \"${SETUP_ONLY}\"; then\n    echo \"SETUP_ONLY: ${finalArgs}\"\n    exit\n  fi\n\n  if isTrue \"${DEBUG_EXEC}\"; then\n    set -x\n  fi\n  if [ -f \"${FTB_DIR}/variables.txt\" ]; then\n      JVM_ARGS=\"${JVM_XX_OPTS} ${JVM_OPTS} $expandedDOpts\"\n      JVM_ARGS=${JVM_ARGS//$'\\n'/}\n      sed -i \"s~JAVA_ARGS=.*~JAVA_ARGS=\\\"${JVM_ARGS}\\\"~\" \"${FTB_DIR}/variables.txt\"\n  fi\n  exec mc-server-runner \"${mcServerRunnerArgs[@]}\" \"${finalArgs[@]}\"\nelif [[ $SERVER =~ run.sh ]]; then\n  log \"Using Forge supplied run.sh script...\"\n  echo $JVM_XX_OPTS $JVM_OPTS $expandedDOpts > user_jvm_args.txt\n  if isTrue ${SETUP_ONLY}; then\n    echo \"SETUP_ONLY: bash ${SERVER}\"\n    exit\n  fi\n  if isTrue \"${DEBUG_EXEC}\"; then\n    set -x\n  fi\n  exec mc-server-runner \"${mcServerRunnerArgs[@]}\" --shell bash \"${SERVER}\" $EXTRA_ARGS\nelse\n  # If we have a bootstrap.txt file... feed that in to the server stdin\n  if [ -f $bootstrapPath ]; then\n    bootstrapArgs=\"--bootstrap $bootstrapPath\"\n  fi\n\n  log \"Starting the Minecraft server...\"\n\n  # Specifically want the variables to expand to args, so...\n  # shellcheck disable=SC2206\n  finalArgs=(\n    $JVM_XX_OPTS\n    $JVM_OPTS\n    $expandedDOpts\n  )\n\n  if [[ $CUSTOM_JAR_EXEC ]]; then\n    # shellcheck disable=SC2206\n    finalArgs+=($CUSTOM_JAR_EXEC)\n  else\n    finalArgs+=(-jar \"$SERVER\")\n  fi\n\n  # shellcheck disable=SC2206\n  finalArgs+=(\n    \"$@\" $EXTRA_ARGS\n  )\n\n  if isTrue ${SETUP_ONLY}; then\n    echo \"SETUP_ONLY: java ${finalArgs[*]}\"\n    exit\n  fi\n\n  if isTrue \"${DEBUG_EXEC}\"; then\n    set -x\n  fi\n\n  exec mc-server-runner ${bootstrapArgs} \"${mcServerRunnerArgs[@]}\" java \"${finalArgs[@]}\"\nfi\n"
  },
  {
    "path": "scripts/start-rconcmds",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\n: \"${RCON_CMDS_STARTUP:=}\"\n: \"${RCON_CMDS_ON_CONNECT:=}\"\n: \"${RCON_CMDS_ON_DISCONNECT:=}\"\n: \"${RCON_CMDS_FIRST_CONNECT:=}\"\n: \"${RCON_CMDS_LAST_DISCONNECT:=}\"\n: \"${RCON_CMDS_PERIOD:=10}\"\n: \"${SERVER_PORT:=25565}\"\nexport RCON_CMDS_STARTUP\nexport RCON_CMDS_ON_CONNECT\nexport RCON_CMDS_ON_DISCONNECT\nexport RCON_CMDS_FIRST_CONNECT\nexport RCON_CMDS_LAST_DISCONNECT\nexport RCON_CMDS_PERIOD\nexport SERVER_PORT\n\nlog \"Rcon cmds functionality enabled\"\n\nisDebugging && set -x\n\nisNumericElseSetToDefault RCON_CMDS_PERIOD 10\ncheckIfNotZeroElseSetToDefault RCON_CMDS_PERIOD 10\n\n\"$(dirname \"$0\")/auto/rcon-cmds-daemon.sh\" &\n"
  },
  {
    "path": "scripts/start-setupDatapack",
    "content": "#!/bin/bash\n\nset -e -o pipefail\n\n: \"${VANILLATWEAKS_FILE:=}\"\n: \"${VANILLATWEAKS_SHARECODE:=}\"\n: \"${REMOVE_OLD_DATAPACKS:=false}\"\n: \"${DATAPACKS_FILE:=}\"\n: \"${REMOVE_OLD_DATAPACKS_DEPTH:=1} \"\n: \"${REMOVE_OLD_DATAPACKS_INCLUDE:=*.zip}\"\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\n\nout_dir=/data/${LEVEL:-world}/datapacks\n\n# Remove old datapacks\nif isTrue \"${REMOVE_OLD_DATAPACKS}\" && [ -z \"${DATAPACKS_FILE}\" ]; then\n    if [ -d \"$out_dir\" ]; then\n    find \"$out_dir\" -mindepth 1 -maxdepth ${REMOVE_OLD_DATAPACKS_DEPTH:-16} -wholename \"${REMOVE_OLD_DATAPACKS_INCLUDE:-*}\" -not -wholename \"${REMOVE_OLD_DATAPACKS_EXCLUDE:-}\" -delete\n  fi\nfi\n\nif [[ \"$DATAPACKS\" ]]; then\n  mkdir -p \"$out_dir\"\n\n  for i in ${DATAPACKS//,/ }\n  do\n    if isURL \"$i\"; then\n      log \"Downloading datapack $i ...\"\n      if ! get -o \"${out_dir}\" \"$i\"; then\n        logError \"Failed to download from $i into $out_dir\"\n        exit 2\n      fi\n    elif [[ -f \"$i\" && \"$i\" =~ .*\\.zip ]]; then\n      log \"Copying datapack located at $i ...\"\n      out_file=$(basename \"$i\")\n      if ! cp \"$i\" \"${out_dir}/$out_file\"; then\n        logError \"Failed to copy from $i into $out_dir\"\n        exit 2\n      fi\n    elif [[ -d \"$i\" ]]; then\n      if [[ -f \"$i/pack.mcmeta\" ]]; then\n        log \"Copying datapack from $i\"\n        cp -r \"$i\" \"${out_dir}\"\n      else\n        log \"Copying datapacks from $i ...\"\n        cp \"$i\"/*.zip \"${out_dir}\"\n      fi\n    else\n      logError \"Invalid URL or path given in DATAPACKS: $i\"\n      exit 2\n    fi\n  done\n\nelif [[ \"$DATAPACKS_FILE\" ]]; then\n  if [ ! -f \"$DATAPACKS_FILE\" ]; then\n    logError \"Given DATAPACKS_FILE file does not exist\"\n    exit 2\n  fi\n\n  mkdir -p \"$out_dir\"\n\n  args=(\n    -o \"${out_dir}\"\n    --log-progress-each\n    --skip-existing\n    --uris-file \"${DATAPACKS_FILE}\"\n  )\n  if isTrue \"${REMOVE_OLD_DATAPACKS}\"; then\n    args+=(\n    --prune-others \"${REMOVE_OLD_DATAPACKS_INCLUDE}\"\n    --prune-depth \"${REMOVE_OLD_DATAPACKS_DEPTH}\"\n    )\n  fi\n\n  if ! get \"${args[@]}\" ; then\n    logError \"Failed to retrieve one or more datapacks\"\n    exit 1\n  fi\nfi\n\nif [[ ${VANILLATWEAKS_FILE} || ${VANILLATWEAKS_SHARECODE} ]]; then\n  mc-image-helper vanillatweaks \\\n    --output-directory=\"/data\" \\\n    --world-subdir=\"${LEVEL:-world}\" \\\n    --share-codes=\"$VANILLATWEAKS_SHARECODE\" \\\n    --pack-files=\"$VANILLATWEAKS_FILE\"\nfi\n\nexec \"$(dirname \"$0\")/start-setupModpack\" \"$@\"\n"
  },
  {
    "path": "scripts/start-setupEnvVariables",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\nset -e\nhandleDebugMode\n\n: \"${REPLACE_ENV_IN_PLACE:=${REPLACE_ENV_VARIABLES:-false}}\"\n: \"${REPLACE_ENV_PATHS:=/data}\"\n: \"${REPLACE_ENV_SUFFIXES:=yml,yaml,txt,cfg,conf,properties,hjson,json,tml,toml}\"\n: \"${REPLACE_ENV_VARIABLE_PREFIX=${ENV_VARIABLE_PREFIX:-CFG_}}\"\n: \"${REPLACE_ENV_VARIABLES_EXCLUDES:=}\"\n: \"${REPLACE_ENV_VARIABLES_EXCLUDE_PATHS:=}\"\n: \"${PATCH_DEFINITIONS:=}\"\n: \"${DEBUG:=false}\"\n: \"${DOWNLOAD_DEFAULTS:=}\"\n: \"${DOWNLOAD_DEFAULT_CONFIGS:=}\"\n: \"${SKIP_DOWNLOAD_DEFAULTS:=false}\"\n: \"${APPLY_EXTRA_FILES:=${DOWNLOAD_EXTRA_CONFIGS:-}}\"\n\nif isTrue \"${REPLACE_ENV_IN_PLACE}\"; then\n  log \"Replacing env variables in ${REPLACE_ENV_PATHS} that match the prefix '$REPLACE_ENV_VARIABLE_PREFIX' ...\"\n\n  mc-image-helper interpolate \\\n    --replace-env-file-suffixes=\"${REPLACE_ENV_SUFFIXES}\" \\\n    --replace-env-excludes=\"${REPLACE_ENV_VARIABLES_EXCLUDES}\" \\\n    --replace-env-exclude-paths=\"${REPLACE_ENV_VARIABLES_EXCLUDE_PATHS}\" \\\n    --replace-env-prefix=\"${REPLACE_ENV_VARIABLE_PREFIX}\" \\\n    \"${REPLACE_ENV_PATHS[@]}\"\nfi\n\nif isFalse \"$SKIP_DOWNLOAD_DEFAULTS\"; then\n  commonArgs=(\n    --skip-existing\n    --skip-up-to-date=false\n    --quiet-when-skipped\n    --ignore-missing-sources\n  )\n  if [[ $DOWNLOAD_DEFAULT_CONFIGS ]]; then\n    if ! mc-image-helper mcopy \\\n      --to /data/config \\\n      \"${commonArgs[@]}\" \\\n      \"$DOWNLOAD_DEFAULT_CONFIGS\"; then\n      logWarning \"One or more default config files were not available from $DOWNLOAD_DEFAULT_CONFIGS\"\n    fi\n  fi\n  if [[ $DOWNLOAD_DEFAULTS ]]; then\n    if ! mc-image-helper mcopy \\\n      --to /data \\\n      \"${commonArgs[@]}\" \\\n      \"$DOWNLOAD_DEFAULTS\"; then\n      logWarning \"One or more default files were not available from $DOWNLOAD_DEFAULTS\"\n    fi\n  fi\nfi\n\nif [[ $APPLY_EXTRA_FILES ]]; then\n  mc-image-helper mcopy \\\n    --to /data \\\n    --skip-existing \\\n    --skip-up-to-date=false \\\n    --quiet-when-skipped \\\n    \"$APPLY_EXTRA_FILES\"\nfi\n\nif [[ ${PATCH_DEFINITIONS} ]]; then\n  log \"Applying patch definitions from ${PATCH_DEFINITIONS}\"\n  mc-image-helper patch \\\n    --patch-env-prefix=\"${REPLACE_ENV_VARIABLE_PREFIX}\" \\\n    \"${PATCH_DEFINITIONS}\"\nfi\n\nexec \"$(dirname \"$0\")/start-setupRbac\" \"$@\"\n"
  },
  {
    "path": "scripts/start-setupModpack",
    "content": "#!/bin/bash\n\nset -e -o pipefail\n\n: \"${REMOVE_OLD_MODS:=false}\"\n: \"${MODS_OUT_DIR:=/data/mods}\"\n: \"${MODS_FILE:=}\"\n: \"${PLUGINS_OUT_DIR:=/data/plugins}\"\n: \"${PLUGINS_FILE:=}\"\n: \"${REMOVE_OLD_MODS_DEPTH:=1} \"\n: \"${REMOVE_OLD_MODS_INCLUDE:=*.jar,*-version.json}\"\n: \"${CF_API_KEY_FILE:=}\" # Path to file containing CurseForge API key\n: \"${MODRINTH_LOADER:=}\"\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\n\nif [[ -n ${CF_API_KEY_FILE} ]]; then\n  if [[ -r \"${CF_API_KEY_FILE}\" ]]; then\n    CF_API_KEY=\"$(cat \"${CF_API_KEY_FILE}\")\"\n    export CF_API_KEY\n  else\n    logError \"CF_API_KEY_FILE is not readable: ${CF_API_KEY_FILE}\"\n    exit 1\n  fi\nfi\n\nsum_file=/data/.generic_pack.sum\n# Remove old mods/plugins\nif isTrue \"${REMOVE_OLD_MODS}\" && [ -z \"${MODS_FILE}\" ]; then\n  removeOldMods \"$MODS_OUT_DIR\"\n  removeOldMods \"$PLUGINS_OUT_DIR\"\n  rm -f \"$sum_file\"\nfi\n\nfunction handlePackwiz() {\n  # If packwiz url passed, bootstrap packwiz and update mods before other modpack processing\n  if [[ \"${PACKWIZ_URL:-}\" ]]; then\n    if ! packwizInstaller=$(mc-image-helper maven-download \\\n                           --maven-repo=https://maven.packwiz.infra.link/repository/release/ \\\n                           --group=link.infra.packwiz --artifact=packwiz-installer --classifier=dist \\\n                           --skip-existing); then\n      logError \"Failed to get packwiz installer\"\n      exit 1\n    fi\n\n    log \"Running packwiz installer against URL: ${PACKWIZ_URL}\"\n    if ! java -cp \"${packwizInstaller}\" link.infra.packwiz.installer.Main -s server \"${PACKWIZ_URL}\"; then\n      logError \"Failed to run packwiz installer\"\n      exit 1\n    fi\n  fi\n}\n\nfunction handleModpackZip() {\n# If supplied with a URL for a modpack (simple zip of jars), download it and unpack\nif [[ \"$MODPACK\" ]]; then\n  if isURL \"${MODPACK}\"; then\n    log \"Downloading mod/plugin pack\"\n    if ! get -o /tmp/modpack.zip \"${MODPACK}\"; then\n      logError \"Failed to download from ${MODPACK}\"\n      exit 2\n    fi\n  elif [[ \"$MODPACK\" =~ .*\\.zip ]]; then\n    if ! cp \"$MODPACK\" /tmp/modpack.zip; then\n      logError \"Failed to copy from $MODPACK\"\n      exit 2\n    fi\n  else\n    logError \"Invalid URL or Path given for MODPACK: $MODPACK\"\n    exit 1\n  fi\n\n  if [ \"$FAMILY\" = \"SPIGOT\" ]; then\n    mkdir -p \"$PLUGINS_OUT_DIR\"\n    if ! unzip -o -d \"$PLUGINS_OUT_DIR\" /tmp/modpack.zip; then\n      logError \"Failed to unzip the modpack from ${MODPACK}\"\n    fi\n  else\n    mkdir -p \"$MODS_OUT_DIR\"\n    if ! unzip -o -d \"$MODS_OUT_DIR\" /tmp/modpack.zip; then\n      logError \"Failed to unzip the modpack from ${MODPACK}\"\n    fi\n  fi\n  rm -f /tmp/modpack.zip\nfi\n}\n\nfunction handleListings() {\n  if usesMods && usesPlugins; then\n      if [[ -v MODS ]]; then\n\n        ensureRemoveAllModsOff \"MODS is set\"\n\n        mkdir -p \"$MODS_OUT_DIR\"\n        mc-image-helper mcopy \\\n          --glob=*.jar \\\n          --scope=var-list \\\n          --to=\"$MODS_OUT_DIR\" \\\n          \"$MODS\"\n      fi\n      if [[ -v PLUGINS ]]; then\n        ensureRemoveAllModsOff \"PLUGINS is set\"\n        mkdir -p \"$PLUGINS_OUT_DIR\"\n        mc-image-helper mcopy \\\n          --glob=*.jar \\\n          --scope=var-list \\\n          --to=\"$PLUGINS_OUT_DIR\" \\\n          \"$PLUGINS\"\n      fi\n\n      if [[ \"$MODS_FILE\" ]]; then\n        ensureRemoveAllModsOff \"MODS_FILE is set\"\n        mkdir -p \"$MODS_OUT_DIR\"\n        mc-image-helper mcopy \\\n          --file-is-listing \\\n          --scope=file-list \\\n          --to=\"$MODS_OUT_DIR\" \\\n          \"$MODS_FILE\"\n      fi\n      if [[ \"$PLUGINS_FILE\" ]]; then\n        ensureRemoveAllModsOff \"PLUGINS_FILE is set\"\n        mkdir -p \"$PLUGINS_OUT_DIR\"\n        mc-image-helper mcopy \\\n          --file-is-listing \\\n          --scope=file-list \\\n          --to=\"$PLUGINS_OUT_DIR\" \\\n          \"$PLUGINS_FILE\"\n      fi\n\n  elif usesPlugins || usesMods; then\n    outDir=\"$MODS_OUT_DIR\"\n    if usesPlugins; then\n      outDir=\"$PLUGINS_OUT_DIR\"\n    fi\n\n    if [[ -v MODS || -v PLUGINS ]]; then\n      ensureRemoveAllModsOff \"MODS or PLUGINS is set\"\n      mkdir -p \"$outDir\"\n      mc-image-helper mcopy \\\n        --glob=*.jar \\\n        --scope=var-list \\\n        --to=\"$outDir\" \\\n        \"${MODS:-}\" \"${PLUGINS:-}\"\n    fi\n\n    if [[ \"$MODS_FILE\" || \"$PLUGINS_FILE\" ]]; then\n      ensureRemoveAllModsOff \"MODS_FILE or PLUGINS_FILE is set\"\n      mkdir -p \"$outDir\"\n      mc-image-helper mcopy \\\n        --file-is-listing \\\n        --scope=file-list \\\n        --to=\"$outDir\" \\\n        \"$MODS_FILE\" \"$PLUGINS_FILE\"\n    fi\n\n  fi\n}\n\nfunction handleGenericPacks() {\n  : \"${GENERIC_PACKS:=${GENERIC_PACK}}\"\n  : \"${GENERIC_PACKS_PREFIX:=}\"\n  : \"${GENERIC_PACKS_SUFFIX:=}\"\n  : \"${GENERIC_PACKS_DISABLE_MODS:=}\"\n\n  if [[ \"${GENERIC_PACKS}\" ]]; then\n    IFS=',' read -ra packs <<< \"${GENERIC_PACKS}\"\n\n    packFiles=()\n    for packEntry in \"${packs[@]}\"; do\n      pack=\"${GENERIC_PACKS_PREFIX}${packEntry}${GENERIC_PACKS_SUFFIX}\"\n      if isURL \"${pack}\"; then\n        mkdir -p /data/packs\n        log \"Downloading generic pack from $pack\"\n        if ! outfile=$(get -o /data/packs --output-filename --skip-up-to-date \"$pack\"); then\n          logError \"Failed to download $pack\"\n          exit 2\n        fi\n        packFiles+=(\"$outfile\")\n      else\n        packFiles+=(\"$pack\")\n      fi\n    done\n\n    isDebugging && [ -f \"$sum_file}\" ] && cat \"$sum_file\"\n\n    log \"Checking if generic packs are up to date\"\n    if isTrue \"${SKIP_GENERIC_PACK_UPDATE_CHECK:-false}\" && [ -f \"$sum_file\" ]; then\n      log \"Skipping generic pack update check\"\n    elif isTrue \"${FORCE_GENERIC_PACK_UPDATE}\" || ! checkSum \"${sum_file}\"; then\n      log \"Generic pack(s) are out of date. Re-applying...\"\n\n      original_base_dir=/data/.tmp/generic_pack_base\n      base_dir=$original_base_dir\n      rm -rf \"${base_dir}\"\n      mkdir -p \"${base_dir}\"\n      for pack in \"${packFiles[@]}\"; do\n        isDebugging && ls -l \"${pack}\"\n        extract \"${pack}\" \"${base_dir}\"\n      done\n\n      # Disable mods\n      for mod in ${GENERIC_PACKS_DISABLE_MODS}; do\n        log Disabling $mod\n        find \"${base_dir}\" -name \"$mod\" -exec mv {} {}.disabled -v \\;\n      done\n\n      # Remove any eula file since container manages it\n      rm -f \"${base_dir}/eula.txt\"\n\n      # recalculate the actual base directory of content\n      if ! base_dir=$(mc-image-helper find \\\n          --max-depth=3 --type=directory --name=mods,plugins,config \\\n          --only-shallowest --fail-no-matches --format '%h' \\\n          \"$base_dir\"); then\n        logError \"Unable to find content base of generic packs ${GENERIC_PACKS}. Directories:\"\n        mc-image-helper find --name=* --max-depth=3 --type=directory --format '- %P' \"$original_base_dir\"\n        exit 1\n      fi\n\n      if [ -f /data/manifest.txt ]; then\n        log \"Manifest exists from older generic pack, cleaning up ...\"\n        while read -r f; do\n          rm -rf \"/data/${f}\"\n        done < /data/manifest.txt\n        # prune empty dirs\n        find /data -mindepth 1 -depth -type d -empty -delete\n        rm -f /data/manifest.txt\n      fi\n\n      log \"Writing generic pack manifest ... \"\n      find \"${base_dir}\" -type f -printf \"%P\\n\" > /data/manifest.txt\n\n      log \"Applying generic pack ...\"\n      cp -R -f \"${base_dir}\"/* /data\n      rm -rf $original_base_dir\n\n      if isTrue \"${SKIP_GENERIC_PACK_CHECKSUM:-false}\"; then\n        log \"Skipping generic pack(s) checksum\"\n      else\n        log \"Saving generic pack(s) checksum\"\n        sha1sum \"${packFiles[@]}\" > \"${sum_file}\"\n        if isDebugging; then\n          cat \"$sum_file\"\n        fi\n      fi\n    fi\n  fi\n}\n\nfunction handleModrinthProjects() {\n  # need to be backward compatible with old variable name, MODRINTH_ALLOWED_VERSION_TYPE\n  : \"${MODRINTH_PROJECTS_DEFAULT_VERSION_TYPE:=${MODRINTH_ALLOWED_VERSION_TYPE:-release}}\"\n  : \"${MODRINTH_DOWNLOAD_DEPENDENCIES:=none}\"\n  if [[ -v MODRINTH_DOWNLOAD_OPTIONAL_DEPENDENCIES ]]; then\n    logWarning \"The variable MODRINTH_DOWNLOAD_OPTIONAL_DEPENDENCIES is removed.\"\n    logWarning \"        Use MODRINTH_DOWNLOAD_DEPENDENCIES=optional instead\"\n  fi\n\n  if [[ -v MODRINTH_PROJECTS ]]; then\n\n    if isType CUSTOM; then\n      if ! [[ $MODRINTH_LOADER ]]; then\n        logError \"MODRINTH_LOADER must be set when using TYPE=CUSTOM and MODRINTH_PROJECTS\"\n        exit 1\n      fi\n      loader=\"${MODRINTH_LOADER,,}\"\n    elif isFamily HYBRID; then\n      loader=${HYBRIDTYPE}\n    elif isFamily VANILLA; then\n      loader=datapack\n    else\n      loader=\"${TYPE,,}\"\n    fi\n\n    mc-image-helper modrinth \\\n      --output-directory=/data \\\n      --world-directory=\"${LEVEL:-world}\" \\\n      --projects=\"${MODRINTH_PROJECTS}\" \\\n      --game-version=\"${VERSION}\" \\\n      --loader=\"$loader\" \\\n      --download-dependencies=\"$MODRINTH_DOWNLOAD_DEPENDENCIES\" \\\n      --allowed-version-type=\"$MODRINTH_PROJECTS_DEFAULT_VERSION_TYPE\"\n  fi\n}\n\nfunction handleCurseForgeFiles() {\n  args=()\n  if usesMods && ! usesPlugins; then\n    args+=(--default-category mc-mods)\n  elif usesPlugins && ! usesMods; then\n    args+=(--default-category bukkit-plugins)\n  fi\n\n  case \"${TYPE,,}\" in\n    forge|neoforge|fabric|quilt)\n      args+=(--mod-loader \"$TYPE\")\n      ;;\n    *)\n      if isFamily HYBRID; then\n        # To disambiguate mc-mods we'll assume that hybrid servers\n        # are blending Forge (rather than Fabric or NeoForge)\n        args+=(--mod-loader \"${HYBRIDTYPE}\")\n      fi\n      ;;\n  esac\n\n  # shellcheck disable=SC2086\n  # since we want CURSEFORGE_FILES to expand\n  mc-image-helper curseforge-files \\\n    \"${args[@]}\" \\\n    \"${CURSEFORGE_FILES}\"\n}\n\nhandlePackwiz\n\nhandleModpackZip\n\nhandleListings\n\nif [[ $MANIFEST ]]; then\n  logError \"MANIFEST is no longer supported.\"\n  logError \"       Use MODPACK_PLATFORM=AUTO_CURSEFORGE and CF_MODPACK_MANIFEST instead\"\n  exit 1\nfi\n\nif [[ $MODS_FORGEAPI_KEY || $MODS_FORGEAPI_FILE || $MODS_FORGEAPI_PROJECTIDS ]]; then\n  logError \"The MODS_FORGEAPI_FILE / MODS_FORGEAPI_PROJECTIDS feature is no longer supported\"\n  logError \"      Use CURSEFORGE_FILES instead.\"\n  exit 1\nfi\n\nhandleGenericPacks\n\nhandleModrinthProjects\n\nif usesMods || usesPlugins; then\n  handleCurseForgeFiles\nfi\n\n# If supplied with a URL for a config (simple zip of configurations), download it and unpack\nif [[ \"$MODCONFIG\" ]]; then\ncase \"X$MODCONFIG\" in\n  X[Hh][Tt][Tt][Pp]*[Zz][iI][pP])\n    log \"Downloading mod/plugin configs via HTTP\"\n    log \"  from $MODCONFIG ...\"\n    curl -sSL -o /tmp/modconfig.zip \"$MODCONFIG\"\n    if [ \"$FAMILY\" = \"SPIGOT\" ]; then\n      mkdir -p /data/plugins\n      unzip -o -d /data/plugins /tmp/modconfig.zip\n    else\n      mkdir -p /data/config\n      unzip -o -d /data/config /tmp/modconfig.zip\n    fi\n    rm -f /tmp/modconfig.zip\n    ;;\n  *)\n    log \"Invalid URL given for modconfig: Must be HTTP or HTTPS and a ZIP file\"\n    ;;\nesac\nfi\n\nexec \"$(dirname \"$0\")/start-setupMounts\" \"$@\"\n"
  },
  {
    "path": "scripts/start-setupMounts",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\n: \"${SYNC_SKIP_NEWER_IN_DESTINATION:=${PLUGINS_SYNC_UPDATE:-true}}\"\n: \"${REPLACE_ENV_DURING_SYNC:=true}\"\n: \"${REPLACE_ENV_SUFFIXES:=yml,yaml,txt,cfg,conf,properties,hjson,json,tml,toml}\"\n: \"${REPLACE_ENV_VARIABLE_PREFIX=${ENV_VARIABLE_PREFIX:-CFG_}}\"\n: \"${REPLACE_ENV_VARIABLES_EXCLUDES:=}\"\n: \"${REPLACE_ENV_VARIABLES_EXCLUDE_PATHS:=}\"\n: \"${DEBUG:=false}\"\n: \"${MODS_OUT_DIR:=/data/mods}\"\n: \"${PLUGINS_OUT_DIR:=/data/plugins}\"\n\nset -e\nisDebugging && set -x\n\nif isTrue \"${SYNC_SKIP_NEWER_IN_DESTINATION}\"; then\n  updateArg=\"--skip-newer-in-destination\"\nfi\n\nif isTrue \"${REPLACE_ENV_DURING_SYNC}\"; then\n  subcommand=sync-and-interpolate\nelse\n  subcommand=sync\nfi\n\nfunction mc-image-helper-mounts(){\n  mc-image-helper \\\n    ${subcommand} $updateArg \\\n    --replace-env-file-suffixes=\"${REPLACE_ENV_SUFFIXES}\" \\\n    --replace-env-excludes=\"${REPLACE_ENV_VARIABLES_EXCLUDES}\" \\\n    --replace-env-exclude-paths=\"${REPLACE_ENV_VARIABLES_EXCLUDE_PATHS}\" \\\n    --replace-env-prefix=\"${REPLACE_ENV_VARIABLE_PREFIX}\" \"$@\"\n}\n\n: \"${COPY_PLUGINS_SRC:=\"/plugins\"}\"\n: \"${COPY_PLUGINS_DEST:=${PLUGINS_OUT_DIR}}\"\n\nif usesPlugins; then\n  mkdir -p \"${COPY_PLUGINS_DEST}\"\n  log \"Copying any plugins from ${COPY_PLUGINS_SRC} to ${COPY_PLUGINS_DEST}\"\n  mc-image-helper-mounts \"${COPY_PLUGINS_SRC}\" \"${COPY_PLUGINS_DEST}\"\nfi\n\n: \"${COPY_MODS_SRC:=\"/mods\"}\"\n: \"${COPY_MODS_DEST:=${MODS_OUT_DIR}}\"\n\nif usesMods; then\n  log \"Copying any mods from ${COPY_MODS_SRC} to ${COPY_MODS_DEST}\"\n  mc-image-helper-mounts \"${COPY_MODS_SRC}\" \"${COPY_MODS_DEST}\"\nfi\n\n: \"${COPY_CONFIG_SRC:=\"/config\"}\"\n: \"${COPY_CONFIG_DEST:=\"/data/config\"}\"\n\nlog \"Copying any configs from ${COPY_CONFIG_SRC} to ${COPY_CONFIG_DEST}\"\nmc-image-helper-mounts \"${COPY_CONFIG_SRC}\" \"${COPY_CONFIG_DEST}\"\n\nexec \"$(dirname \"$0\")/start-setupServerProperties\" \"$@\"\n"
  },
  {
    "path": "scripts/start-setupRbac",
    "content": "#!/bin/bash\nset -euo pipefail\nIFS=$'\\n\\t'\n\n: \"${EXISTING_OPS_FILE:=SYNC_FILE_MERGE_LIST}\"\n: \"${EXISTING_WHITELIST_FILE:=SYNC_FILE_MERGE_LIST}\"\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nisDebugging && set -x\n\nif [[ -v APPEND_OPS ]] && isTrue \"${APPEND_OPS}\"; then\n  EXISTING_OPS_FILE=MERGE\nelif [[ -v OVERRIDE_OPS ]] && isTrue \"${OVERRIDE_OPS}\"; then\n  EXISTING_OPS_FILE=SYNCHRONIZE\nfi\n\nif [[ -v APPEND_WHITELIST ]] && isTrue \"${APPEND_WHITELIST}\"; then\n  EXISTING_WHITELIST_FILE=MERGE\nelif [[ -v OVERRIDE_WHITELIST ]] && isTrue \"${OVERRIDE_WHITELIST}\"; then\n  EXISTING_WHITELIST_FILE=SYNCHRONIZE\nfi\n\nsharedArgs=(--version=\"$VERSION\")\nif [[ ${FTB_DIR:-} ]]; then\n  sharedArgs+=(--output-directory=\"$FTB_DIR\")\nelse\n  sharedArgs+=(--output-directory=/data)\nfi\n\nif [[ -v OPS_FILE ]]; then\n  existing=\"$EXISTING_OPS_FILE\"\n  if [[ \"$EXISTING_OPS_FILE\" = SYNC_FILE_MERGE_LIST ]]; then\n    existing=SYNCHRONIZE\n  fi\n  mc-image-helper manage-users \\\n    \"${sharedArgs[@]}\" \\\n    --type=JAVA_OPS \\\n    --input-is-file \\\n    --existing=\"${existing}\" \\\n    \"$OPS_FILE\"\nfi\nif [[ -v OPS ]]; then\n  args=()\n  existing=\"$EXISTING_OPS_FILE\"\n  # Working with an OPS list, so normalize the value to a \"non-file\" mode\n  if [[ \"$EXISTING_OPS_FILE\" = SYNC_FILE_MERGE_LIST ]]; then\n    existing=MERGE\n  fi\n  # legacy option\n  if [[ -v APPEND_OPS ]] && isTrue \"${APPEND_OPS}\"; then\n    existing=MERGE\n  fi\n  # legacy option\n  if [[ -v OVERRIDE_OPS ]] && isFalse \"${OVERRIDE_OPS}\"; then\n    existing=SKIP\n  fi\n  # shellcheck disable=SC2086\n  mc-image-helper manage-users \\\n    \"${sharedArgs[@]}\" \"${args[@]}\" \\\n    --type=JAVA_OPS \\\n    --existing=\"${existing}\" \\\n    $OPS\nfi\n\nif [[ -v WHITELIST_FILE ]]; then\n  existing=\"$EXISTING_WHITELIST_FILE\"\n  if [[ \"$EXISTING_WHITELIST_FILE\" = SYNC_FILE_MERGE_LIST ]]; then\n    existing=SYNCHRONIZE\n  fi\n  mc-image-helper manage-users \\\n    \"${sharedArgs[@]}\" \\\n    --type=JAVA_WHITELIST \\\n    --input-is-file \\\n    --existing=\"${existing}\" \\\n   \"$WHITELIST_FILE\"\nfi\nif [[ -v WHITELIST ]]; then\n  args=()\n  existing=\"$EXISTING_WHITELIST_FILE\"\n  if [[ \"$EXISTING_WHITELIST_FILE\" = SYNC_FILE_MERGE_LIST ]]; then\n    existing=MERGE\n  fi\n  # legacy option\n  if [[ -v APPEND_WHITELIST ]] && isTrue \"${APPEND_WHITELIST}\"; then\n    existing=MERGE\n  fi\n  # legacy option\n  if [[ -v OVERRIDE_WHITELIST ]] && isFalse \"${OVERRIDE_WHITELIST}\"; then\n    existing=SKIP\n  fi\n  # shellcheck disable=SC2086\n  mc-image-helper manage-users \\\n    \"${sharedArgs[@]}\" \"${args[@]}\" \\\n    --type=JAVA_WHITELIST \\\n    --existing=\"${existing}\" \\\n    $WHITELIST\nfi\n\nexec \"$(dirname \"$0\")/start-finalExec\" \"$@\"\n"
  },
  {
    "path": "scripts/start-setupServerProperties",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\n\n: \"${SERVER_PROPERTIES:=/data/server.properties}\"\n: \"${OVERRIDE_SERVER_PROPERTIES:=true}\"\n: \"${SKIP_SERVER_PROPERTIES:=false}\"\n: \"${ENABLE_WHITELIST:=}\"\n\nfunction customizeServerProps {\n  local firstSetup=$1\n  # Whitelist processing\n  if [ -n \"$WHITELIST\" ] || [ -n \"$WHITELIST_FILE\" ] || isTrue \"${ENABLE_WHITELIST}\"; then\n    log \"Enabling whitelist functionality\"\n    WHITELIST_PROP=true\n    ENFORCE_WHITELIST=true\n    export WHITELIST_PROP ENFORCE_WHITELIST\n  elif isTrue \"$firstSetup\" || isFalse \"${ENABLE_WHITELIST}\"; then\n    log \"Disabling whitelist functionality\"\n    WHITELIST_PROP=false\n    export WHITELIST_PROP\n  fi\n\n  # normalize MOTD\n  if [[ ${TYPE^^} = LIMBO ]]; then\n    if [[ $MOTD ]] && ! [[ $MOTD =~ ^{ ]]; then\n      # shellcheck disable=SC2089\n      MOTD=\"{\\\"text\\\":\\\"${MOTD}\\\"}\"\n    fi\n  fi\n\n  if [[ -v MODE ]]; then\n    log \"Setting mode\"\n    case ${MODE,,} in\n      su*|0)\n        if versionLessThan 1.13; then\n          MODE=0\n        else\n          MODE=survival\n        fi\n        ;;\n      c*|1)\n        if versionLessThan 1.13; then\n          MODE=1\n        else\n          MODE=creative\n        fi\n        ;;\n      a*|2)\n        if versionLessThan 1.13; then\n          MODE=2\n        else\n          MODE=adventure\n        fi\n        ;;\n      sp*|3)\n        if versionLessThan 1.13; then\n          MODE=3\n        else\n          MODE=spectator\n        fi\n        ;;\n      *)\n        logError \"Invalid game mode: $MODE\"\n        exit 1\n        ;;\n    esac\n  fi\n\n  if [[ -v DIFFICULTY ]]; then\n    case ${DIFFICULTY,,} in\n      peaceful|0)\n        if versionLessThan 1.13; then\n          DIFFICULTY=0\n        else\n          DIFFICULTY=peaceful\n        fi\n        ;;\n      easy|1)\n        if versionLessThan 1.13; then\n          DIFFICULTY=1\n        else\n          DIFFICULTY=easy\n        fi\n        ;;\n      normal|2)\n        if versionLessThan 1.13; then\n          DIFFICULTY=2\n        else\n          DIFFICULTY=normal\n        fi\n        ;;\n      hard|3)\n        if versionLessThan 1.13; then\n          DIFFICULTY=3\n        else\n          DIFFICULTY=hard\n        fi\n        ;;\n      *)\n        log \"DIFFICULTY must be peaceful(0), easy(1), normal(2), or hard(3).\"\n        exit 1\n        ;;\n    esac\n  fi\n\n  setPropertiesArgs=(\n    --definitions \"/image/property-definitions.json\"\n  )\n  if [[ -v CUSTOM_SERVER_PROPERTIES ]]; then\n    setPropertiesArgs+=(--custom-properties \"$CUSTOM_SERVER_PROPERTIES\")\n  fi\n  if [[ -v SERVER_PROPERTIES_ESCAPE_UNICODE ]]; then\n    if isTrue \"$SERVER_PROPERTIES_ESCAPE_UNICODE\"; then\n      setPropertiesArgs+=(--escape-unicode)\n    fi\n  elif versionLessThan '1.20'; then\n    setPropertiesArgs+=(--escape-unicode)\n  fi\n\n  handleDebugMode\n  if ! mc-image-helper set-properties \"${setPropertiesArgs[@]}\" \"$SERVER_PROPERTIES\"; then\n    logError \"Failed to update server.properties\"\n    exit 1\n  fi\n\n}\n\n# Deploy server.properties file\nif [[ ${TYPE} == \"CURSEFORGE\" ]]; then\n  export SERVER_PROPERTIES=\"${FTB_DIR}/server.properties\"\n  log \"detected FTB, changing properties path to ${SERVER_PROPERTIES}\"\nfi\n\nif ! isTrue \"${SKIP_SERVER_PROPERTIES}\"; then\n  if [ ! -e \"$SERVER_PROPERTIES\" ] && [[ \"${SERVER_PROPERTIES_REPO_URL:-}\" ]]; then\n    # Helper to support base URLs (like the config repos)\n    # If the URL doesn't end in .properties, assume it is a base and append version/file\n    if [[ \"${SERVER_PROPERTIES_REPO_URL}\" != *\".properties\" ]]; then\n      SERVER_PROPERTIES_REPO_URL=$(buildDownloadList \"$SERVER_PROPERTIES_REPO_URL\" \"$VERSION\" server.properties)\n    fi\n\n    if ! mc-image-helper mcopy \\\n      --to \"$(dirname \"$SERVER_PROPERTIES\")\" \\\n      --skip-existing \\\n      --ignore-missing-sources \\\n      \"$SERVER_PROPERTIES_REPO_URL\"; then\n      logWarning \"Failed to download server.properties from ${SERVER_PROPERTIES_REPO_URL}\"\n    fi\n  fi\n\n  if [ ! -e \"$SERVER_PROPERTIES\" ]; then\n    log \"Creating server properties in ${SERVER_PROPERTIES}\"\n\n    # If not provided, generate a reasonable default message-of-the-day,\n    # which shows up in the server listing in the client\n    if ! [ -v MOTD ]; then\n      # snapshot is the odd case where we have to look at version to identify that label\n      if [[ ${DECLARED_TYPE} == \"VANILLA\" && ${VERSION} == \"SNAPSHOT\" ]]; then\n        label=SNAPSHOT\n      else\n        label=${DECLARED_TYPE}\n      fi\n\n      # Convert label to title-case\n      label=${label,,}\n      label=${label^}\n      MOTD=\"A ${label} Minecraft Server powered by Docker\"\n    fi\n\n    customizeServerProps true\n  elif isTrue \"${OVERRIDE_SERVER_PROPERTIES}\"; then\n    customizeServerProps false\n  else\n    log \"server.properties already created and managed manually\"\n  fi\nelse\n  log \"Skipping setup of server.properties\"\nfi\n\nif isTrue \"${ENABLE_AUTOPAUSE}\"; then\n  if [ -f \"$SERVER_PROPERTIES\" ]; then\n    current_max_tick=$( grep 'max-tick-time' \"$SERVER_PROPERTIES\" | sed -r 's/( )+//g' | awk -F= '{print $2}' )\n    if (( current_max_tick > 0 && current_max_tick < 86400000 )); then\n      logWarning \"The server.properties for the server doesn't have the Server Watchdog (effectively) disabled.\"\n      logWarning \"         Autopause functionality resuming the process might trigger the Watchdog and restart the server completely.\"\n      logWarning \"         Set the MAX_TICK_TIME env variable (or max-tick-time property) to a high value (or disable the Watchdog with value -1 for versions 1.8.1+).\"\n    fi\n  fi\nfi\n\nif isTrue \"${DUMP_SERVER_PROPERTIES:-false}\"; then\n  log \"DEBUG Dumping server.properties\"\n  cat \"${SERVER_PROPERTIES}\"\nfi\n\nexec \"$(dirname \"$0\")/start-setupEnvVariables\" \"$@\"\n"
  },
  {
    "path": "scripts/start-setupWorld",
    "content": "#!/bin/bash\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nset -e\nisDebugging && set -x\n\nworldDownload=/data/tmp/world.zip\ntmpWorldData=/data/tmp/world-data\n\n# support absolute directories\nif [[ \"${LEVEL:-world}\" =~ ^\\/.*$ ]]; then\n  worldDest=${LEVEL}\nelse\n  if [ \"$TYPE\" = \"CURSEFORGE\" ]; then\n    worldDest=$FTB_DIR/${LEVEL:-world}\n  else\n    worldDest=/data/${LEVEL:-world}\n  fi\nfi\n\nif [[ \"$WORLD\" ]] && ( isTrue \"${FORCE_WORLD_COPY}\" || [ ! -d \"$worldDest\" ] ); then\n  if isTrue \"${FORCE_WORLD_COPY}\"; then\n    log \"Removing existing world data in $worldDest ${worldDest}_nether ${worldDest}_the_end\"\n    rm -rf \"$worldDest\" \\\n      \"${worldDest}_nether\" \\\n      \"${worldDest}_the_end\"\n  fi\n\n  if isURL \"$WORLD\"; then\n    log \"Downloading world from $WORLD\"\n    mkdir -p /data/tmp\n    if ! get -o ${worldDownload} \"$WORLD\"; then\n      logError \"Failed to download world from $WORLD\"\n      exit 1\n    fi\n    WORLD=${worldDownload}\n  fi\n\n  if [ -f \"$WORLD\" ]; then\n    log \"Extracting world\"\n\n    # Stage contents so that the correct subdirectory can be picked off\n    mkdir -p ${tmpWorldData}\n    if ! extract \"$WORLD\" ${tmpWorldData}; then\n      logError \"Extracting world from $WORLD\"\n      exit 1\n    fi\n\n    baseDirs=$(find ${tmpWorldData} -name \"level.dat\" -exec dirname \"{}\" \\;)\n\n    if ! [[ $baseDirs ]]; then\n      logError \"World content is not valid since level.dat could not be found\"\n      exit 2\n    fi\n\n    count=$(echo \"$baseDirs\" | wc -l)\n    if [[ $count -gt 1 ]]; then\n      baseDirsNoSpigotSuffix=$(echo \"$baseDirs\" | sed -re 's:(_nether|_the_end)/?$::' | sort -u)\n      if [ \"$(echo \"$baseDirsNoSpigotSuffix\" | wc -l)\" -eq 1 ]; then\n        baseDir=\"$baseDirsNoSpigotSuffix\"\n        baseName=$(basename \"$baseDir\")\n        log \"Found Spigot naming conventions, taking $baseName as main dimension\"\n      else\n        baseDir=\"$(echo \"$baseDirs\" | sed -n ${WORLD_INDEX:-1}p)\"\n        baseName=$(basename \"$baseDir\")\n        logWarning \"Multiple levels found, picking: $baseName\"\n      fi\n    elif [[ $count -gt 0 ]]; then\n      baseDir=\"$baseDirs\"\n    else\n      logError \"Invalid world content\"\n      exit 1\n    fi\n\n    if [ -d \"${baseDir}_nether/DIM-1\" ]; then\n      if [ -d \"$baseDir/DIM-1\" ]; then\n        logWarning \"Found Nether dimension in both $baseDir and ${baseDir}_nether, picking ${baseDir}_nether\"\n        rm -r \"$baseDir/DIM-1\"\n      fi\n    fi\n    if [ -d \"${baseDir}_the_end/DIM1\" ]; then\n      if [ -d \"$baseDir/DIM1\" ]; then\n        logWarning \"Found End dimension in both $baseDir and ${baseDir}_the_end, picking ${baseDir}_the_end\"\n        rm -r \"$baseDir/DIM1\"\n      fi\n    fi\n\n    log \"Copying world...\"\n    rsync --remove-source-files --recursive --delete \"$baseDir/\" \"$worldDest\"\n\n    if [ \"$FAMILY\" = \"SPIGOT\" ]; then\n      if [ -d \"${baseDir}_nether\" ]; then\n        log \"Copying Spigot Nether...\"\n        rsync --remove-source-files --recursive --delete \"${baseDir}_nether/\" \"${worldDest}_nether\"\n      elif [ -d \"$worldDest/DIM-1\" ]; then\n        log \"Moving Nether to Spigot location...\"\n        mkdir -p \"${worldDest}_nether\"\n        mv -f \"$worldDest/DIM-1\" \"${worldDest}_nether/\"\n      fi\n      if [ -d \"${baseDir}_the_end\" ]; then\n        log \"Copying Spigot End...\"\n        rsync --remove-source-files --recursive --delete \"${baseDir}_the_end/\" \"${worldDest}_the_end\"\n      elif [ -d \"$worldDest/DIM1\" ]; then\n        log \"Moving End to Spigot location...\"\n        mkdir -p \"${worldDest}_the_end\"\n        mv -f \"$worldDest/DIM1\" \"${worldDest}_the_end/\"\n      fi\n    else\n      if [ -d \"${baseDir}_nether/DIM-1\" ]; then\n        log \"Copying Spigot Nether to vanilla location...\"\n        rsync --remove-source-files --recursive --delete \"${baseDir}_nether/DIM-1\" \"${worldDest}/\"\n      fi\n      if [ -d \"${baseDir}_the_end/DIM1\" ]; then\n        log \"Copying Spigot End to vanilla location...\"\n        rsync --remove-source-files --recursive --delete \"${baseDir}_the_end/DIM1\" \"${worldDest}/\"\n      fi\n    fi\n  elif [ -d \"$WORLD\" ]; then\n    log \"Cloning world directory from $WORLD ...\"\n    rsync --recursive --delete \"${WORLD%/}\"/ \"$worldDest\"\n  else\n    logError \"World file/directory $WORLD is missing\"\n    exit 1\n  fi\n\n  if [ \"$FAMILY\" = \"SPIGOT\" ]; then\n    # Reorganise if a Spigot server\n    log \"Moving End and Nether maps to Spigot location\"\n    [ -d \"$worldDest/DIM1\" ] && mv -f \"$worldDest/DIM1\" \"${worldDest}_the_end\"\n    [ -d \"$worldDest/DIM-1\" ] && mv -f \"$worldDest/DIM-1\" \"${worldDest}_nether\"\n  fi\nfi\n\n[[ -f ${worldDownload} ]] && rm ${worldDownload}\n[[ -d ${tmpWorldData} ]] && rm -rf ${tmpWorldData}\n\nexec \"$(dirname \"$0\")/start-setupDatapack\" \"$@\"\n"
  },
  {
    "path": "scripts/start-spiget",
    "content": "#!/bin/bash\nset -euo pipefail\nIFS=$'\\n\\t'\n\n# shellcheck source=start-utils\n. \"$(dirname \"$0\")/start-utils\"\nhandleDebugMode\n\n: \"${SPIGET_RESOURCES:=}\"\n: \"${SPIGET_DOWNLOAD_TOLERANCE:=5}\" # in minutes\n: \"${REMOVE_OLD_MODS:=false}\"\n: \"${REMOVE_OLD_MODS_EXCLUDE:=}\"\n: \"${REMOVE_OLD_MODS_INCLUDE:=*.jar,*-version.json}\"\n: \"${REMOVE_OLD_MODS_DEPTH:=1} \"\n\nacceptArgs=(--accept application/zip --accept application/java-archive --accept application/octet-stream)\n\ncontainsJars() {\n  file=${1?}\n\n  pat='\\.jar$'\n\n  while read -r line; do\n    if [[ $line =~ $pat ]]; then\n      return 0\n    fi\n  done < <(unzip -l \"$file\" | tail -n +4)\n\n  return 1\n}\n\ncontainsPlugin() {\n  file=${1?}\n\n  pat='plugin.yml$'\n\n  while read -r line; do\n    if [[ $line =~ $pat ]]; then\n      return 0\n    fi\n  done < <(unzip -l \"$file\" | tail -n +4)\n\n  return 1\n}\n\ngetResourceFromSpiget() {\n  resource=${1?}\n\n  log \"Downloading resource ${resource} ...\"\n\n  mkdir -p /data/plugins\n\n  versionfile=\"/data/plugins/.${resource}-version.json\"\n  versionfileNew=\"${versionfile}.tmp\"\n\n  if [ -f \"$versionfile\" ]; then\n    if [[ -n $(find \"$versionfile\" -mmin +${SPIGET_DOWNLOAD_TOLERANCE}) ]]; then\n      urlVersion=\"https://api.spiget.org/v2/resources/${resource}/versions/latest\"\n      if ! get -o \"${versionfileNew}\" \"${urlVersion}\"; then\n        logError \"Failed to download resource version meta data '${resource}' from ${urlVersion}\"\n        exit 2\n      fi\n\n      if isTrue \"${REMOVE_OLD_MODS:-false}\"; then\n        installedVersion=0.0.0\n      else\n        installedVersion=$(jq -r '.name' $versionfile)\n      fi\n      newVersion=$(jq -r '.name' $versionfileNew)\n      \n      if [ \"$installedVersion\" = \"$newVersion\" ]; then\n        log \"Resource '${resource}': installed version '${installedVersion}' already up to date\"\n        mv \"${versionfileNew}\" \"${versionfile}\"\n      else\n        if downloadResourceFromSpiget \"${resource}\"; then\n          mv \"${versionfileNew}\" \"${versionfile}\"\n        fi\n      fi\n    else\n      log \"resource '${resource}' not checked because version meta file newer than '${SPIGET_DOWNLOAD_TOLERANCE}' minutes\"\n    fi\n  else\n    if downloadResourceFromSpiget \"${resource}\"; then\n      urlVersion=\"https://api.spiget.org/v2/resources/${resource}/versions/latest\"\n      if ! get -o \"${versionfileNew}\" \"${urlVersion}\"; then\n        logError \"Failed to download resource version meta data '${resource}' from ${urlVersion}\"\n        exit 2\n      fi\n      mv \"${versionfileNew}\" \"${versionfile}\"\n    fi\n  fi\n\n}\n\ndownloadResourceFromSpiget() {\n  resource=${1?}\n\n  tempDir=\"/data/plugins/tmp-${resource}\"\n  mkdir -p $tempDir\n  resourceUrl=\"https://api.spiget.org/v2/resources/${resource}\"\n  if ! outfile=$(get_silent --output-filename -o $tempDir \"${acceptArgs[@]}\" \"${resourceUrl}/download\"); then\n    logError \"Failed to download resource '${resource}' from ${resourceUrl}/download\"\n    if externalUrl=$(get --json-path '$.file.externalUrl' \"${resourceUrl}\"); then\n      logError \"       Visit $externalUrl to pre-download the resource\"\n      logError \"       instead of using SPIGET_RESOURCES\"\n    fi\n    exit 1\n  fi\n\n  if ! fileType=$(get --json-path '.file.type' \"${resourceUrl}\"); then\n    logError \"Failed to retrieve file type of resource $resource\"\n    exit 1\n  fi\n  if [[ $fileType = .sk ]]; then\n    mkdir -p /data/plugins/Skript/scripts\n    mv \"$outfile\" /data/plugins/Skript/scripts\n  else\n    if containsPlugin \"${outfile}\"; then\n      log \"Moving resource ${resource} into plugins\"\n      mv \"$outfile\" /data/plugins\n    elif containsJars \"${outfile}\"; then\n      log \"Extracting contents of resource ${resource} into plugins\"\n      extract \"$outfile\" /data/plugins\n    else\n      logError \"File for resource ${resource} has an unexpected file type: ${fileType}\"\n      exit 2\n    fi\n  fi\n  rm -rf \"$tempDir\"\n}\n\nif [[ ${SPIGET_RESOURCES} ]]; then\n  if isTrue \"${REMOVE_OLD_MODS}\"; then\n    removeOldMods /data/plugins\n    REMOVE_OLD_MODS=false\n  fi\n\n  log \"Getting plugins via Spiget\"\n  IFS=',' read -r -a resources <<<\"${SPIGET_RESOURCES}\"\n  for resource in \"${resources[@]}\"; do\n    getResourceFromSpiget \"${resource}\"\n  done\nfi\n\nexec \"$(dirname \"$0\")/start-setupWorld\" \"$@\"\n"
  },
  {
    "path": "scripts/start-utils",
    "content": "#!/bin/bash\n\nfunction get_from_gh() {\n  if [[ \"${GH_TOKEN:-}\" ]]; then\n    # User has provided a Personal Access Token to mitigate rate-limiting issues\n    if [[ -z \"${oAuthScopes}\" ]]; then\n      oAuthScopes=$(curl -s -H \"Authorization: token $GH_TOKEN\" https://api.github.com/users/codertocat -I | grep x-oauth-scopes)\n    fi\n    if [[ ! \"$oAuthScopes\" =~ ^x-oauth-scopes:[[:space:]]*$ ]]; then\n      # Don't use what you don't have to...\n      logError \"GH_TOKEN has permissions it doesn't need. Recreate or update this personal access token and disable ALL scopes.\"\n      exit 1\n    else\n      curl -fsSL -H \"Authorization: token $GH_TOKEN\" \"${@:2}\" \"$1\"\n    fi\n  else\n    curl -fsSL \"${@:2}\" \"$1\"\n  fi\n}\n\nfunction applyResultsFile() {\n  # grab SERVER and export it\n  set -a\n  # shellcheck disable=SC1090\n  source \"$1\"\n  set +a\n}\n\nfunction join_by() {\n  local d=$1\n  shift\n  echo -n \"$1\"\n  shift\n  printf \"%s\" \"${@/#/$d}\"\n}\n\nfunction get_major_version() {\n  version=$1\n  echo \"$version\" | cut -d. -f 1-2\n}\n\nfunction isPercentage() {\n  local value=$1\n  \n  [[ $value =~ ^[0-9]+(\\.[0-9]+)?\\s*%$ ]]\n}\n\nfunction getPercentageValue() {\n  local value=$1\n  if [[ \"$value\" =~ ^([0-9]+(\\.[0-9]+)?)\\s*%$ ]]; then\n    echo \"${BASH_REMATCH[1]}\"\n    return 0\n  else\n    logError \"Value '$value' is not a valid percentage.\"\n    return 1\n  fi\n}\n\nfunction isURL() {\n  local value=$1\n\n  [[ $value =~ ^(https?|ftp):// ]]\n}\n\nfunction isValidFileURL() {\n  suffix=${1:?Missing required suffix arg}\n  url=${2:?Missing required url arg}\n\n  [[ \"$url\" =~ ^http.*://.*\\.${suffix}(\\?.*)?$ ]]\n}\n\nfunction resolveEffectiveUrl() {\n  url=\"${1:?Missing required url argument}\"\n  if ! curl -Ls -o /dev/null -w \"%{url_effective}\" \"$url\"; then\n    logError \"Failed to resolve effective URL from $url\"\n    exit 2\n  fi\n}\n\nfunction getFilenameFromUrl() {\n  url=\"${1:?Missing required url argument}\"\n  strippedOfQuery=\"${url%\\?*}\"\n  basename \"$strippedOfQuery\"\n}\n\nfunction isTrue() {\n  case \"${1,,}\" in\n  true | yes | on | 1)\n    return 0\n    ;;\n  *)\n    return 1\n    ;;\n  esac\n}\n\nfunction isFalse() {\n  case \"${1,,}\" in\n  false | no | off | 0)\n    return 0\n    ;;\n  *)\n    return 1\n    ;;\n  esac\n}\n\nfunction isDebugging() {\n  isTrue \"${DEBUG:-false}\"\n}\n\nfunction handleDebugMode() {\n  if isDebugging; then\n    set -x\n  fi\n}\n\nfunction debug() {\n  if isDebugging; then\n    log \"DEBUG: $*\"\n  fi\n}\n\nfunction logn() {\n  echo -n \"[init] $*\"\n}\n\n# Use as\n# cmd | applyLogPrefix\nfunction applyLogPrefix() {\n  sed 's/^/[init] /'\n}\n\nfunction log() {\n  local oldState\n  # The  return  status  when listing options is zero if all optnames are enabled, non- zero otherwise.\n  oldState=$(shopt -po xtrace || true)\n  shopt -u -o xtrace\n  ts=\n  if isDebugging || isTrue \"${LOG_TIMESTAMP:-false}\"; then\n    ts=\" $(date --rfc-3339=seconds)\"\n  fi\n  echo -e \"[init]${ts} $*\"\n  eval \"$oldState\"\n}\n\n# Refer to https://unix.stackexchange.com/a/10065/102376\nfunction isTerminal() {\n  if test -t 1; then\n    # see if it supports colors...\n    ncolors=$(tput colors)\n    test -n \"$ncolors\" && test \"$ncolors\" -ge 8\n  else\n    return 1\n  fi\n}\n\nerrorLogTag=\"[ERROR]\"\nwarningLogTag=\"[WARN]\"\n\nif isTerminal; then\n  normal=\"$(tput sgr0)\"\n  red=\"$(tput setaf 1)\"\n  yellow=\"$(tput setaf 3)\"\n  function getErrorColoredLogString() {\n    echo \"${red}$errorLogTag $* ${normal}\"\n  }\n  function getWarningColoredLogString() {\n    echo \"${yellow}$warningLogTag $* ${normal}\"\n  }\nelse\n  function getErrorColoredLogString() {\n    echo \"$errorLogTag $*\"\n  }\n  function getWarningColoredLogString() {\n    echo \"$warningLogTag $*\"\n  }\nfi\n\nfunction error() {\n  echo -e \"$(getErrorColoredLogString \"$*\")\"\n}\n\nfunction logError() {\n  if isDebugging; then\n    set +x\n  fi\n  log \"$(getErrorColoredLogString \"$*\")\"\n  if isDebugging; then\n    set -x\n  fi\n}\n\nfunction warning() {\n  if isDebugging; then\n    set +x\n  fi\n  echo -e \"$(getWarningColoredLogString \"$*\")\"\n  if isDebugging; then\n    set -x\n  fi\n}\n\nfunction logWarning() {\n  log \"$(getWarningColoredLogString \"$*\")\"\n}\n\nfunction isNumeric() {\n  [[ $1 =~ ^[0-9]+$ ]]\n}\n\nfunction isNumericElseSetToDefault() {\n  local var_name=\"$1\"\n  local default_value=\"$2\"\n  \n  if ! isNumeric ${!var_name} ; then\n    eval \"$var_name=$default_value\"\n    export \"$var_name\"\n    logWarning \"$var_name is not numeric, set to $default_value (seconds)\"\n  fi\n}\n\nfunction checkIfNotZeroElseSetToDefault() {\n  local var_name=\"$1\"\n  local default_value=\"$2\"\n  if [ \"${!var_name}\" -eq \"0\" ] ; then\n    eval \"$var_name=$default_value\"\n    export \"$var_name\"\n    logWarning \"$var_name must not be 0, set to $default_value (seconds)\"\n  fi\n}\n\nfunction logAutopause() {\n  echo \"[Autopause loop] $*\"\n}\n\nfunction logAutopauseAction() {\n  echo \"[$(date -Iseconds)] [Autopause] $*\"\n}\n\nfunction logAutostop() {\n  echo \"[Autostop loop] $*\"\n}\n\nfunction logAutostopAction() {\n  echo \"[$(date -Iseconds)] [Autostop] $*\"\n}\n\nfunction logRcon() {\n  echo \"[Rcon loop] $*\"\n}\n\nfunction normalizeMemSize() {\n  local scale=1\n  local mem=1\n  case ${1,,} in\n  *k)\n    scale=1024\n    ;;\n  *m)\n    scale=1048576\n    ;;\n  *g)\n    scale=1073741824\n    ;;\n  *%)\n    # Get system memory\n    if [ -f \"/sys/fs/cgroup/memory/memory.limit_in_bytes\" ]; then\n    mem=$( cat /sys/fs/cgroup/memory/memory.limit_in_bytes )\n    elif [ -f \"/sys/fs/cgroup/memory.max\" ]; then\n    mem=$( cat /sys/fs/cgroup/memory.max )\n    fi\n    # Scale is used to transform percentages into decimals (eg. 60 -> 0.6)\n    scale=0.01\n    ;;\n  esac\n\n  val=${1:0:-1}\n  echo $((val * scale * mem))\n}\n\nfunction compare_version() {\n  local left_version=$1\n  local comparison=$2\n  local right_version=$3\n\n  if [[ -z \"$left_version\" ]]; then\n    echo \"Left version is required\"\n    return 1\n  fi\n\n  if [[ -z \"$right_version\" ]]; then\n    echo \"Right version is required\"\n    return 1\n  fi\n\n  # Handle version channels ('a', 'b', or numeric)\n  if [[ $left_version == a* ]]; then\n    left_version=${left_version:1}\n    left_version_channel=1\n  elif [[ $left_version == b* ]]; then\n    left_version=${left_version:1}\n    left_version_channel=2\n  else \n    left_version_channel=3    \n  fi\n  if [[ $right_version == a* ]]; then\n    right_version=${right_version:1}\n    right_version_channel=1\n  elif [[ $right_version == b* ]]; then\n    right_version=${right_version:1}\n    right_version_channel=2\n  else \n    right_version_channel=3    \n  fi\n\n  if [[ $comparison == \"lt\" && $left_version_channel < $right_version_channel ]]; then\n      return 0\n  elif [[ $comparison == \"lt\" && $left_version_channel > $right_version_channel ]]; then\n      return 1\n  elif [[ $comparison == \"gt\" && $left_version_channel > $right_version_channel ]]; then\n      return 0\n  elif [[ $comparison == \"gt\" && $left_version_channel < $right_version_channel ]]; then\n      return 1\n  elif [[ $comparison == \"le\" && $left_version_channel < $right_version_channel ]]; then\n      return 0\n  elif [[ $comparison == \"le\" && $left_version_channel == $right_version_channel ]]; then\n      return 0\n  elif [[ $comparison == \"ge\" && $left_version_channel > $right_version_channel ]]; then\n      return 0\n  elif [[ $comparison == \"ge\" && $left_version_channel == $right_version_channel ]]; then\n      return 0\n  elif [[ $comparison == \"eq\" && $left_version_channel == $right_version_channel ]]; then\n      return 0\n  fi\n\n# Compare the versions using sort -V\n  local result=1\n\n  case $comparison in\n      \"lt\")\n          if [[ $(echo -e \"$left_version\\n$right_version\" | sort -V | head -n1) == \"$left_version\" && \"$left_version\" != \"$right_version\" ]]; then\n              result=0\n          fi\n          ;;\n      \"le\")\n          if [[ $(echo -e \"$left_version\\n$right_version\" | sort -V | head -n1) == \"$left_version\" ]]; then\n              result=0\n          fi\n          ;;\n      \"eq\")\n          if [[ \"$left_version\" == \"$right_version\" ]]; then\n              result=0\n          fi\n          ;;\n      \"ge\")\n          if [[ $(echo -e \"$left_version\\n$right_version\" | sort -V | tail -n1) == \"$left_version\" ]]; then\n              result=0\n          fi\n          ;;\n      \"gt\")\n          if [[ $(echo -e \"$left_version\\n$right_version\" | sort -V | tail -n1) == \"$left_version\" && \"$left_version\" != \"$right_version\" ]]; then\n              result=0\n          fi\n          ;;\n      *)\n          echo \"Unsupported comparison operator: $comparison\"\n          return 1\n          ;;\n  esac\n\n  return $result\n}\n\nfunction versionLessThan() {\n  local oldState\n  # The  return  status  when listing options is zero if all optnames are enabled, non- zero otherwise.\n  oldState=$(shopt -po xtrace || true)\n  shopt -u -o xtrace\n\n  eval \"$oldState\"\n\n  # Verify strict mode because it might be enabled\n  compare_version \"${VERSION}\" \"lt\" \"${1?}\"\n}\n\nfunction writeEula() {\n  if ! echo \"# Generated via Docker\n# $(date)\neula=${EULA,,}\n\" >/data/eula.txt; then\n    logError \"Unable to write eula to /data. Please make sure attached directory is writable by uid=${UID}\"\n    exit 2\n  fi\n}\n\nfunction removeOldMods {\n  if [ -d \"$1\" ]; then\n    log \"Removing old mods including='${REMOVE_OLD_MODS_INCLUDE}' excluding='${REMOVE_OLD_MODS_EXCLUDE}' up to depth=${REMOVE_OLD_MODS_DEPTH}\"\n    args=(\n      --delete\n      --type file\n      --min-depth=1 --max-depth \"${REMOVE_OLD_MODS_DEPTH}\"\n      --name \"${REMOVE_OLD_MODS_INCLUDE}\"\n      --exclude-name \"${REMOVE_OLD_MODS_EXCLUDE}\"\n    )\n    if ! isDebugging; then\n      args+=(--quiet)\n    fi\n    mc-image-helper find \"${args[@]}\" \"$1\"\n  fi\n}\n\nfunction get() {\n  mc-image-helper get \"$@\"\n}\n\nfunction get_silent() {\n  local flags=(-s)\n  if isTrue \"${DEBUG_GET:-false}\"; then\n    flags+=(\"--debug\")\n  fi\n  mc-image-helper \"${flags[@]}\" get \"$@\"\n}\n\nfunction isFamily() {\n  for f in \"${@}\"; do\n    if [[ ${FAMILY^^} == \"${f^^}\" ]]; then\n      return 0\n    fi\n  done\n  return 1\n}\n\nfunction isType() {\n  for t in \"${@}\"; do\n    # shellcheck disable=SC2153\n    if [[ ${TYPE^^} == \"${t^^}\" ]]; then\n      return 0\n    fi\n  done\n  return 1\n}\n\nfunction extract() {\n  src=${1?}\n  destDir=${2?}\n\n  type=$(file -b --mime-type \"${src}\")\n  case \"${type}\" in\n  application/zip)\n    unzip -o -q -d \"${destDir}\" \"${src}\"\n    ;;\n  application/x-tar | application/gzip | application/x-gzip | application/x-bzip2)\n    tar -C \"${destDir}\" -xf \"${src}\"\n    ;;\n  application/zstd | application/x-zstd)\n    tar -C \"${destDir}\" --use-compress-program=unzstd -xf \"${src}\"\n    ;;\n  *)\n    logError \"Unsupported archive type: $type\"\n    return 1\n    ;;\n  esac\n}\n\nfunction getDistro() {\n  grep -E \"^ID=\" /etc/os-release | cut -d= -f2 | sed -e 's/\"//g'\n}\n\nfunction checkSum() {\n  local sum_file=${1?}\n\n  # Get distro\n  distro=$(getDistro)\n\n  case \"${distro}\" in\n  debian | ubuntu | ol)\n    sha1sum -c \"${sum_file}\" --status 2>/dev/null && return 0\n    ;;\n  alpine)\n    sha1sum -c \"${sum_file}\" -s 2>/dev/null && return 0\n    ;;\n  *)\n    return 1\n    ;;\n  esac\n}\n\nfunction usesMods() {\n  if isTrue \"${USES_MODS:-}\"; then\n    return 0\n\n  else\n    case \"$FAMILY\" in\n    FORGE | FABRIC | HYBRID | SPONGE)\n      return 0\n      ;;\n    esac\n    return 1\n\n  fi\n}\n\nfunction usesPlugins() {\n  if isTrue \"${USES_PLUGINS:-}\"; then\n    return 0\n\n  else\n    case \"$FAMILY\" in\n    SPIGOT | HYBRID)\n      return 0\n      ;;\n    esac\n    return 1\n\n  fi\n}\n\nfunction resolveVersion() {\n  givenVersion=\"$VERSION\"\n  # shellcheck disable=SC2153\n  if ! VERSION=$(mc-image-helper resolve-minecraft-version \"$VERSION\"); then\n    exit 2\n  fi\n  log \"Resolved version given ${givenVersion} into ${VERSION}\"\n}\n\nfunction resolveFamily() {\n  case \"$TYPE\" in\n  PAPER | SPIGOT | BUKKIT | CANYON | PUFFERFISH | PURPUR)\n    FAMILY=SPIGOT\n    ;;\n  FORGE)\n    FAMILY=FORGE\n    ;;\n  FABRIC | QUILT)\n    FAMILY=FABRIC\n    ;;\n  esac\n  export FAMILY\n}\n\nfunction ensureRemoveAllModsOff() {\n  reason=${1?}\n\n  if isTrue \"${REMOVE_OLD_MODS:-false}\"; then\n    logWarning \"Using REMOVE_OLD_MODS interferes with $reason -- it is now disabled\"\n    REMOVE_OLD_MODS=false\n  fi\n}\n\nfunction buildDownloadList() {\n  repoUrl=${1?}\n  version=${2?}\n  shift 2\n  result=\n  for c in \"${@}\"; do\n    if [[ $result ]]; then\n      result+=\",\"\n    fi\n    result+=\"${repoUrl}/${version}/$c\"\n  done\n  echo \"$result\"\n}\n\nfunction firstArrayElement {\n  local -n a=\"$1\"\n  if (( ${#a[@]} )); then\n    echo \"${a[0]}\"\n  fi\n}\n\nfunction shiftArray {\n  local -n a=\"$1\"\n  if (( ${#a[@]} )); then\n    a=(\"${a[@]:1}\")\n  fi\n}\n"
  },
  {
    "path": "tests/.gitignore",
    "content": "data/\ncf_api_key.secret"
  },
  {
    "path": "tests/fulltests/multi-part-motd/docker-compose.yml",
    "content": "services:\n  monitor:\n    depends_on:\n      mc:\n        condition: service_started\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    entrypoint: mc-monitor\n    command: status --host mc --retry-interval 1s --timeout 1s --retry-limit 60\n    restart: no\n  mc:\n    restart: no\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      VERSION: ${MINECRAFT_VERSION:-LATEST}\n      TYPE: PAPER\n      # regression tests https://github.com/itzg/docker-minecraft-server/issues/2545\n      MOTD: \"Foo§rBar\"\n"
  },
  {
    "path": "tests/fulltests/test.sh",
    "content": "#!/bin/bash\n\n# go to script root directory\ncd \"$(dirname \"$0\")\" || exit 1\n\n# tests to completely spin up Minecraft and use the monitor to validate the service is running.\nfullMinecraftUpTest(){\n  file=\"$1\"\n  result=0\n\n  echo \"Testing with images:\"\n  docker compose -f \"$file\" config --images\n\n  # run the monitor to validate the Minecraft image is healthy\n  upArgs=(\n    --attach-dependencies\n    --always-recreate-deps\n    --abort-on-container-failure\n  )\n  if ! docker compose -f \"$file\" up \"${upArgs[@]}\" monitor; then\n    echo \"$(dirname \"$file\") Result: failed\"\n    result=1\n  else\n    echo \"$(dirname \"$file\") Result: success\"\n  fi\n  docker compose -f \"$file\" down -v --remove-orphans\n  return $result\n}\n\n# go through each folder in fulltests and run fullbuilds\nfiles=$(ls */docker-compose.yml)\nfor file in $files; do\n\n    echo \"Starting Tests on $(dirname \"$file\")\"\n    if ! fullMinecraftUpTest \"$file\"; then\n      exit 2\n    fi\n\ndone\n"
  },
  {
    "path": "tests/fulltests/vanilla-latest/docker-compose.yml",
    "content": "services:\n  monitor:\n    depends_on:\n      mc:\n        condition: service_started\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    entrypoint: mc-monitor\n    command: status --host mc --retry-interval 1s --timeout 1s --retry-limit 300\n    restart: no\n  mc:\n    restart: no\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      VERSION: ${MINECRAFT_VERSION:-LATEST}\n\n"
  },
  {
    "path": "tests/setuponlytests/auto_curseforge/docker-compose.yml",
    "content": "services:\n  mc:\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"true\"\n      SETUP_ONLY: \"TRUE\"\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\n      CF_API_KEY: ${CF_API_KEY}\n      CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/the-pixelmon-modpack/files/5954570\n      DEBUG: true\n    volumes:\n      - ./data:/data\n\n"
  },
  {
    "path": "tests/setuponlytests/auto_curseforge/require.sh",
    "content": "[[ $CF_API_KEY ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/auto_curseforge/verify.sh",
    "content": "mc-image-helper assert fileExists \"/data/mods/ExplorersCompass-1.16.5-1.1.2-forge.jar\"\nmc-image-helper assert fileExists \"/data/forge-1.16.5-36.2.34.jar\"\n"
  },
  {
    "path": "tests/setuponlytests/auto_curseforge_file/docker-compose.yml",
    "content": "services:\n  mc:\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"true\"\n      SETUP_ONLY: \"TRUE\"\n      MODPACK_PLATFORM: AUTO_CURSEFORGE\n      CF_API_KEY_FILE: /run/secrets/cf_api_key\n      CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/the-pixelmon-modpack/files/5954570\n      DEBUG: true\n    volumes:\n      - ./data:/data\n    secrets:\n      - cf_api_key\nsecrets:\n  cf_api_key:\n    file: cf_api_key.secret\n\n"
  },
  {
    "path": "tests/setuponlytests/auto_curseforge_file/require.sh",
    "content": "[[ -n \"$CF_API_KEY\" ]] || exit 1\necho \"$CF_API_KEY\" > cf_api_key.secret || exit 1"
  },
  {
    "path": "tests/setuponlytests/auto_curseforge_file/verify.sh",
    "content": "mc-image-helper assert fileExists \"/data/mods/ExplorersCompass-1.16.5-1.1.2-forge.jar\"\nmc-image-helper assert fileExists \"/data/forge-1.16.5-36.2.34.jar\"\n"
  },
  {
    "path": "tests/setuponlytests/cf_files/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      TYPE: FABRIC\n      VERSION: 1.20.1\n      # Contains mix of Forge and Fabric mods\n      CURSEFORGE_FILES: |\n        https://www.curseforge.com/minecraft/mc-mods/clumps/files/4153343\n        jei\n        306612\n        @/extras/listing.txt\n      CF_API_KEY: ${CF_API_KEY}\n    volumes:\n      - ./data:/data\n      - ./fake.jar:/servers/fake.jar:ro\n      - ./listing.txt:/extras/listing.txt:ro\n"
  },
  {
    "path": "tests/setuponlytests/cf_files/fake.jar",
    "content": ""
  },
  {
    "path": "tests/setuponlytests/cf_files/listing.txt",
    "content": "architectury-api@9.2.14\n"
  },
  {
    "path": "tests/setuponlytests/cf_files/require.sh",
    "content": "[[ $CF_API_KEY ]] || exit 1\n"
  },
  {
    "path": "tests/setuponlytests/cf_files/verify.sh",
    "content": "mc-image-helper assert fileExists \"/data/mods/architectury-*-fabric.jar\"\nmc-image-helper assert fileExists \"/data/mods/Clumps-fabric-*.jar\"\nmc-image-helper assert fileExists \"/data/mods/fabric-api-*.jar\"\nmc-image-helper assert fileExists \"/data/mods/jei-*-fabric-*.jar\"\n\n"
  },
  {
    "path": "tests/setuponlytests/conflicting_world_for_spigot_server/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      VERSION: ${MINECRAFT_VERSION:-LATEST}\n      TYPE: \"PAPER\"\n      WORLD: /worlds/world-for-testing.zip\n    volumes:\n      - ./worlds:/worlds:ro\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/conflicting_world_for_spigot_server/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/conflicting_world_for_spigot_server/verify.sh",
    "content": "mc-image-helper assert fileExists \\\n  world/level.dat \\\n  world_nether/DIM-1/some_spigot_nether_file \\\n  world_the_end/DIM1/some_spigot_end_file\nmc-image-helper assert fileNotExists \\\n  world_nether/DIM-1/some_vanilla_nether_file \\\n  world_the_end/DIM1/some_vanilla_end_file\n"
  },
  {
    "path": "tests/setuponlytests/conflicting_world_for_vanilla_server/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      WORLD: /worlds/world-for-testing.zip\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./worlds:/worlds:ro\n      - ./data:/data\n      # the following are only used to speed up test execution\n      - ./verify.sh:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/conflicting_world_for_vanilla_server/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/conflicting_world_for_vanilla_server/verify.sh",
    "content": "mc-image-helper assert fileExists world/level.dat && \\\nmc-image-helper assert fileExists world/DIM-1/some_spigot_nether_file && \\\nmc-image-helper assert fileExists world/DIM1/some_spigot_end_file && \\\n! mc-image-helper assert fileExists world/DIM-1/some_vanilla_nether_file && \\\n! mc-image-helper assert fileExists world/DIM1/some_vanilla_end_file\n"
  },
  {
    "path": "tests/setuponlytests/curseforge/docker-compose.yml",
    "content": "services:\n  mc:\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server:java8-multiarch}\n    environment:\n      EULA: \"true\"\n      SETUP_ONLY: \"TRUE\"\n      TYPE: CURSEFORGE\n      CF_SERVER_MOD: /modpacks/pack.zip\n      INIT_MEMORY: 2G\n      MAX_MEMORY: 6G\n    volumes:\n      - ./data:/data\n      - ./modpacks:/modpacks:ro\n\n"
  },
  {
    "path": "tests/setuponlytests/curseforge/require.sh",
    "content": "[[ $VARIANT == java8* ]] || exit 1\n"
  },
  {
    "path": "tests/setuponlytests/curseforge/verify.sh",
    "content": "mc-image-helper assert fileExists \"/data/FeedTheBeast/forge-installer.jar\"\nmc-image-helper assert fileExists \"/data/FeedTheBeast/forge.jar\"\n"
  },
  {
    "path": "tests/setuponlytests/defaults/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n    volumes:\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/defaults/verify.sh",
    "content": "mc-image-helper assert propertyEquals --file=server.properties --property=rcon.port --expect=25575\nmc-image-helper assert propertyEquals --file=server.properties --property=enable-rcon --expect=true\n"
  },
  {
    "path": "tests/setuponlytests/generic-packs/docker-compose.yml",
    "content": "services:\n  web:\n    image: nginx\n    volumes:\n      - ./web:/usr/share/nginx/html\n    healthcheck:\n      test: [\"CMD\", \"curl\", \"--fail\", \"http://localhost/configs.zip\"]\n      interval: 3s\n      timeout: 5s\n      retries: 3\n  mc:\n    depends_on:\n      web:\n        condition: service_healthy\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"true\"\n      SETUP_ONLY: \"true\"\n      GENERIC_PACKS: http://web/configs.zip,/packs/testing.zip\n      LOG_TIMESTAMP: \"true\"\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n      DEBUG: \"true\"\n    volumes:\n      - ./packs:/packs\n      - ./data:/data\n      - ./fake.jar:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/generic-packs/fake.jar",
    "content": ""
  },
  {
    "path": "tests/setuponlytests/generic-packs/verify.sh",
    "content": "mc-image-helper assert fileExists one.txt mods/two.txt\nmc-image-helper assert fileExists config/opt.yml"
  },
  {
    "path": "tests/setuponlytests/generic-packs-prefix/docker-compose.yml",
    "content": "services:\n  mc:\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"true\"\n      SETUP_ONLY: \"TRUE\"\n      GENERIC_PACKS: testing\n      GENERIC_PACKS_PREFIX: /packs/\n      GENERIC_PACKS_SUFFIX: .zip\n      DEBUG: \"true\"\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./packs:/packs\n      - ./data:/data\n      # the following are only used to speed up test execution\n      - ./verify.sh:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/generic-packs-prefix/verify.sh",
    "content": "mc-image-helper assert fileExists one.txt mods/two.txt\n"
  },
  {
    "path": "tests/setuponlytests/icon-file-exact/docker-compose.yml",
    "content": "services:\n  mc:\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"true\"\n      SETUP_ONLY: \"true\"\n      ICON: /extra/4737386_minecraft_squircle_icon_64x64.png\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./data:/data\n      - ./extra:/extra\n      - ./fake.jar:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/icon-file-exact/fake.jar",
    "content": ""
  },
  {
    "path": "tests/setuponlytests/icon-file-exact/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/icon-file-exact/verify.sh",
    "content": "mc-image-helper assert fileExists server-icon.png"
  },
  {
    "path": "tests/setuponlytests/icon-file-scale/docker-compose.yml",
    "content": "services:\n  mc:\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"true\"\n      SETUP_ONLY: \"true\"\n      ICON: /extra/4737386_minecraft_squircle_icon.png\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./data:/data\n      - ./extra:/extra\n      - ./fake.jar:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/icon-file-scale/fake.jar",
    "content": ""
  },
  {
    "path": "tests/setuponlytests/icon-file-scale/verify.sh",
    "content": "mc-image-helper assert fileExists server-icon.png"
  },
  {
    "path": "tests/setuponlytests/icon-gif-multiframe/docker-compose.yml",
    "content": "services:\n  web:\n    image: nginx\n    volumes:\n      - ./web:/usr/share/nginx/html\n    healthcheck:\n      test:\n        [\"CMD\", \"curl\", \"--fail\", \"http://localhost/motion-tween-example.gif\"]\n      interval: 3s\n      timeout: 5s\n      retries: 3\n  mc:\n    depends_on:\n      web:\n        condition: service_healthy\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"true\"\n      SETUP_ONLY: \"true\"\n      ICON: http://web/motion-tween-example.gif\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./data:/data\n      - ./fake.jar:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/icon-gif-multiframe/fake.jar",
    "content": ""
  },
  {
    "path": "tests/setuponlytests/icon-gif-multiframe/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/icon-gif-multiframe/verify.sh",
    "content": "mc-image-helper assert fileExists server-icon.png"
  },
  {
    "path": "tests/setuponlytests/icon-png-atscale/docker-compose.yml",
    "content": "services:\n  web:\n    image: nginx\n    volumes:\n      - ./web:/usr/share/nginx/html\n    healthcheck:\n      test:\n        [\n          \"CMD\",\n          \"curl\",\n          \"--fail\",\n          \"http://localhost/4737386_minecraft_squircle_icon.png\",\n        ]\n      interval: 3s\n      timeout: 5s\n      retries: 3\n  mc:\n    depends_on:\n      web:\n        condition: service_healthy\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"true\"\n      SETUP_ONLY: \"true\"\n      ICON: http://web/4737386_minecraft_squircle_icon.png\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./data:/data\n      - ./fake.jar:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/icon-png-atscale/fake.jar",
    "content": ""
  },
  {
    "path": "tests/setuponlytests/icon-png-atscale/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/icon-png-atscale/verify.sh",
    "content": "mc-image-helper assert fileExists server-icon.png"
  },
  {
    "path": "tests/setuponlytests/icon-png-scale/docker-compose.yml",
    "content": "services:\n  web:\n    image: nginx\n    volumes:\n      - ./web:/usr/share/nginx/html\n    healthcheck:\n      test:\n        [\n          \"CMD\",\n          \"curl\",\n          \"--fail\",\n          \"http://localhost/4737386_minecraft_squircle_icon.png\",\n        ]\n      interval: 3s\n      timeout: 5s\n      retries: 3\n  mc:\n    depends_on:\n      web:\n        condition: service_healthy\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"true\"\n      SETUP_ONLY: \"true\"\n      ICON: http://web/4737386_minecraft_squircle_icon.png\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./data:/data\n      - ./fake.jar:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/icon-png-scale/fake.jar",
    "content": ""
  },
  {
    "path": "tests/setuponlytests/icon-png-scale/verify.sh",
    "content": "mc-image-helper assert fileExists server-icon.png"
  },
  {
    "path": "tests/setuponlytests/modrinth/docker-compose.yml",
    "content": "services:\n  mc:\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"true\"\n      SETUP_ONLY: \"true\"\n      TYPE: FABRIC\n      VERSION: 1.21.4\n      MODRINTH_PROJECTS: fabric-api,cloth-config\n    volumes:\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/modrinth/verify.sh",
    "content": "mc-image-helper assert fileExists \"mods/cloth-config-*.jar\" \"mods/fabric-api-*.jar\""
  },
  {
    "path": "tests/setuponlytests/mounts-custom/custom/config/test.json",
    "content": "{}"
  },
  {
    "path": "tests/setuponlytests/mounts-custom/custom/mods/mod.jar",
    "content": ""
  },
  {
    "path": "tests/setuponlytests/mounts-custom/custom/plugins/plugin.jar",
    "content": ""
  },
  {
    "path": "tests/setuponlytests/mounts-custom/docker-compose.yml",
    "content": "services:\n  mc:\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"true\"\n      SETUP_ONLY: \"true\"\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n      COPY_PLUGINS_SRC: /custom/plugins\n      COPY_PLUGINS_DEST: /data/custom-plugins\n      COPY_MODS_SRC: /custom/mods\n      COPY_MODS_DEST: /data/custom-mods\n      COPY_CONFIG_SRC: /custom/config\n      COPY_CONFIG_DEST: /data/custom-config\n    volumes:\n      - ./data:/data\n      - ./custom:/custom\n      - ./fake.jar:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/mounts-custom/fake.jar",
    "content": ""
  },
  {
    "path": "tests/setuponlytests/mounts-custom/verify.sh",
    "content": "mc-image-helper assert fileExists custom-plugins/plugin.jar\nmc-image-helper assert fileExists custom-mods/mod.jar\nmc-image-helper assert fileExists custom-config/test.json\n"
  },
  {
    "path": "tests/setuponlytests/nanolimbo/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      TYPE: NANOLIMBO\n    volumes:\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/nanolimbo/verify.sh",
    "content": "mc-image-helper assert fileExists \"/data/NanoLimbo*.jar\"\nmc-image-helper assert fileExists \"/data/settings.yml\"\n"
  },
  {
    "path": "tests/setuponlytests/ops_from_scratch/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      OPS: itzg\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./data:/data\n      # the following are only used to speed up test execution\n      - ./verify.sh:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/ops_from_scratch/verify.sh",
    "content": "mc-image-helper assert jsonPathEquals --file=ops.json --path='$[0].name' --expect=itzg\nmc-image-helper assert jsonPathEquals --file=ops.json --path='$[0].uuid' --expect=5cddfd26-fc86-4981-b52e-c42bb10bfdef\nmc-image-helper assert jsonPathEquals --file=ops.json --path='$[0].level' --expect=4"
  },
  {
    "path": "tests/setuponlytests/packwiz/docker-compose.yml",
    "content": "services:\n  web:\n    image: nginx\n    volumes:\n      - ./web:/usr/share/nginx/html\n    healthcheck:\n      test: [\"CMD\", \"curl\", \"--fail\", \"http://localhost/pack.toml\"]\n      interval: 3s\n      timeout: 5s\n      retries: 3\n  mc:\n    depends_on:\n      web:\n        condition: service_healthy\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"true\"\n      PACKWIZ_URL: http://web/pack.toml\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.19\n      DEBUG_HELPER: \"true\"\n    volumes:\n      - ./data:/data\n      - ./fake.jar:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/packwiz/fake.jar",
    "content": ""
  },
  {
    "path": "tests/setuponlytests/packwiz/require.sh",
    "content": "# maven.packwiz.infra.link is not resolvable\nexit 1"
  },
  {
    "path": "tests/setuponlytests/packwiz/verify.sh",
    "content": "mc-image-helper assert fileExists mods/architectury-5.7.28-fabric.jar\n"
  },
  {
    "path": "tests/setuponlytests/packwiz/web/index.toml",
    "content": "hash-format = \"sha256\"\r\n\r\n[[files]]\r\nfile = \"mods/architectury-api.pw.toml\"\r\nhash = \"c20179449fff711afb96ba0eadd3328fbf1aae639082d25d77c9080837685b79\"\r\nmetafile = true"
  },
  {
    "path": "tests/setuponlytests/packwiz/web/mods/architectury-api.pw.toml",
    "content": "name = \"Architectury API\"\nfilename = \"architectury-5.7.28-fabric.jar\"\nside = \"both\"\n\n[download]\nurl = \"https://cdn.modrinth.com/data/lhGA9TYQ/versions/5.7.28+fabric/architectury-5.7.28-fabric.jar\"\nhash-format = \"sha1\"\nhash = \"aa38ae9cc2e978e4ec87ff891f7b02ea0c0ee1b8\"\n\n[update]\n[update.modrinth]\nmod-id = \"lhGA9TYQ\"\nversion = \"Hf0Bau1j\""
  },
  {
    "path": "tests/setuponlytests/packwiz/web/pack.toml",
    "content": "name = \"Vanillia Server\"\r\nauthor = \"itzg\"\r\nversion = \"2.0.0\"\r\npack-format = \"packwiz:1.1.0\"\r\n\r\n[index]\r\nfile = \"index.toml\"\r\nhash-format = \"sha256\"\r\nhash = \"1a27b406c3fb6d35167fe659384ab528a6b3f8a66e6c05d593058e646aec591f\"\r\n\r\n[versions]\r\nfabric = \"0.14.8\"\r\nminecraft = \"1.19\""
  },
  {
    "path": "tests/setuponlytests/pufferfish/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      TYPE: PUFFERFISH\n      VERSION: ${MINECRAFT_VERSION:-LATEST}\n    volumes:\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/pufferfish/require.sh",
    "content": "[[ $MINECRAFT_VERSION == LATEST ]] || exit 1\n"
  },
  {
    "path": "tests/setuponlytests/pufferfish/verify.sh",
    "content": "mc-image-helper assert fileExists \"/data/pufferfish-*.jar\"\n"
  },
  {
    "path": "tests/setuponlytests/quilt/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      TYPE: QUILT\n      VERSION: ${MINECRAFT_VERSION:-LATEST}\n    volumes:\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/quilt/require.sh",
    "content": "[[ $MINECRAFT_VERSION == LATEST ]] || exit 1\n"
  },
  {
    "path": "tests/setuponlytests/quilt/verify.sh",
    "content": "mc-image-helper assert fileExists \"/data/quilt-server-*-launch.jar\"\n"
  },
  {
    "path": "tests/setuponlytests/spiget/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      TYPE: PAPER\n      # jar doesn't need to exist for setuponly tests\n      PAPER_CUSTOM_JAR: /servers/fake.jar\n      SPIGET_RESOURCES: \"34315,3836,6245,2124\"\n    volumes:\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/spiget/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/spiget/verify.sh",
    "content": "mc-image-helper assert fileExists plugins/3836.jar\nmc-image-helper assert fileExists plugins/34315.jar\nmc-image-helper assert fileExists plugins/6245.jar\nmc-image-helper assert fileExists plugins/SkinsRestorer.jar"
  },
  {
    "path": "tests/setuponlytests/spigot_world_for_spigot_server/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      TYPE: \"PAPER\"\n      WORLD: /worlds/world-for-testing.zip\n      # the following are only used to speed up test execution\n      VERSION: 1.18.1\n      PAPER_CUSTOM_JAR: /servers/fake.jar\n    volumes:\n      - ./worlds:/worlds:ro\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/spigot_world_for_spigot_server/verify.sh",
    "content": "mc-image-helper assert fileExists world/level.dat && \\\nmc-image-helper assert fileExists world/some_overworld_file && \\\nmc-image-helper assert fileExists world_nether/DIM-1/some_nether_file && \\\nmc-image-helper assert fileExists world_the_end/DIM1/some_end_file\n"
  },
  {
    "path": "tests/setuponlytests/spigot_world_for_vanilla_server/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      WORLD: /worlds/world-for-testing.zip\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./worlds:/worlds:ro\n      - ./data:/data\n      # the following are only used to speed up test execution\n      - ./verify.sh:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/spigot_world_for_vanilla_server/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/spigot_world_for_vanilla_server/verify.sh",
    "content": "mc-image-helper assert fileExists world/level.dat && \\\nmc-image-helper assert fileExists world/some_overworld_file && \\\nmc-image-helper assert fileExists world/DIM-1/some_nether_file && \\\nmc-image-helper assert fileExists world/DIM1/some_end_file\n"
  },
  {
    "path": "tests/setuponlytests/spongevanilla_version_compare/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      TYPE: \"SPONGEVANILLA\"\n      SPONGEVERSION: \"1.12.2-7.3.1-RC391\"\n      DIFFICULTY: \"0\"\n\n    volumes:\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/spongevanilla_version_compare/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/spongevanilla_version_compare/verify.sh",
    "content": "mc-image-helper assert propertyEquals --file=server.properties --property=difficulty --expect=0\n"
  },
  {
    "path": "tests/setuponlytests/test.sh",
    "content": "#!/bin/bash\nset -euo pipefail\nIFS=$'\\n\\t'\n\n: \"${IMAGE_TO_TEST:=itzg/minecraft-server}\"\n\n# go to script root directory\ncd \"$(dirname \"$0\")\" || exit 1\n\noutputContainerLog() {\n  logs=${1?}\n\n  echo \"${folder} test scenario FAILED\"\n  echo \":::::::::::: LOGS ::::::::::::::::\n$logs\n::::::::::::::::::::::::::::::::::\n\"\n}\n\ndelta() {\n  startTime=${1?}\n\n  endTime=$(date +%s)\n  echo \"$(( endTime - startTime )) seconds\"\n}\n\n# tests that only run the setup files for things like downloads and configuration.\nsetupOnlyMinecraftTest(){\n  folder=$1\n  cd \"$folder\"\n  result=0\n\n  if [ -f require.sh ]; then\n    # require.sh scripts can check for environment variables, etc that are required for the test.\n    # The script should exit with a non-zero status to indicate the test requirements are missing\n    # and the test should be skipped\n    if ! bash require.sh; then\n      echo \"${folder} SKIP\"\n      cd ..\n      return 0\n    fi\n  fi\n\n  # false positive since it's used in delta calculations below\n  # shellcheck disable=SC2034\n  start=$(date +%s)\n  status=PASSED\n  verify=\n  if ! logs=$(docker compose run --rm -e SETUP_ONLY=true -e DEBUG=\"${DEBUG:-false}\" mc 2>&1); then\n    status=FAILED\n    outputContainerLog \"$logs\"\n    result=1\n  elif [ -f verify.sh ]; then\n    verify=\" verify\"\n    if ! docker run --rm --entrypoint bash -v \"${PWD}/data\":/data -v \"${PWD}/verify.sh\":/verify \"${IMAGE_TO_TEST}\" -e /verify; then\n      status=FAILED\n      outputContainerLog \"$logs\"\n      result=1\n    fi\n  fi\n  echo \"${folder} ${status}${verify} in $(delta start)\"\n\n  docker compose down -v --remove-orphans >& /dev/null\n  cd ..\n\n  return $result\n}\n\nfoldersList=(\"$@\")\nimage=\"\"\n\n# Go through each folder in setuponly and test setups\nif (( $# == 0 )); then\n  readarray -t folders < <(find . -maxdepth 2 -mindepth 2 -name docker-compose.yml -exec dirname \"{}\" \\;)\n  foldersList=(\"${folders[@]}\")\n  image=\" using $IMAGE_TO_TEST\"\nfi\n\nfor folder in \"${foldersList[@]}\"; do\n  echo \"Starting Tests in ${folder}${image}\"\n  setupOnlyMinecraftTest \"$folder\"\ndone"
  },
  {
    "path": "tests/setuponlytests/vanilla_world_for_spigot_server/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      TYPE: \"PAPER\"\n      WORLD: /worlds/world-for-testing.zip\n      # the following are only used to speed up test execution\n      VERSION: 1.18.1\n      PAPER_CUSTOM_JAR: /servers/fake.jar\n    volumes:\n      - ./worlds:/worlds:ro\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/vanilla_world_for_spigot_server/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/vanilla_world_for_spigot_server/verify.sh",
    "content": "mc-image-helper assert fileExists world/level.dat && \\\nmc-image-helper assert fileExists world_nether/DIM-1/some_nether_file && \\\nmc-image-helper assert fileExists world_the_end/DIM1/some_end_file\n"
  },
  {
    "path": "tests/setuponlytests/vanilla_world_for_vanilla_server/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      WORLD: /worlds/world-for-testing.zip\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./worlds:/worlds:ro\n      - ./data:/data\n      # the following are only used to speed up test execution\n      - ./verify.sh:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/vanilla_world_for_vanilla_server/verify.sh",
    "content": "mc-image-helper assert fileExists world/level.dat && \\\nmc-image-helper assert fileExists world/DIM-1/some_nether_file && \\\nmc-image-helper assert fileExists world/DIM1/some_end_file\n"
  },
  {
    "path": "tests/setuponlytests/vanillatweaks_file/docker-compose.yml",
    "content": "services:\r\n  mc:\r\n    restart: \"no\"\r\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\r\n    environment:\r\n      VANILLATWEAKS_FILE: /config/vt-datapacks.json,/config/vt-craftingtweaks.json\r\n      EULA: \"TRUE\"\r\n      SETUP_ONLY: \"TRUE\"\r\n      # the following are only used to speed up test execution\r\n      TYPE: CUSTOM\r\n      CUSTOM_SERVER: /servers/fake.jar\r\n      VERSION: 1.18.1\r\n    volumes:\r\n      - ./data:/data\r\n      - ./vt-datapacks.json:/config/vt-datapacks.json:ro\r\n      - ./vt-craftingtweaks.json:/config/vt-craftingtweaks.json:ro\r\n      # the following are only used to speed up test execution\r\n      - ./verify.sh:/servers/fake.jar\r\n"
  },
  {
    "path": "tests/setuponlytests/vanillatweaks_file/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/vanillatweaks_file/verify.sh",
    "content": "mc-image-helper assert fileExists \"/data/world/datapacks/afk*\"\nmc-image-helper assert fileExists \"/data/world/datapacks/graves*\"\nmc-image-helper assert fileExists \"/data/world/datapacks/VanillaTweaks_*\"\nmc-image-helper assert fileExists \"/data/resourcepacks/VanillaTweaks_*\"\n"
  },
  {
    "path": "tests/setuponlytests/vanillatweaks_file/vt-craftingtweaks.json",
    "content": "{\n    \"type\": \"craftingtweaks\",\n    \"version\": \"1.18\",\n    \"packs\": {\n        \"quality of life\": [\n            \"dropper to dispenser\",\n            \"double slabs\",\n            \"back to blocks\"\n        ]\n    },\n    \"result\": \"ok\"\n}"
  },
  {
    "path": "tests/setuponlytests/vanillatweaks_file/vt-datapacks.json",
    "content": "{\n    \"type\": \"datapacks\",\n    \"version\": \"1.18\",\n    \"packs\": {\n        \"survival\": [\n            \"graves\",\n            \"multiplayer sleep\",\n            \"afk display\",\n            \"armor statues\",\n            \"unlock all recipes\",\n            \"fast leaf decay\",\n            \"coordinates hud\"\n        ],\n        \"items\": [\"armored elytra\"]\n    },\n    \"result\": \"ok\"\n}\n"
  },
  {
    "path": "tests/setuponlytests/vanillatweaks_file/vt-resourcepacks.json",
    "content": "{\n    \"type\": \"resourcepacks\",\n    \"version\": \"1.18\",\n    \"packs\": {\n        \"aesthetic\": [\"CherryPicking\", \"BlackNetherBricks\", \"AlternateBlockDestruction\"]\n    },\n    \"result\": \"ok\"\n}"
  },
  {
    "path": "tests/setuponlytests/vanillatweaks_sharecode/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      VANILLATWEAKS_SHARECODE: MGr52E,tF1zL2,LnEDwT\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./data:/data\n      # the following are only used to speed up test execution\n      - ./verify.sh:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/vanillatweaks_sharecode/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/vanillatweaks_sharecode/verify.sh",
    "content": "mc-image-helper assert fileExists \"/data/world/datapacks/afk*\"\nmc-image-helper assert fileExists \"/data/world/datapacks/graves*\"\nmc-image-helper assert fileExists \"/data/world/datapacks/VanillaTweaks_488158f.zip\"\nmc-image-helper assert fileExists \"/data/resourcepacks/VanillaTweaks_d1d810f.zip\"\n"
  },
  {
    "path": "tests/setuponlytests/whitelist_from_scratch/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      WHITELIST: itzg\n      ENFORCE_WHITELIST: \"true\"\n      OVERRIDE_SERVER_PROPERTIES: \"true\"\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./data:/data\n      # the following are only used to speed up test execution\n      - ./verify.sh:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/whitelist_from_scratch/verify.sh",
    "content": "mc-image-helper assert jsonPathEquals --file=whitelist.json --path='$[0].name' --expect=itzg\nmc-image-helper assert jsonPathEquals --file=whitelist.json --path='$[0].uuid' --expect=5cddfd26-fc86-4981-b52e-c42bb10bfdef\nmc-image-helper assert propertyEquals --file=server.properties --property=white-list --expect=true\nmc-image-helper assert propertyEquals --file=server.properties --property=enforce-whitelist --expect=true\n"
  },
  {
    "path": "tests/setuponlytests/world_from_tar/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      WORLD: /worlds/world-for-testing.tar\n      # the following are only used to speed up test execution\n      TYPE: CUSTOM\n      CUSTOM_SERVER: /servers/fake.jar\n      VERSION: 1.18.1\n    volumes:\n      - ./worlds:/worlds:ro\n      - ./data:/data\n      # the following are only used to speed up test execution\n      - ./verify.sh:/servers/fake.jar\n"
  },
  {
    "path": "tests/setuponlytests/world_from_tar/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/world_from_tar/verify.sh",
    "content": "mc-image-helper assert fileExists world/level.dat"
  },
  {
    "path": "tests/setuponlytests/world_from_tarbz2/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      VERSION: ${MINECRAFT_VERSION:-LATEST}\n      WORLD: /worlds/world-for-testing.tar.bz2\n    volumes:\n      - ./worlds:/worlds:ro\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/world_from_tarbz2/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/world_from_tarbz2/verify.sh",
    "content": "mc-image-helper assert fileExists world/level.dat"
  },
  {
    "path": "tests/setuponlytests/world_from_targz/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      VERSION: ${MINECRAFT_VERSION:-LATEST}\n      WORLD: /worlds/world-for-testing.tar.gz\n    volumes:\n      - ./worlds:/worlds:ro\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/world_from_targz/verify.sh",
    "content": "mc-image-helper assert fileExists world/level.dat"
  },
  {
    "path": "tests/setuponlytests/world_from_tarzst/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      VERSION: ${MINECRAFT_VERSION:-LATEST}\n      WORLD: /worlds/world-for-testing.tar.zst\n    volumes:\n      - ./worlds:/worlds:ro\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/world_from_tarzst/require.sh",
    "content": "[[ $EXTENDED_TESTS ]] || exit 1"
  },
  {
    "path": "tests/setuponlytests/world_from_tarzst/verify.sh",
    "content": "mc-image-helper assert fileExists world/level.dat"
  },
  {
    "path": "tests/setuponlytests/world_from_zip/docker-compose.yml",
    "content": "services:\n  mc:\n    restart: \"no\"\n    image: ${IMAGE_TO_TEST:-itzg/minecraft-server}\n    environment:\n      EULA: \"TRUE\"\n      SETUP_ONLY: \"TRUE\"\n      VERSION: ${MINECRAFT_VERSION:-LATEST}\n      WORLD: /worlds/world-for-testing.zip\n    volumes:\n      - ./worlds:/worlds:ro\n      - ./data:/data\n"
  },
  {
    "path": "tests/setuponlytests/world_from_zip/verify.sh",
    "content": "mc-image-helper assert fileExists world/level.dat"
  },
  {
    "path": "tests/test.sh",
    "content": "#!/bin/bash\nset -euo pipefail\nIFS=$'\\n\\t'\n\n# go to script root directory\ncd \"$(dirname \"$0\")\" || exit 1\n\n# go through top level folders and trigger the tests in the subfolders\nreadarray -t folders < <(find . -maxdepth 2 -mindepth 2 -name test.sh -printf '%h\\n')\nfor folder in \"${folders[@]}\"; do\n  cd \"$folder\"\n  echo \"Starting ${folder} Tests\"\n  bash ./test.sh\n  cd ..\ndone\n"
  },
  {
    "path": "zensical.toml",
    "content": "[project]\nsite_name = \"Minecraft Server on Docker (Java Edition)\"\nsite_url = \"https://docker-minecraft-server.readthedocs.io/en/latest/\"\nsite_description = \"Documentation for Minecraft Server on Docker\"\nrepo_name = \"itzg/docker-minecraft-server\"\nrepo_url = \"https://github.com/itzg/docker-minecraft-server\"\nsite_author = \"itzg\"\ncopyright = \"Copyright &copy; itzg 2026.\"\ndocs_dir = \"docs\"\nsite_dir = \"site\"\n\n[project.theme]\nfeatures = [\n    \"navigation.tracking\",\n    \"navigation.tabs\",\n    \"navigation.tabs.sticky\",\n    \"navigation.sections\",\n    \"navigation.top\",\n    \"navigation.indexes\"\n]\nlanguage = \"en\"\n\n[project.theme.icon]\nrepo = \"fontawesome/brands/github\"\n\n[[project.theme.palette]]\nmedia = \"(prefers-color-scheme)\"\ntoggle.icon = \"lucide/sun-moon\"\ntoggle.name = \"Switch to light mode\"\n\n[[project.theme.palette]]\nmedia = \"(prefers-color-scheme: light)\"\nscheme = \"default\"\ntoggle.icon = \"lucide/sun\"\ntoggle.name = \"Switch to dark mode\"\n\n[[project.theme.palette]]\nmedia = \"(prefers-color-scheme: dark)\"\nscheme = \"slate\"\ntoggle.icon = \"lucide/moon\"\ntoggle.name = \"Switch to light mode\"\n"
  }
]