Repository: itzg/docker-minecraft-server Branch: master Commit: fba656561f17 Files: 370 Total size: 618.2 KB Directory structure: gitextract_eezdj074/ ├── .dockerignore ├── .editorconfig ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug.yml │ │ ├── config.yml │ │ └── enhancement.yml │ ├── dependabot.yml │ ├── release.yml │ └── workflows/ │ ├── auto-release.yml │ ├── build.yml │ ├── discord.yml │ ├── issue-label.yml │ ├── stale-check.yml │ └── verify-pr.yml ├── .gitignore ├── .readthedocs.yaml ├── Dockerfile ├── LICENSE ├── README.md ├── build/ │ ├── alpine/ │ │ ├── install-packages.sh │ │ └── setup-user.sh │ ├── ol/ │ │ ├── install-packages.sh │ │ └── setup-user.sh │ ├── run.sh │ └── ubuntu/ │ ├── install-packages.sh │ └── setup-user.sh ├── compose-dev.yml ├── compose-docs.yml ├── docker-compose.yml ├── docs/ │ ├── Dockerfile │ ├── configuration/ │ │ ├── auto-rcon-commands.md │ │ ├── interpolating.md │ │ ├── jvm-options.md │ │ ├── misc-options.md │ │ └── server-properties.md │ ├── css/ │ │ └── extra.css │ ├── data-directory.md │ ├── img/ │ │ ├── .gitignore │ │ ├── Minecraft server containment.xml │ │ └── docs-banner.txt │ ├── index.md │ ├── misc/ │ │ ├── autopause-autostop/ │ │ │ ├── autopause.md │ │ │ └── autostop.md │ │ ├── autoscale/ │ │ │ └── autoscale.md │ │ ├── building.md │ │ ├── contributing/ │ │ │ ├── development.md │ │ │ └── docs.md │ │ ├── deployment/ │ │ │ └── index.md │ │ ├── examples.md │ │ ├── healthcheck.md │ │ ├── log4j.md │ │ ├── raspberrypi.md │ │ ├── related-projects.md │ │ ├── troubleshooting.md │ │ └── world-data.md │ ├── mods-and-plugins/ │ │ ├── curseforge-files.md │ │ ├── index.md │ │ ├── modrinth.md │ │ ├── packwiz.md │ │ └── spiget.md │ ├── requirements.txt │ ├── sending-commands/ │ │ ├── commands.md │ │ ├── ssh.md │ │ └── websocket.md │ ├── types-and-platforms/ │ │ ├── index.md │ │ ├── mod-platforms/ │ │ │ ├── auto-curseforge.md │ │ │ ├── curseforge.md │ │ │ ├── ftb.md │ │ │ ├── gtnh.md │ │ │ └── modrinth-modpacks.md │ │ └── server-types/ │ │ ├── bukkit-spigot.md │ │ ├── fabric.md │ │ ├── forge.md │ │ ├── hybrids.md │ │ ├── others.md │ │ ├── paper.md │ │ └── quilt.md │ ├── variables.md │ └── versions/ │ ├── java.md │ └── minecraft.md ├── examples/ │ ├── .gitignore │ ├── README.md │ ├── apply-extra-configs/ │ │ ├── compose.yml │ │ └── shared-configs/ │ │ └── LuckPerms/ │ │ └── config.yml │ ├── auto-curseforge/ │ │ ├── aof7/ │ │ │ └── docker-compose.yml │ │ ├── atm10/ │ │ │ └── docker-compose.yml │ │ ├── atm8/ │ │ │ ├── docker-compose.yml │ │ │ └── filebrowser.json │ │ ├── atm9/ │ │ │ └── docker-compose.yml │ │ ├── craftoria/ │ │ │ └── docker-compose.yml │ │ ├── fabric-custom-loader/ │ │ │ └── docker-compose.yml │ │ ├── ftb-evolution/ │ │ │ └── compose.yaml │ │ ├── modpack-manifest/ │ │ │ ├── docker-compose.yml │ │ │ └── manifests/ │ │ │ └── manifest.json │ │ ├── one-block-modded/ │ │ │ ├── .gitignore │ │ │ └── docker-compose.yml │ │ ├── pixelmon/ │ │ │ └── docker-compose.yml │ │ ├── prominence2/ │ │ │ └── docker-compose.yml │ │ ├── rad2/ │ │ │ └── compose.yaml │ │ ├── skyfactory4/ │ │ │ └── compose.yml │ │ ├── skyfactory5/ │ │ │ └── compose.yml │ │ ├── using-excludes/ │ │ │ └── docker-compose.yml │ │ ├── valhesia5/ │ │ │ └── docker-compose.yml │ │ └── vault-hunters-1-18-2/ │ │ └── docker-compose.yml │ ├── autopause/ │ │ └── compose.yml │ ├── autostop/ │ │ └── compose.yml │ ├── bentobox/ │ │ └── compose.yml │ ├── bettermc/ │ │ └── docker-compose.yml │ ├── canyon/ │ │ └── compose.yml │ ├── cleanroom/ │ │ └── compose.yml │ ├── curseforge-files/ │ │ └── docker-compose.yml │ ├── docker-compose-big.yml │ ├── docker-compose-curseforge-atm7.yaml │ ├── docker-compose-curseforge.yml │ ├── docker-compose-forge-bettermcplus │ ├── docker-compose-forge.yml │ ├── docker-compose-magma.yml │ ├── docker-compose-mods-mount.yml │ ├── docker-compose-rconcmd.yml │ ├── docker-compose-simple.yml │ ├── docker-compose-spongeforge.yml │ ├── docker-compose-world-download.yml │ ├── docker-compose.yml │ ├── fabric/ │ │ └── compose.yml │ ├── fabric-cardboard/ │ │ └── compose.yml │ ├── ftba/ │ │ ├── compose.yml │ │ └── stoneblock4/ │ │ └── compose.yml │ ├── generic-pack/ │ │ └── rlcraft/ │ │ └── compose.yml │ ├── geyser/ │ │ └── docker-compose.yml │ ├── gtnh/ │ │ ├── docker-compose-type-gtnh.yaml │ │ └── docker-compose.yaml │ ├── jline/ │ │ └── docker-compose.yml │ ├── k8s/ │ │ ├── using-statefulset.yml │ │ └── vanilla-deployment.yml │ ├── ketting/ │ │ └── docker-compose.yml │ ├── lazymc/ │ │ └── docker_compose.yml │ ├── lazytainer/ │ │ └── docker_compose.yml │ ├── mc-router-autoscale/ │ │ ├── compose-minimal.yml │ │ └── compose.yml │ ├── modrinth/ │ │ ├── bmc4/ │ │ │ └── compose.yml │ │ ├── local-modpack/ │ │ │ └── docker-compose.yml │ │ ├── modrinth-modpack/ │ │ │ └── compose.yml │ │ ├── modrinth-paper/ │ │ │ └── compose.yml │ │ ├── modrinth-projects/ │ │ │ └── compose.yml │ │ └── version-from-modrinth-projects/ │ │ └── compose.yml │ ├── mods-file/ │ │ ├── docker-compose.yml │ │ └── mods.txt │ ├── mohist/ │ │ └── compose.yml │ ├── multi-project/ │ │ ├── README.md │ │ ├── dbs/ │ │ │ └── docker-compose.yml │ │ └── servers/ │ │ ├── docker-compose.yml │ │ ├── mc-plugins/ │ │ │ └── LuckPerms/ │ │ │ └── config.yml │ │ ├── velocity-config/ │ │ │ └── velocity.toml │ │ └── waterfall-config/ │ │ ├── config.yml │ │ └── waterfall.yml │ ├── multiline-motd/ │ │ └── docker-compose.yml │ ├── neoforge/ │ │ └── docker-compose.yml │ ├── ngrok/ │ │ └── compose.yml │ ├── optimized-server-config/ │ │ ├── README.md │ │ └── docker-compose.yml │ ├── packwiz/ │ │ └── compose.yml │ ├── paper/ │ │ ├── compose.yml │ │ ├── paper-build-plugins/ │ │ │ ├── Dockerfile │ │ │ ├── docker-compose.yml │ │ │ └── plugins/ │ │ │ ├── .gitignore │ │ │ └── README.md │ │ └── paper-proxy-protocol/ │ │ ├── config/ │ │ │ └── paper-global.yml │ │ └── docker-compose.yml │ ├── proxied/ │ │ └── compose.yml │ ├── quilt/ │ │ └── docker-compose.yml │ ├── rlcraft/ │ │ └── docker-compose.yml │ ├── simple-voice-chat/ │ │ ├── fabric-compose.yaml │ │ ├── neoforge-compose.yaml │ │ └── paper-compose.yaml │ ├── skript/ │ │ └── docker-compose.yml │ ├── spiget/ │ │ └── docker-compose.yml │ ├── spigot/ │ │ └── docker-compose.yml │ ├── tailscale/ │ │ └── compose.yml │ ├── valhelsia2/ │ │ └── docker-compose.yml │ └── vanilla-tweaks/ │ ├── docker-compose.yml │ ├── vanillatweaks-craftingtweaks.json │ ├── vanillatweaks-datapacks.json │ └── vanillatweaks-resourcepacks.json ├── files/ │ ├── cf-exclude-include.json │ ├── knockd-config.cfg │ ├── modrinth-exclude-include.json │ ├── nanolimbo-settings-patch.json │ ├── property-definitions.json │ └── server.properties ├── images.json ├── kustomize/ │ ├── README.md │ └── base/ │ ├── kustomization.yml │ ├── service.yml │ └── statefulset.yml ├── notes/ │ ├── .gitignore │ ├── Minecraft server containment.xml │ └── rbac-processing.mmd ├── renovate.json5 ├── scripts/ │ ├── auto/ │ │ ├── autopause-daemon.sh │ │ ├── autopause-fcns.sh │ │ ├── autostop-daemon.sh │ │ ├── pause.sh │ │ ├── rcon-cmds-daemon.sh │ │ ├── resume.sh │ │ └── stop.sh │ ├── shims/ │ │ ├── mc-health │ │ ├── mc-send-to-console │ │ └── mcstatus │ ├── start │ ├── start-autopause │ ├── start-autostop │ ├── start-configuration │ ├── start-deployArcLight │ ├── start-deployAutoCF │ ├── start-deployBukkitSpigot │ ├── start-deployCF │ ├── start-deployCanyon │ ├── start-deployCrucible │ ├── start-deployCustom │ ├── start-deployFTBA │ ├── start-deployFabric │ ├── start-deployFolia │ ├── start-deployForge │ ├── start-deployGTNH │ ├── start-deployKetting │ ├── start-deployLeaf │ ├── start-deployLimbo │ ├── start-deployMagma │ ├── start-deployMagmaMaintained │ ├── start-deployModrinth │ ├── start-deployMohist │ ├── start-deployNanoLimbo │ ├── start-deployNeoForge │ ├── start-deployPaper │ ├── start-deployPoseidon │ ├── start-deployPufferfish │ ├── start-deployPurpur │ ├── start-deployQuilt │ ├── start-deploySpongeVanilla │ ├── start-deployVanilla │ ├── start-finalExec │ ├── start-rconcmds │ ├── start-setupDatapack │ ├── start-setupEnvVariables │ ├── start-setupModpack │ ├── start-setupMounts │ ├── start-setupRbac │ ├── start-setupServerProperties │ ├── start-setupWorld │ ├── start-spiget │ └── start-utils ├── tests/ │ ├── .gitignore │ ├── fulltests/ │ │ ├── multi-part-motd/ │ │ │ └── docker-compose.yml │ │ ├── test.sh │ │ └── vanilla-latest/ │ │ └── docker-compose.yml │ ├── setuponlytests/ │ │ ├── auto_curseforge/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── auto_curseforge_file/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── cf_files/ │ │ │ ├── docker-compose.yml │ │ │ ├── fake.jar │ │ │ ├── listing.txt │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── conflicting_world_for_spigot_server/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── conflicting_world_for_vanilla_server/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── curseforge/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── defaults/ │ │ │ ├── docker-compose.yml │ │ │ └── verify.sh │ │ ├── generic-packs/ │ │ │ ├── docker-compose.yml │ │ │ ├── fake.jar │ │ │ └── verify.sh │ │ ├── generic-packs-prefix/ │ │ │ ├── docker-compose.yml │ │ │ └── verify.sh │ │ ├── icon-file-exact/ │ │ │ ├── docker-compose.yml │ │ │ ├── fake.jar │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── icon-file-scale/ │ │ │ ├── docker-compose.yml │ │ │ ├── fake.jar │ │ │ └── verify.sh │ │ ├── icon-gif-multiframe/ │ │ │ ├── docker-compose.yml │ │ │ ├── fake.jar │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── icon-png-atscale/ │ │ │ ├── docker-compose.yml │ │ │ ├── fake.jar │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── icon-png-scale/ │ │ │ ├── docker-compose.yml │ │ │ ├── fake.jar │ │ │ └── verify.sh │ │ ├── modrinth/ │ │ │ ├── docker-compose.yml │ │ │ └── verify.sh │ │ ├── mounts-custom/ │ │ │ ├── custom/ │ │ │ │ ├── config/ │ │ │ │ │ └── test.json │ │ │ │ ├── mods/ │ │ │ │ │ └── mod.jar │ │ │ │ └── plugins/ │ │ │ │ └── plugin.jar │ │ │ ├── docker-compose.yml │ │ │ ├── fake.jar │ │ │ └── verify.sh │ │ ├── nanolimbo/ │ │ │ ├── docker-compose.yml │ │ │ └── verify.sh │ │ ├── ops_from_scratch/ │ │ │ ├── docker-compose.yml │ │ │ └── verify.sh │ │ ├── packwiz/ │ │ │ ├── docker-compose.yml │ │ │ ├── fake.jar │ │ │ ├── require.sh │ │ │ ├── verify.sh │ │ │ └── web/ │ │ │ ├── index.toml │ │ │ ├── mods/ │ │ │ │ └── architectury-api.pw.toml │ │ │ └── pack.toml │ │ ├── pufferfish/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── quilt/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── spiget/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── spigot_world_for_spigot_server/ │ │ │ ├── docker-compose.yml │ │ │ └── verify.sh │ │ ├── spigot_world_for_vanilla_server/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── spongevanilla_version_compare/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── test.sh │ │ ├── vanilla_world_for_spigot_server/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── vanilla_world_for_vanilla_server/ │ │ │ ├── docker-compose.yml │ │ │ └── verify.sh │ │ ├── vanillatweaks_file/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ ├── verify.sh │ │ │ ├── vt-craftingtweaks.json │ │ │ ├── vt-datapacks.json │ │ │ └── vt-resourcepacks.json │ │ ├── vanillatweaks_sharecode/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── whitelist_from_scratch/ │ │ │ ├── docker-compose.yml │ │ │ └── verify.sh │ │ ├── world_from_tar/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ └── verify.sh │ │ ├── world_from_tarbz2/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ ├── verify.sh │ │ │ └── worlds/ │ │ │ └── world-for-testing.tar.bz2 │ │ ├── world_from_targz/ │ │ │ ├── docker-compose.yml │ │ │ └── verify.sh │ │ ├── world_from_tarzst/ │ │ │ ├── docker-compose.yml │ │ │ ├── require.sh │ │ │ ├── verify.sh │ │ │ └── worlds/ │ │ │ └── world-for-testing.tar.zst │ │ └── world_from_zip/ │ │ ├── docker-compose.yml │ │ └── verify.sh │ └── test.sh └── zensical.toml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ data testdata examples k8s-examples .idea .git ================================================ FILE: .editorconfig ================================================ [start-*] indent_size = 2 indent_style = space end_of_line = lf ================================================ FILE: .github/FUNDING.yml ================================================ github: itzg custom: - https://www.buymeacoffee.com/itzg - https://paypal.me/itzg ================================================ FILE: .github/ISSUE_TEMPLATE/bug.yml ================================================ name: Bug Report description: File a bug report labels: - bug - status/needs triage body: - type: markdown attributes: value: | Thanks for taking the time to report a bug. Please double check some things first: 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. 2. Did you **re-pull the newest image** and confirmed the issue after that? Run `docker pull ` or if using a compose file, it's as easy as running `docker compose pull`. If using Kubernetes, add `imagePullPolicy: Always` to the container. 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. 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. - type: textarea id: problem attributes: label: Describe the problem validations: required: true - type: textarea id: container attributes: label: Container definition description: Please provide the compose file or run command used to create the container value: | ``` Paste run command or compose file here ``` - type: textarea id: logs attributes: label: Container logs description: | 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. value: | ``` Paste logs here ``` ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ contact_links: - name: Ask a question in discussions url: https://github.com/itzg/docker-minecraft-server/discussions about: Please ask questions here - name: Ask a question on Discord url: https://discord.gg/DXfKpjB about: Please ask questions here ================================================ FILE: .github/ISSUE_TEMPLATE/enhancement.yml ================================================ name: Enhancement Request description: Request an enhancement labels: - enhancement - status/needs triage body: - type: markdown attributes: value: | Thanks for taking the time to request an enhancement. 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. - type: dropdown id: type attributes: label: Enhancement Type options: - Improve an existing feature - A completely new feature - New server type - Not sure validations: required: true - type: textarea id: description attributes: label: Describe the enhancement validations: required: true ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" groups: updates: patterns: - "*" update-types: - patch - minor - package-ecosystem: pip directory: "/docs" schedule: interval: weekly groups: patches: patterns: - "*" update-types: - patch - minor ================================================ FILE: .github/release.yml ================================================ changelog: exclude: authors: - dependabot - renovate categories: - title: Enhancements labels: - enhancement - title: Bug Fixes labels: - bug - title: Documentation labels: - documentation - title: Other Changes labels: - "*" ================================================ FILE: .github/workflows/auto-release.yml ================================================ name: Auto release on: schedule: - cron: "0 14 * * *" workflow_dispatch: jobs: auto-release: runs-on: ubuntu-latest steps: - uses: zenengeo/github-auto-release-action@main with: stable-duration: 3d force-duration: 14d token: '${{ secrets.GH_TOKEN }}' ================================================ FILE: .github/workflows/build.yml ================================================ name: Build and Publish on: push: branches: - master - test/** tags: - "[0-9]+.[0-9]+.[0-9]+" paths-ignore: - "*.md" - "docs/**" - "examples/**" - "notes/**" - "kustomize/**" - "docker-compose.yml" - "images.json" - "compose-docs.yml" - "zensical.toml" - ".readthedocs.yaml" - "renovate.json5" jobs: build: strategy: fail-fast: false matrix: # NOTE: the "latest" variant is identified in the Docker meta step's 'latest' config variant: - java25 - java25-alpine - java25-jdk - java21 - java21-alpine - java21-jdk - java17 - java16 - java11 - java8 - java8-jdk include: # JAVA 25 - variant: java25 baseImage: eclipse-temurin:25-jre platforms: linux/amd64,linux/arm64 mcVersion: latest - variant: java25-alpine baseImage: eclipse-temurin:25-jre-alpine platforms: linux/amd64,linux/arm64 mcVersion: latest - variant: java25-jdk baseImage: eclipse-temurin:25 platforms: linux/amd64,linux/arm64 mcVersion: latest # JAVA 21: - variant: java21 baseImage: eclipse-temurin:21-jre platforms: linux/amd64,linux/arm64 mcVersion: latest - variant: java21-jdk baseImage: eclipse-temurin:21 platforms: linux/amd64,linux/arm64 mcVersion: latest - variant: java21-alpine baseImage: eclipse-temurin:21-jre-alpine platforms: linux/amd64,linux/arm64 mcVersion: latest # JAVA 17: - variant: java17 # jammy doesn't work until minecraft updates to https://github.com/netty/netty/issues/12343 baseImage: eclipse-temurin:17-jre-focal platforms: linux/amd64,linux/arm/v7,linux/arm64 mcVersion: 1.20.4 # JAVA 16 - variant: java16 baseImage: adoptopenjdk:16-jre-hotspot platforms: linux/amd64,linux/arm/v7,linux/arm64 mcVersion: 1.16.5 # JAVA 11 - variant: java11 baseImage: adoptopenjdk:11-jre-hotspot platforms: linux/amd64,linux/arm/v7,linux/arm64 mcVersion: 1.16.4 # JAVA 8: NOTE: Unable to go past 8u312 because of Forge dependencies - variant: java8 baseImage: eclipse-temurin:8u312-b07-jre-focal platforms: linux/amd64,linux/arm/v7,linux/arm64 mcVersion: 1.12.2 # Pin version for Java 8 mcHelperVersion: 1.51.1 - variant: java8-jdk baseImage: eclipse-temurin:8u312-b07-jdk-focal platforms: linux/amd64,linux/arm64 mcVersion: 1.12.2 # Pin version for Java 8 mcHelperVersion: 1.51.1 env: IMAGE_TO_TEST: "${{ github.repository_owner }}/minecraft-server:test-${{ matrix.variant }}-${{ github.run_id }}" HAS_IMAGE_REPO_ACCESS: ${{ secrets.DOCKER_USER != '' && secrets.DOCKER_PASSWORD != '' }} MAIN_VARIANT: java25 runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v6.0.2 with: # for build-files step fetch-depth: 0 - name: Docker meta id: meta uses: docker/metadata-action@v5.10.0 with: # NOTE for forks: if your Docker Hub organization doesn't match your Github repo's, # then the use of ${{ github.repository_owner }} will need to be replaced. images: | ${{ github.repository_owner }}/minecraft-server ghcr.io/${{ github.repository_owner }}/minecraft-server tags: | # Apply the variant as a moving tag for most recent commit per variant type=raw,value=${{ matrix.variant }},enable=${{ github.ref_name == github.event.repository.default_branch }} # For the "main" variant, it gets the tag as-is type=pep440,pattern={{version}},enable=${{ matrix.variant == env.MAIN_VARIANT }} # ...and all variants (including main one) get the tag with the variant suffix, such as 2023.1.1-java17 type=pep440,pattern={{version}},suffix=-${{ matrix.variant }} # latest tag gets a moving 'stable' image tag applied to the main variant type=pep440,pattern=stable,enable=${{ matrix.variant == env.MAIN_VARIANT }} # ...and qualified stable for all variants type=pep440,pattern=stable,suffix=-${{ matrix.variant }} # for building test/* branch images type=ref,event=branch,suffix=-${{ matrix.variant }},enable=${{ github.ref_name != github.event.repository.default_branch }} # for backward compatibility with users referencing java8-multiarch, this will set an extra label on java8 type=raw,value=java8-multiarch,enable=${{ matrix.variant == 'java8' && github.ref_name == github.event.repository.default_branch }} # NOTE this identifies which variant will be published as "latest", which isn't # necessarily the newest version of Java flavor: | latest=${{ matrix.variant == env.MAIN_VARIANT && github.ref_name == github.event.repository.default_branch }} labels: | org.opencontainers.image.authors=Geoff Bourne - name: Setup Docker Buildx uses: docker/setup-buildx-action@v3.12.0 - name: Set up QEMU uses: docker/setup-qemu-action@v3.7.0 - name: Build for test uses: docker/build-push-action@v6.19.2 with: platforms: linux/amd64 tags: ${{ env.IMAGE_TO_TEST }} # ensure latest base image is used pull: true # load into daemon for test usage in next step load: true push: false build-args: | BASE_IMAGE=${{ matrix.baseImage }} ${{ matrix.mcHelperVersion && format('MC_HELPER_VERSION={0}', matrix.mcHelperVersion) }} cache-from: type=gha,scope=${{ matrix.variant }} # no cache-to to avoid cross-cache update from next build step - name: Run tests env: MINECRAFT_VERSION: ${{ matrix.mcVersion }} VARIANT: ${{ matrix.variant }} CF_API_KEY: ${{ secrets.CF_API_KEY }} GH_TOKEN: ${{ github.token }} run: | tests/test.sh - name: Login to DockerHub uses: docker/login-action@v3.7.0 if: env.HAS_IMAGE_REPO_ACCESS with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to GHCR uses: docker/login-action@v3.7.0 if: env.HAS_IMAGE_REPO_ACCESS with: registry: ghcr.io username: ${{ github.actor }} password: ${{ github.token }} - name: Build and push uses: docker/build-push-action@v6.19.2 if: github.actor == github.repository_owner with: platforms: ${{ matrix.platforms }} push: > ${{ github.ref_type == 'tag' || github.ref_name == github.event.repository.default_branch || startsWith(github.ref_name, 'test/') || ( github.event_name == 'pull_request' && env.HAS_IMAGE_REPO_ACCESS && contains(github.event.pull_request.labels.*.name, 'ci/push-image') ) }} tags: ${{ steps.meta.outputs.tags }} # ensure latest base image is used pull: true labels: ${{ steps.meta.outputs.labels }} # Since some consumers, like Watchtower are broken https://github.com/containrrr/watchtower/discussions/1529 # Also refer to https://github.com/docker/build-push-action/releases/tag/v3.3.0 provenance: false build-args: | BASE_IMAGE=${{ matrix.baseImage }} BUILD_FILES_REV=${{ steps.build-files-rev.outputs.REV }} BUILDTIME=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }} VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }} REVISION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }} ${{ matrix.mcHelperVersion && format('MC_HELPER_VERSION={0}', matrix.mcHelperVersion) }} cache-from: type=gha,scope=${{ matrix.variant }} cache-to: type=gha,mode=max,scope=${{ matrix.variant }} ================================================ FILE: .github/workflows/discord.yml ================================================ name: Discord notifications on: release: types: - published workflow_run: workflows: ["Build and Publish"] types: - completed env: DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} jobs: discord: runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: on-publish if: github.event_name == 'release' && github.event.action == 'published' uses: SethCohen/github-releases-to-discord@v1.19.0 with: webhook_url: ${{ secrets.DISCORD_WEBHOOK }} - name: on-success if: github.event.workflow_run.conclusion == 'success' uses: Ilshidur/action-discord@master with: args: | Github repo: ${{ github.repository }} - Branch: ${{ github.event.workflow_run.head_branch }} - [Link: to Actions](<${{ github.event.workflow_run.html_url }}>) - Status: 🎉 ${{ github.event.workflow_run.conclusion }} 🍏 - name: on-failure if: github.event.workflow_run.conclusion == 'failure' uses: Ilshidur/action-discord@master with: args: | Github repo: ${{ github.repository }} - Branch: ${{ github.event.workflow_run.head_branch }} - [Link: to Actions](<${{ github.event.workflow_run.html_url }}>) - Status: 🤔 ${{ github.event.workflow_run.conclusion }} 💣💥 ================================================ FILE: .github/workflows/issue-label.yml ================================================ name: Issues notify on: issues: types: - opened - labeled - unlabeled - closed jobs: notify: uses: itzg/github-workflows/.github/workflows/issues-notify-discord.yml@main with: triageLabel: 'status/needs triage' secrets: discordWebhook: "${{secrets.DISCORD_ISSUES_WEBHOOK}}" ================================================ FILE: .github/workflows/stale-check.yml ================================================ name: Stale Check on: schedule: - cron: 0 2 * * * jobs: check: runs-on: ubuntu-22.04 permissions: issues: write pull-requests: write steps: - name: Process Stale Issues uses: actions/stale@v10 with: stale-issue-label: status/stale stale-pr-label: status/stale stale-issue-message: > This issue is stale because it has been open 30 days with no activity. Please add a comment describing the reason to keep this issue open. days-before-stale: 30 days-before-close: 5 exempt-issue-labels: 'enhancement,keep,status/needs triage,priority/high' ================================================ FILE: .github/workflows/verify-pr.yml ================================================ name: Verify PR on: pull_request: branches: - master paths-ignore: - "*.md" - "docs/**" - "examples/**" - "notes/**" - "kustomize/**" - "docker-compose.yml" - "images.json" - "compose-docs.yml" - "zensical.toml" - ".readthedocs.yaml" - "renovate.json5" jobs: build: strategy: fail-fast: false matrix: variant: - java25 - java25-alpine - java17 - java8 include: # JAVA 21/25: - variant: java25 baseImage: eclipse-temurin:25-jre platforms: linux/amd64,linux/arm64 mcVersion: latest - variant: java25-alpine baseImage: eclipse-temurin:25-jre-alpine platforms: linux/amd64,linux/arm64 mcVersion: latest # JAVA 17: - variant: java17 # jammy doesn't work until minecraft updates to https://github.com/netty/netty/issues/12343 baseImage: eclipse-temurin:17-jre-focal platforms: linux/amd64 mcVersion: 1.20.4 - variant: java8 baseImage: eclipse-temurin:8u312-b07-jre-focal platforms: linux/amd64 mcVersion: 1.12.2 # Pin version for Java 8 mcHelperVersion: 1.51.1 env: IMAGE_TO_TEST: ${{ github.repository_owner }}/minecraft-server:test-${{ matrix.variant }}-${{ github.run_id }} runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v6.0.2 with: # for build-files step fetch-depth: 0 - name: Setup Docker Buildx uses: docker/setup-buildx-action@v3.12.0 - name: Confirm multi-arch build uses: docker/build-push-action@v6.19.2 with: platforms: ${{ matrix.platforms }} # ensure latest base image is used pull: true build-args: | BASE_IMAGE=${{ matrix.baseImage }} ${{ matrix.mcHelperVersion && format('MC_HELPER_VERSION={0}', matrix.mcHelperVersion) }} cache-from: type=gha,scope=${{ matrix.variant }} - name: Build for test uses: docker/build-push-action@v6.19.2 with: # Only build single platform since loading multi-arch image into daemon fails with # "docker exporter does not currently support exporting manifest lists" platforms: linux/amd64 tags: ${{ env.IMAGE_TO_TEST }} # ensure latest base image is used pull: true # load into daemon for test usage in next step load: true push: false build-args: | BASE_IMAGE=${{ matrix.baseImage }} ${{ matrix.mcHelperVersion && format('MC_HELPER_VERSION={0}', matrix.mcHelperVersion) }} cache-from: type=gha,scope=${{ matrix.variant }} - name: Run tests env: MINECRAFT_VERSION: ${{ matrix.mcVersion }} VARIANT: ${{ matrix.variant }} CF_API_KEY: ${{ secrets.CF_API_KEY }} DEBUG: ${{ runner.debug }} GH_TOKEN: ${{ github.token }} run: | tests/test.sh ================================================ FILE: .gitignore ================================================ .vscode /data/ /.idea/ *.iml /gh-md-toc personal-build-and-develop.* site/ ================================================ FILE: .readthedocs.yaml ================================================ version: 2 build: os: ubuntu-24.04 tools: python: "3.14" jobs: # We recommend using a requirements file for reproducible builds. # This is just a quick example to get started. # https://docs.readthedocs.io/page/guides/reproducible-builds.html install: - pip install -r docs/requirements.txt build: html: - zensical build post_build: - mkdir -p $READTHEDOCS_OUTPUT/html/ - cp --recursive site/* $READTHEDOCS_OUTPUT/html/ ================================================ FILE: Dockerfile ================================================ ARG BASE_IMAGE=eclipse-temurin:25-jre FROM ${BASE_IMAGE} # hook into docker BuildKit --platform support # see https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope ARG TARGETOS ARG TARGETARCH ARG TARGETVARIANT # The following three arg/env vars get used by the platform specific "install-packages" script ARG EXTRA_DEB_PACKAGES="" ARG EXTRA_DNF_PACKAGES="" ARG EXTRA_ALPINE_PACKAGES="" ARG FORCE_INSTALL_PACKAGES=1 RUN --mount=target=/build,source=build \ TARGET=${TARGETARCH}${TARGETVARIANT} \ /build/run.sh install-packages COPY --from=tianon/gosu /gosu /usr/local/bin/ RUN --mount=target=/build,source=build \ /build/run.sh setup-user EXPOSE 25565 ARG APPS_REV=1 ARG GITHUB_BASEURL=https://github.com # renovate: datasource=github-releases packageName=itzg/easy-add ARG EASY_ADD_VERSION=0.8.11 ADD ${GITHUB_BASEURL}/itzg/easy-add/releases/download/${EASY_ADD_VERSION}/easy-add_${TARGETOS}_${TARGETARCH}${TARGETVARIANT} /usr/bin/easy-add RUN chmod +x /usr/bin/easy-add # renovate: datasource=github-releases packageName=itzg/restify ARG RESTIFY_VERSION=1.7.12 RUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \ --var version=${RESTIFY_VERSION} --var app=restify --file {{.app}} \ --from ${GITHUB_BASEURL}/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz # renovate: datasource=github-releases packageName=itzg/rcon-cli ARG RCON_CLI_VERSION=1.7.4 RUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \ --var version=${RCON_CLI_VERSION} --var app=rcon-cli --file {{.app}} \ --from ${GITHUB_BASEURL}/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz # renovate: datasource=github-releases packageName=itzg/mc-monitor ARG MC_MONITOR_VERSION=0.16.1 RUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \ --var version=${MC_MONITOR_VERSION} --var app=mc-monitor --file {{.app}} \ --from ${GITHUB_BASEURL}/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz # renovate: datasource=github-releases packageName=itzg/mc-server-runner ARG MC_SERVER_RUNNER_VERSION=1.14.3 RUN easy-add --var os=${TARGETOS} --var arch=${TARGETARCH}${TARGETVARIANT} \ --var version=${MC_SERVER_RUNNER_VERSION} --var app=mc-server-runner --file {{.app}} \ --from ${GITHUB_BASEURL}/itzg/{{.app}}/releases/download/{{.version}}/{{.app}}_{{.version}}_{{.os}}_{{.arch}}.tar.gz # renovate: datasource=github-releases packageName=itzg/mc-image-helper versioning=loose ARG MC_HELPER_VERSION=1.55.2 ARG MC_HELPER_BASE_URL=${GITHUB_BASEURL}/itzg/mc-image-helper/releases/download/${MC_HELPER_VERSION} # used for cache busting local copy of mc-image-helper ARG MC_HELPER_REV=1 RUN curl -fsSL ${MC_HELPER_BASE_URL}/mc-image-helper-${MC_HELPER_VERSION}.tgz \ | tar -C /usr/share -zxf - \ && ln -s /usr/share/mc-image-helper-${MC_HELPER_VERSION}/ /usr/share/mc-image-helper \ && ln -s /usr/share/mc-image-helper/bin/mc-image-helper /usr/bin VOLUME ["/data"] WORKDIR /data STOPSIGNAL SIGTERM # End user MUST set EULA and change RCON_PASSWORD ENV TYPE=VANILLA VERSION=LATEST EULA="" UID=1000 GID=1000 LC_ALL=en_US.UTF-8 COPY --chmod=755 scripts/start* /image/scripts/ # Backward compatible shim for those with legacy entrypoint COPY --chmod=755 <> /etc/gitconfig [user] name = Minecraft Server on Docker email = server@example.com EOF ================================================ FILE: build/alpine/setup-user.sh ================================================ #!/bin/sh set -e addgroup -g 1000 minecraft adduser -Ss /bin/false -u 1000 -G minecraft -h /home/minecraft minecraft ================================================ FILE: build/ol/install-packages.sh ================================================ #!/bin/bash export TARGET set -euo pipefail os_major_version=$(awk -F'[= ."]+' '/^VERSION_ID=/{ print $2 }' /etc/os-release) # Install and configure dnf microdnf install dnf -y dnf install 'dnf-command(config-manager)' -y dnf config-manager --set-enabled ol${os_major_version}_codeready_builder # Add EPEL repository tee /etc/yum.repos.d/ol${os_major_version}-epel.repo < /usr/local/sbin/knockd #!/bin/sh echo "Auto-pause (using knockd) is currently unavailable on graalvm image variants" echo "Consider using a different image variant https://docker-minecraft-server.readthedocs.io/en/latest/versions/java/" echo "or mc-router's auto scale up/down feature https://github.com/itzg/mc-router#docker-auto-scale-updown" exit 2 EOF chmod 755 /usr/local/sbin/knockd # TODO restore retrieval from https://github.com/Metalcape/knock when tar's "Cannot open: Invalid argument" is solved # Set git credentials globally cat <> /etc/gitconfig [user] name = Minecraft Server on Docker email = server@example.com EOF ================================================ FILE: build/ol/setup-user.sh ================================================ groupadd --gid 1000 minecraft useradd --system --shell /bin/false --uid 1000 -g minecraft --home /data minecraft ================================================ FILE: build/run.sh ================================================ #!/bin/sh set -e distro=$(cat /etc/os-release | grep -E "^ID=" | cut -d= -f2 | sed -e 's/"//g') "$(dirname "$0")/${distro}/$1".sh ================================================ FILE: build/ubuntu/install-packages.sh ================================================ #!/bin/bash export TARGET set -euo pipefail # Update and install packages apt-get update # shellcheck disable=SC2086 DEBIAN_FRONTEND=noninteractive \ apt-get install -y \ imagemagick \ file \ sudo \ net-tools \ iputils-ping \ curl \ git \ jq \ dos2unix \ mysql-client \ tzdata \ rsync \ nano \ unzip \ zstd \ lbzip2 \ nfs-common \ libpcap0.8 \ libnuma1 \ ${EXTRA_DEB_PACKAGES} # Install Git LFS curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash apt-get update apt-get install -y git-lfs # Clean up APT when done apt-get clean # Download and install patched knockd curl -fsSL -o /tmp/knock.tar.gz https://github.com/Metalcape/knock/releases/download/0.8.1/knock-0.8.1-$TARGET.tar.gz tar -xf /tmp/knock.tar.gz -C /usr/local/ && rm /tmp/knock.tar.gz ln -s /usr/local/sbin/knockd /usr/sbin/knockd setcap cap_net_raw=ep /usr/local/sbin/knockd find /usr/lib -name 'libpcap.so.0.8' -execdir cp '{}' libpcap.so.1 \; # Set git credentials globally cat <> /etc/gitconfig [user] name = Minecraft Server on Docker email = server@example.com EOF ================================================ FILE: build/ubuntu/setup-user.sh ================================================ #!/bin/sh set -e if id ubuntu > /dev/null 2>&1; then deluser ubuntu fi addgroup --gid 1000 minecraft adduser --system --shell /bin/false --uid 1000 --ingroup minecraft --home /data minecraft ================================================ FILE: compose-dev.yml ================================================ services: mc-dev: build: context: . environment: EULA: true volumes: - ./scripts:/image/scripts:ro - data:/data ports: - "25565:25565" stdin_open: true tty: true volumes: data: ================================================ FILE: compose-docs.yml ================================================ # This composition can be used to serve up the rendered mkdocs for local authoring. # # docker compose -f compose-docs.yml -p zensical up # # and then access http://localhost:8000 services: zensical: build: context: . dockerfile: docs/Dockerfile volumes: - ./zensical.toml:/docs/zensical.toml - ./docs:/docs/docs ports: - "8000:8000" ================================================ FILE: docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: "true" ports: - "25565:25565" volumes: - data:/data stdin_open: true tty: true restart: unless-stopped volumes: data: {} ================================================ FILE: docs/Dockerfile ================================================ FROM zensical/zensical RUN --mount=target=/build/requirements.txt,source=docs/requirements.txt \ pip install -r /build/requirements.txt ================================================ FILE: docs/configuration/auto-rcon-commands.md ================================================ # Auto-execute RCON commands RCON commands can be configured to execute when the server starts, a client connects, or a client disconnects. !!! note When declaring several commands within a compose file environment variable, it's easiest to use YAML's `|-` [block style indicator](https://yaml-multiline.info/). **On Server Start:** ``` yaml RCON_CMDS_STARTUP: |- gamerule doFireTick false pregen start 200 ``` **On Client Connection:** ``` yaml RCON_CMDS_ON_CONNECT: |- team join New @a[team=] ``` **Note:** * On client connect we only know there was a connection, and not who connected. RCON commands will need to be used for that. **On Client Disconnect:** ``` yaml RCON_CMDS_ON_DISCONNECT: |- gamerule doFireTick true ``` **On First Client Connect** ``` yaml RCON_CMDS_FIRST_CONNECT: |- pregen stop ``` **On Last Client Disconnect** ``` yaml RCON_CMDS_LAST_DISCONNECT: |- kill @e[type=minecraft:boat] pregen start 200 ``` **Example of rules for new players** Uses 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. [Reference Article](https://www.minecraftforum.net/forums/minecraft-java-edition/redstone-discussion-and/2213523-detect-players-first-join) ``` yaml RCON_CMDS_STARTUP: |- /pregen start 200 /gamerule doFireTick false /team add New /team add Old RCON_CMDS_ON_CONNECT: |- /team join New @a[team=] /give @a[team=New] birch_boat /team join Old @a[team=New] RCON_CMDS_FIRST_CONNECT: |- /pregen stop RCON_CMDS_LAST_DISCONNECT: |- /kill @e[type=minecraft:boat] /pregen start 200 ``` ================================================ FILE: docs/configuration/interpolating.md ================================================ --- title: Modifying config files --- ## Replacing variables inside configs Sometimes 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. Or maybe you have some runtime information like the server name that needs to be set in your config files after the container starts. For those cases there is the option to replace defined variables inside your configs with environment variables defined at container runtime. When 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`). Variables 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. You 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. If 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`. For example, a `my.cnf` file could contain: ```toml [client] password = ${CFG_DB_PASSWORD} ``` ...a secret declared in the compose file with: ```yaml secrets: db_password: external: true ``` ...and finally the environment variable would be named with a `_FILE` suffix and point to the mounted secret: ```yaml environment: CFG_DB_PASSWORD_FILE: /run/secrets/db_password ``` Variables will be replaced in files with the following extensions: `.yml`, `.yaml`, `.txt`, `.cfg`, `.conf`, `.properties`. Specific files can be excluded by listing their name (without path) in the variable `REPLACE_ENV_VARIABLES_EXCLUDES`. Paths can be excluded by listing them in the variable `REPLACE_ENV_VARIABLES_EXCLUDE_PATHS`. Path excludes are recursive. Here is an example: ``` REPLACE_ENV_VARIABLES_EXCLUDE_PATHS="/data/plugins/Essentials/userdata /data/plugins/MyPlugin" ``` Here is a full example where we want to replace values inside a `database.yml`. ```yaml --- database: host: ${CFG_DB_HOST} name: ${CFG_DB_NAME} password: ${CFG_DB_PASSWORD} ``` This is how your `compose.yaml` file could look like: ```yaml title="compose.yaml" # Other docker compose examples in /examples services: minecraft: image: itzg/minecraft-server ports: - "25565:25565" volumes: - "mc:/data" environment: EULA: "TRUE" ENABLE_RCON: "true" RCON_PASSWORD: "testing" RCON_PORT: 28016 # enable env variable replacement REPLACE_ENV_VARIABLES: "TRUE" # define an optional prefix for your env variables you want to replace ENV_VARIABLE_PREFIX: "CFG_" # and here are the actual variables CFG_DB_HOST: "http://localhost:3306" CFG_DB_NAME: "minecraft" CFG_DB_PASSWORD_FILE: "/run/secrets/db_password" volumes: mc: rcon: secrets: db_password: file: ./db_password ``` ## Patching existing files JSON 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). The `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_". The following example shows a patch-set file where various fields in the `paper.yaml` configuration file can be modified and added: ```json { "patches": [ { "file": "/data/paper.yml", "ops": [ { "$set": { "path": "$.verbose", "value": true } }, { "$set": { "path": "$.settings['velocity-support'].enabled", "value": "${CFG_VELOCITY_ENABLED}", "value-type": "bool" } }, { "$put": { "path": "$.settings", "key": "my-test-setting", "value": "testing" } } ] } ] } ``` Supports the file formats: - JSON - JSON5 - Yaml - TOML, but processed output is not pretty ================================================ FILE: docs/configuration/jvm-options.md ================================================ # JVM Options ## Memory Limit By default, the image declares an initial and maximum Java memory-heap limit of 1 GB. There are several ways to adjust the memory settings: - `MEMORY`: "1G" by default, can be used to adjust both initial (`Xms`) and max (`Xmx`) memory heap settings of the JVM - `INIT_MEMORY`: independently sets the initial heap size - `MAX_MEMORY`: independently sets the max heap size The values of all three are passed directly to the JVM and support format/units as `[g|G|m|M|k|K]`. To have control over heap size, without relying on absolute memory sizes percentages are also supported using `%`. !!! info "RAMPercentage parameters" Percentage based heap sizing is enabled using `-XX:InitialRAMPercentage` for `INIT_MEMORY` and `-XX:MaxRAMPercentage` for `MAX_MEMORY`. For details on the function of these parameters look [here](https://www.baeldung.com/java-jvm-parameters-rampercentage). !!! example "Using docker run" ``` -e MEMORY=2G ``` or to use init and max memory: ``` -e INIT_MEMORY=1G -e MAX_MEMORY=4G ``` !!! example "Using compose file" ``` environment: MEMORY: 2G ``` or to use init and max memory: ``` environment: INIT_MEMORY: 1G MAX_MEMORY: 4G ``` To 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. !!! important 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). ## Extra JVM Options General JVM options can be passed to the Minecraft Server invocation by passing a `JVM_OPTS` environment 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. ``` docker run ... -e JVM_OPTS="-XsomeJVMOption -DpropName=value" ... ``` **NOTE** When declaring `JVM_OPTS` in a compose file's `environment` section with list syntax, **do not** include the quotes: ```yaml environment: - EULA=true - JVM_OPTS=-XsomeJVMOption -DpropName=value ``` Using object syntax is recommended and more intuitive: ```yaml environment: EULA: "true" JVM_OPTS: "-XsomeJVMOption -DpropName=value" # or without quotes # JVM_OPTS: -XsomeJVMOption -DpropName=value ``` 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.) For example, instead of passing ```yaml JVM_OPTS: -Dfml.queryResult=confirm -Dname=value ``` you can use ```yaml JVM_DD_OPTS: fml.queryResult=confirm,name=value ``` ## Enable Remote JMX for Profiling To 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: !!! example With `docker run` ``` -e ENABLE_JMX=true -e JMX_HOST=$HOSTNAME -p 7091:7091 ``` If needing to map to a different port, then also set the environment variable `JMX_PORT` to the desired host port. !!! example With a compose file: ```yaml environment: ENABLE_JMX: true JMX_HOST: ${HOSTNAME} JMX_PORT: "7092" ports: - "7092:7092" ``` ## Enable Aikar's Flags [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. The set of flags documented there can be added using -e USE_AIKAR_FLAGS=true When `MEMORY` is greater than or equal to 12G, then the Aikar flags will be adjusted according to the article. ## Enable MeowIce's Flags [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 The 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`. ================================================ FILE: docs/configuration/misc-options.md ================================================ ## Running with a custom server JAR If you would like to run a custom server JAR, set `-e TYPE=CUSTOM` and pass the custom server JAR via `CUSTOM_SERVER`. It can either be a URL or a container path to an existing JAR file. If it is a URL, it will only be downloaded into the `/data` directory if it wasn't already. As such, if you need to upgrade or re-download the JAR, then you will need to stop the container, remove the file from the container's `/data` directory, and start again. ## Force re-download of the server file For VANILLA, FORGE, BUKKIT, SPIGOT, PAPER, CURSEFORGE, SPONGEVANILLA server types, set `$FORCE_REDOWNLOAD` to some value (e.g. 'true) to force a re-download of the server file for the particular server type. by adding a `-e FORCE_REDOWNLOAD=true` to your command-line. For example, with Paper, it would look something like this: ``` docker run -d --pull=always \ -v /path/on/host:/data \ -e TYPE=PAPER -e FORCE_REDOWNLOAD=true \ -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server ``` ## Running as alternate user/group ID By 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`. The startup will also skip user switching if the `--user`/`-u` argument is passed to `docker run` or `user` is set on the compose service. ## Extra Arguments Arguments 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. See [Custom worlds directory path](../misc/world-data.md#custom-worlds-directory-path) for an example. ## Interactive and Color Console When 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. If 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. ## Server Shutdown Options To 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. !!! warning "Increase stop grace period" 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. 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. The `STOP_SERVER_ANNOUNCE_DELAY` can be bypassed by sending a `SIGUSR1` signal to the `mc-server-runner` process. `docker`: docker stop --signal SIGUSR1 mc `docker compose`: docker compose kill --signal SIGUSR1 ## Configuration Options for Minecraft Server Health Monitoring The image tags include specific variables to simplify configuration for monitoring the health of a Minecraft server: - `-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. - `-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. ## OpenJ9 Specific Options The openj9 image tags include specific variables to simplify configuration: - `-e TUNE_VIRTUALIZED=TRUE` : enables the option to [optimize for virtualized environments](https://www.eclipse.org/openj9/docs/xtunevirtualized/) - `-e TUNE_NURSERY_SIZES=TRUE` : configures nursery sizes where the initial size is 50% of the `MAX_MEMORY` and the max size is 80%. ## Customize Minecraft server logging The 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. Set the environment variable `GENERATE_LOG4J2_CONFIG` to "true" to enable the following features. !!! note This configures the server logging only. To diagnose container startup, set the `DEBUG` environment variable to "true". ### Customization via environment variables You can customize various aspects of the logging behavior using environment variables: - `LOG_LEVEL` : Root logger level (default: `info`) ``` -e LOG_LEVEL=debug ``` - `ROLLING_LOG_FILE_PATTERN` : Pattern for rolled log file names (default: `logs/%d{yyyy-MM-dd}-%i.log.gz`) ``` -e ROLLING_LOG_FILE_PATTERN="logs/archive/%d{yyyy-MM-dd}-%i.log.gz" ``` - `ROLLING_LOG_MAX_FILES` : Maximum number of archived log files to keep (default: `1000`) ``` -e ROLLING_LOG_MAX_FILES=30 ``` ### Customizing log message formats For 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): - `LOG_CONSOLE_FORMAT` : Format for console output (what you see in `docker logs`) Default: `[%d{HH:mm:ss}] [%t/%level]: %msg%n` - `LOG_FILE_FORMAT` : Format for file logs (written to `logs/latest.log`) Default: `[%d{HH:mm:ss}] [%t/%level]: %msg%n` - `LOG_TERMINAL_FORMAT` : Format for interactive terminal console (used with `docker attach`) Default: `[%d{HH:mm:ss} %level]: %msg%n` ### Example configurations Simple timestamp customization (most common use case): ```yaml environment: # What you see in docker logs LOG_CONSOLE_FORMAT: "[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%t/%level]: %msg%n" # What gets written to logs/latest.log LOG_FILE_FORMAT: "[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%t/%level]: %msg%n" ``` Advanced customization: ```yaml environment: LOG_LEVEL: debug # Custom ISO8601 format with logger names LOG_CONSOLE_FORMAT: "%d{ISO8601} %-5level [%t] %logger{36} - %msg%n" LOG_FILE_FORMAT: "%d{ISO8601} %-5level [%t] %logger{36} - %msg%n" ROLLING_LOG_MAX_FILES: 50 ``` ### Legacy ENABLE_ROLLING_LOGS option The `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. > **NOTE** The templated log4j2 configuration may interfere with interactive/color consoles [as described in the section above](#interactive-and-color-console) ## Timezone Configuration You can configure the timezone to match yours by setting the `TZ` environment variable: -e TZ=Europe/London such as: docker run -d -it --pull=always -e TZ=Europe/London -p 25565:25565 --name mc itzg/minecraft-server Or mounting `/etc/timezone` as readonly (not supported on Windows): -v /etc/timezone:/etc/timezone:ro such as: docker run -d -it --pull=always -v /etc/timezone:/etc/timezone:ro -p 25565:25565 --name mc itzg/minecraft-server ## HTTP Proxy You 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`. ## Using "noconsole" option Some older versions (pre-1.14) of Spigot required `--noconsole` to be passed when detaching stdin, which can be done by setting `-e CONSOLE=FALSE`. ## Explicitly disable GUI Some older servers get confused and think that the GUI interface is enabled. You can explicitly disable that by passing `-e GUI=FALSE`. ## Stop Duration When 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. Be 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). ## Setup only 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 `SETUP_ONLY` to `true`. ## Enable Flare Flags To enable the JVM flags required to fully support the [Flare profiling suite](https://blog.airplane.gg/flare), set the following variable: -e USE_FLARE_FLAGS=true Flare is built-in to Pufferfish/Purpur, and is available in [plugin form](https://github.com/TECHNOVE/FlarePlugin) for other server types. ## Enable support for optimized SIMD operations To enable support for optimized SIMD operations, the JVM flag can be set with the following variable: -e USE_SIMD_FLAGS=true SIMD optimized operations are supported by Pufferfish and Purpur. For example, to download configuration files for plugins: ```yaml environment: DOWNLOAD_EXTRA_CONFIGS: | plugins/WorldEdit- { "layers": [ { "block": "minecraft:bedrock", "height": 1 }, { "block": "minecraft:stone", "height": 2 }, { "block": "minecraft:sandstone", "height": 15 } ], "biome": "minecraft:desert" } ``` For 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). ### Custom Server Resource Pack You 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: docker run -d -e 'RESOURCE_PACK=http://link.com/to/pack.zip?=1' -e 'RESOURCE_PACK_SHA1=d5db29cd03a2ed055086cef9c31c252b4587d6d0' You can enforce the resource pack on clients by setting `RESOURCE_PACK_ENFORCE` to `TRUE` (default: `FALSE`). ### Level / World Save Name You can either switch between world saves or run multiple containers with different saves by using the `LEVEL` option, where the default is "world": docker run -d -e LEVEL=bonus ... > **NOTE:** if running multiple containers be sure to either specify a different `-v` host directory for each `LEVEL` in use or don't use `-v` and the container's filesystem will keep things encapsulated. > **INFO** Refer to the [data directory](../data-directory.md) section for a visual description of where the `$LEVEL` directory is situated. ### Server port > **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. If you must, the server port can be set like: !!! example "Using docker run" ``` docker run -d -e SERVER_PORT=25566 ... ``` !!! example "Using compose" ```yaml environment: SERVER_PORT: 25566 ``` **however**, be sure to change your port mapping accordingly and be prepared for some features to break. ### Custom server properties Some 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. Within a compose file, newline delimited entries can be declared as shown here: ```yaml CUSTOM_SERVER_PROPERTIES: | custom1=value1 defaultworldgenerator-port=f8c04631-f744-11ec-b260-f02f74b094e0 ``` When using `docker run` from a bash shell, the entries must be quoted with the `$'` syntax, such as ``` -e CUSTOM_SERVER_PROPERTIES=$'k1=v1\nk2=v2' ``` ### Other server property mappings !!! warning "Version compatibility" 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. | Environment Variable | Server Property | |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------| | ACCEPTS_TRANSFERS | [accepts-transfers](https://minecraft.wiki/w/Server.properties#accepts-transfers) | | ALLOW_FLIGHT | [allow-flight](https://minecraft.wiki/w/Server.properties#allow-flight) | | ALLOW_NETHER | [allow-nether](https://minecraft.wiki/w/Server.properties#allow-nether) | | ANNOUNCE_PLAYER_ACHIEVEMENTS | [announce-player-achievements](https://minecraft.wiki/w/Server.properties#announce-player-achievements) | | BROADCAST_CONSOLE_TO_OPS | [broadcast-console-to-ops](https://minecraft.wiki/w/Server.properties#broadcast-console-to-ops) | | BROADCAST_RCON_TO_OPS | [broadcast-rcon-to-ops](https://minecraft.wiki/w/Server.properties#broadcast-rcon-to-ops) | | BUG_REPORT_LINK | [bug-report-link](https://minecraft.wiki/w/Server.properties#bug-report-link) | | ENABLE_COMMAND_BLOCK | [enable-command-block](https://minecraft.wiki/w/Server.properties#enable-command-block) | | ENABLE_STATUS | [enable-status](https://minecraft.wiki/w/Server.properties#enable-status) | | ENFORCE_SECURE_PROFILE | [enforce-secure-profile](https://minecraft.wiki/w/Server.properties#enforce-secure-profile) | | ENTITY_BROADCAST_RANGE_PERCENTAGE | [entity-broadcast-range-percentage](https://minecraft.wiki/w/Server.properties#entity-broadcast-range-percentage) | | FORCE_GAMEMODE | [force-gamemode](https://minecraft.wiki/w/Server.properties#force-gamemode) | | FUNCTION_PERMISSION_LEVEL | [function-permission-level](https://minecraft.wiki/w/Server.properties#function-permission-level) | | GENERATE_STRUCTURES | [generate-structures](https://minecraft.wiki/w/Server.properties#generate-structures) | | GENERATOR_SETTINGS | [generator-settings](https://minecraft.wiki/w/Server.properties#generator-settings) | | HARDCORE | [hardcore](https://minecraft.wiki/w/Server.properties#hardcore) | | HIDE_ONLINE_PLAYERS | [hide-online-players](https://minecraft.wiki/w/Server.properties#hide-online-players) | | LOG_IPS | [log-ips](https://minecraft.wiki/w/Server.properties#log-ips) | | MANAGEMENT_SERVER_ALLOWED_ORIGINS | [management-server-allowed-origins](https://minecraft.wiki/w/Server.properties#management-server-allowed-origins) | | MANAGEMENT_SERVER_ENABLED | [management-server-enabled](https://minecraft.wiki/w/Server.properties#management-server-enabled) | | MANAGEMENT_SERVER_HOST | [management-server-host](https://minecraft.wiki/w/Server.properties#management-server-host) | | MANAGEMENT_SERVER_PORT | [management-server-port](https://minecraft.wiki/w/Server.properties#management-server-port) | | MANAGEMENT_SERVER_SECRET | [management-server-secret](https://minecraft.wiki/w/Server.properties#management-server-secret) | | MANAGEMENT_SERVER_TLS_ENABLED | [management-server-tls-enabled](https://minecraft.wiki/w/Server.properties#management-server-tls-enabled) | | MANAGEMENT_SERVER_TLS_KEYSTORE | [management-server-tls-keystore](https://minecraft.wiki/w/Server.properties#management-server-tls-keystore) | | MANAGEMENT_SERVER_TLS_KEYSTORE_PASSWORD | [management-server-tls-keystore-password](https://minecraft.wiki/w/Server.properties#management-server-tls-keystore-password) | | MAX_BUILD_HEIGHT | [max-build-height](https://minecraft.wiki/w/Server.properties#max-build-height) | | MAX_COMMAND_CHAIN_LENGTH | [max-command-chain-length](https://minecraft.wiki/w/Server.properties#max-command-chain-length) | | MAX_ENTITY_CRAMMING | [max-entity-cramming](https://minecraft.wiki/w/Server.properties#max-entity-cramming) | | MAX_ENTITY_COLLISION_RADIUS | [max-entity-collision-radius](https://minecraft.wiki/w/Server.properties#max-entity-collision-radius) | | MAX_FUNCTION_CHAIN_DEPTH | [max-function-chain-depth](https://minecraft.wiki/w/Server.properties#max-function-chain-depth) | | MAX_NEIGHBORS | [max-neighbors](https://minecraft.wiki/w/Server.properties#max-neighbors) | | MAX_CHAINED_NEIGHBOR_UPDATES | [max-chained-neighbor-updates](https://minecraft.wiki/w/Server.properties#max-chained-neighbor-updates) | | MAX_PLAYERS | [max-players](https://minecraft.wiki/w/Server.properties#max-players) | | MAX_TICK_TIME | [max-tick-time](https://minecraft.wiki/w/Server.properties#max-tick-time) | | MAX_WORLD_SIZE | [max-world-size](https://minecraft.wiki/w/Server.properties#max-world-size) | | NETWORK_COMPRESSION_THRESHOLD | [network-compression-threshold](https://minecraft.wiki/w/Server.properties#network-compression-threshold) | | ONLINE_MODE | [online-mode](https://minecraft.wiki/w/Server.properties#online-mode) | | OP_PERMISSION_LEVEL | [op-permission-level](https://minecraft.wiki/w/Server.properties#op-permission-level) | | PAUSE_WHEN_EMPTY_SECONDS | [pause-when-empty-seconds](https://minecraft.wiki/w/Server.properties#pause-when-empty-seconds) | | PLAYER_IDLE_TIMEOUT | [player-idle-timeout](https://minecraft.wiki/w/Server.properties#player-idle-timeout) | | PREVENT_PROXY_CONNECTIONS | [prevent-proxy-connections](https://minecraft.wiki/w/Server.properties#prevent-proxy-connections) | | PVP | [pvp](https://minecraft.wiki/w/Server.properties#pvp) | | RATE_LIMIT | [rate-limit](https://minecraft.wiki/w/Server.properties#rate-limit) | | REGION_FILE_COMPRESSION | [region-file-compression](https://minecraft.wiki/w/Server.properties#region-file-compression) | | RESOURCE_PACK_ID | [resource-pack-id](https://minecraft.wiki/w/Server.properties#resource-pack-id) | | RESOURCE_PACK_PROMPT | [resource-pack-prompt](https://minecraft.wiki/w/Server.properties#resource-pack-prompt) | | SERVER_NAME | [server-name](https://minecraft.wiki/w/Server.properties#server-name) | | SIMULATION_DISTANCE | [simulation-distance](https://minecraft.wiki/w/Server.properties#simulation-distance) | | SNOOPER_ENABLED | [snooper-enabled](https://minecraft.wiki/w/Server.properties#snooper-enabled) | | SPAWN_ANIMALS | [spawn-animals](https://minecraft.wiki/w/Server.properties#spawn-animals) | | SPAWN_MONSTERS | [spawn-monsters](https://minecraft.wiki/w/Server.properties#spawn-monsters) | | SPAWN_NPCS | [spawn-npcs](https://minecraft.wiki/w/Server.properties#spawn-npcs) | | SPAWN_PROTECTION | [spawn-protection](https://minecraft.wiki/w/Server.properties#spawn-protection) | | STATUS_HEARTBEAT_INTERVAL | [status-heartbeat-interval](https://minecraft.wiki/w/Server.properties#status-heartbeat-interval) | | SYNC_CHUNK_WRITES | [sync-chunk-writes](https://minecraft.wiki/w/Server.properties#sync-chunk-writes) | | USE_NATIVE_TRANSPORT | [use-native-transport](https://minecraft.wiki/w/Server.properties#use-native-transport) | | VIEW_DISTANCE | [view-distance](https://minecraft.wiki/w/Server.properties#view-distance) | ================================================ FILE: docs/css/extra.css ================================================ ================================================ FILE: docs/data-directory.md ================================================ Everything the container manages is located under the **container's** `/data` path, as shown here: ![](img/level-vs-world.drawio.png) !!! note 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. ### Attaching data directory to host filesystem In 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: -v /home/user/minecraft-data:/data ------------------------- ----- | | | +-- must always be /data | +-- replace with a directory on your host machine When 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. !!! important "Rootless, Podman, SELinux, AppArmor usage" When running rootless containers, such as with Podman, or using SELinux / AppArmor on your system, append ":Z" to the volume mapping. For example: ``` /home/user/minecraft-data:/data:Z ``` There might be a safer/better way to accommodate these systems. Please post an issue or PR if you have more information. With 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. ```yaml title="compose.yaml" services: mc: image: itzg/minecraft-server:latest pull_policy: daily ports: - 25565:25565 environment: EULA: "TRUE" tty: true stdin_open: true restart: unless-stopped volumes: # attach a directory relative to the directory containing this compose file - ./minecraft-data:/data ``` ### Converting anonymous `/data` volume to named volume If 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. !!! note In this example, it is assumed the original container was given a `--name` of "mc", so change the container identifier accordingly. You can also locate the Docker-managed directory from the `Source` field obtained from `docker inspect -f "{{json .Mounts}}"` First, stop the existing container: ``` shell docker stop mc ``` Use a temporary container to copy over the anonymous volume's content into a named volume, "mc" in this case: ``` shell docker run --rm --volumes-from mc -v mc:/new alpine cp -avT /data /new ``` Now you can recreate the container with any environment variable changes, etc by attaching the named volume created from the previous step: ``` shell docker run -d -it --name mc-new -v mc:/data -p 25565:25565 -e EULA=TRUE -e MEMORY=2G itzg/minecraft-server ``` ### Locating filesystem path of anonymous volume The `Source` field from the output of this command will show where the anonymous volume is mounted from: ``` shell docker inspect -f "{{json .Mounts}}" CONTAINER_NAME_OR_ID ``` !!! note On Windows with WSL the volumes path is `\\wsl$\docker-desktop-data\data\docker\volumes` ================================================ FILE: docs/img/.gitignore ================================================ /.*.bkp ================================================ FILE: docs/img/Minecraft server containment.xml ================================================ 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 ================================================ FILE: docs/img/docs-banner.txt ================================================ Generated at https://textcraft.net/ Line 1: "Click here for" Small, none, Minecraftia Line 2: "Documentation" Large, non, Minecrafter Overall settings: - drop shadow ================================================ FILE: docs/index.md ================================================ # Intro [![Docker Pulls](https://img.shields.io/docker/pulls/itzg/minecraft-server.svg)](https://hub.docker.com/r/itzg/minecraft-server/) [![Docker Stars](https://img.shields.io/docker/stars/itzg/minecraft-server.svg?maxAge=2592000)](https://hub.docker.com/r/itzg/minecraft-server/) [![GitHub Issues](https://img.shields.io/github/issues-raw/itzg/docker-minecraft-server.svg)](https://github.com/itzg/docker-minecraft-server/issues) [![Discord](https://img.shields.io/discord/660567679458869252?label=Discord&logo=discord)](https://discord.gg/DXfKpjB) [![Build and Publish](https://github.com/itzg/docker-minecraft-server/workflows/Build%20and%20Publish/badge.svg)](https://github.com/itzg/docker-minecraft-server/actions) [![](https://img.shields.io/badge/Donate-Buy%20me%20a%20coffee-orange.svg)](https://www.buymeacoffee.com/itzg) This docker image provides a Minecraft Server that will automatically download the latest stable version at startup. You can also run/upgrade to any specific version or the latest snapshot. See the _Versions_ section below for more information. To simply use the latest stable version, run docker run -d -it -p 25565:25565 -e EULA=TRUE itzg/minecraft-server where, in this case, the standard server port 25565 will be exposed on your host machine. !!! important "Persistent Data" 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. Using `docker run` add a `-v` option somewhere before the image name: ``` ... -v /path/on/host:/data itzg/minecraft-server ``` Using docker compose, add a `volumes` section to the service definition: ```yaml services: mc: # ... image and environment section volumes: # attach the relative directory 'data' to the container's /data path - ./data:data ``` !!! note 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. !!! info Be sure to always include `-e EULA=TRUE` in your commands and container definitions, as Mojang/Microsoft requires EULA acceptance. By 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. ## Using [Docker Compose](https://docs.docker.com/compose/) 1. Create a new directory 2. Put the contents of the file below in a file called `compose.yaml` 3. Run `docker compose up -d` in that directory 4. Done! Point your client at your host's name/IP address and port 25565. ```yaml title="compose.yaml" services: mc: image: itzg/minecraft-server:latest pull_policy: daily tty: true stdin_open: true ports: - "25565:25565" environment: EULA: "TRUE" volumes: # attach the relative directory 'data' to the container's /data path - ./data:/data ``` To apply changes made to the compose file, just run `docker compose up -d` again. Follow 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`. !!! note "Configurator Tool" 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. !!! note "More Compose Examples" There are more [examples located in the Github repo](https://github.com/itzg/docker-minecraft-server/tree/master/examples). !!! note "Deployment Examples" The [deployments page](misc/deployment/index.md) provides more examples of deployment with and beyond Docker Compose. ================================================ FILE: docs/misc/autopause-autostop/autopause.md ================================================ # Auto-Pause !!! important 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. An 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". !!! important **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). 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` 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. The 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. A 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. A `.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. A starting, example compose file has been provided in [the examples](https://github.com/itzg/docker-minecraft-server/blob/master/examples/autopause/compose.yml). !!! note 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. ## Additional configuration The following environment variables define the behaviour of auto-pausing: - `AUTOPAUSE_TIMEOUT_EST`, default `3600` (seconds) describes the time between the last client disconnect and the pausing of the process (read as timeout established) - `AUTOPAUSE_TIMEOUT_INIT`, default `600` (seconds) describes the time between server start and the pausing of the process, when no client connects inbetween (read as timeout initialized) - `AUTOPAUSE_TIMEOUT_KN`, default `120` (seconds) 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) - `AUTOPAUSE_PERIOD`, default `10` (seconds) describes period of the daemonized state machine, that handles the pausing of the process (resuming is done independently) - `AUTOPAUSE_KNOCK_INTERFACE`, default `eth0`
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. - `AUTOPAUSE_STATUS_RETRY_LIMIT`, default 10 - `AUTOPAUSE_STATUS_RETRY_INTERVAL`, default 2s !!! tip To troubleshoot, add `DEBUG_AUTOPAUSE=true` to see additional output ## Rootless Auto-Pause If 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". You might need to set change the default port forwarder from RootlessKit to slirp4netns. For Docker, see the following for setup: - https://docs.docker.com/engine/security/rootless/#networking-errors - https://rootlesscontaine.rs/getting-started/docker/#changing-the-port-forwarder For Podman, see the following for setup: - https://rootlesscontaine.rs/getting-started/podman/#changing-the-port-forwarder !!! example "Using docker run" -e AUTOPAUSE_KNOCK_INTERFACE=tap0 --cap-add=CAP_NET_RAW --network slirp4netns:port_handler=slirp4netns ================================================ FILE: docs/misc/autopause-autostop/autostop.md ================================================ # Auto-Stop An 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. !!! note the docker container variables have to be set accordingly (restart policy set to "no") and that the container has to be manually restarted. A `.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. A 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). Enable the Autostop functionality by setting: ``` -e ENABLE_AUTOSTOP=TRUE ``` The following environment variables define the behavior of auto-stopping: - `AUTOSTOP_TIMEOUT_EST`, default `3600` (seconds) describes the time between the last client disconnect and the stopping of the server (read as timeout established) - `AUTOSTOP_TIMEOUT_INIT`, default `1800` (seconds) describes the time between server start and the stopping of the server, when no client connects in-between (read as timeout initialized) - `AUTOSTOP_PERIOD`, default `10` (seconds) describes period of the daemonized state machine, that handles the stopping of the server - `AUTOPAUSE_STATUS_RETRY_LIMIT`, default 10 - `AUTOPAUSE_STATUS_RETRY_INTERVAL`, default 2s > To troubleshoot, add `DEBUG_AUTOSTOP=true` to see additional output ## Proxy Support If 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 ================================================ FILE: docs/misc/autoscale/autoscale.md ================================================ # Autoscaling (sleep when idle) Autoscaling (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). ## mc-router [mc-router](https://github.com/itzg/mc-router) is a Minecraft-aware router/multiplexer. - Routes players by the hostname they connect with (useful for multiple servers behind one port) - Can auto-start a backend container on join and stop it again after an idle timeout Examples: - See the mc-router section in [Examples](../examples.md#mc-router-with-auto-scale) ## Lazymc [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). - Players connect to the proxy; the proxy starts/stops the server container - Usually requires a static IP for the Minecraft container on a user-defined network Example: - See the Lazymc section in [Examples](../examples.md#lazymc-put-your-minecraft-server-to-rest-when-idle) ## Lazytainer [Lazytainer](https://github.com/vmorganp/Lazytainer) starts/stops containers based on network traffic. - Uses packet thresholds + inactivity timeouts (not Minecraft hostname aware) - Can be triggered by port scans/pings in noisy environments Example: - See the Lazytainer section in [Examples](../examples.md#lazytainer-stop-minecraft-container-based-on-traffic) ================================================ FILE: docs/misc/building.md ================================================ ## Simple image additions You can easily build upon the base image using an inline Dockerfile. ```yaml title="compose.yaml" services: mc: build: context: . dockerfile_inline: | FROM itzg/minecraft-server:latest RUN apt-get update && apt-get install -y \ webp \ && rm -rf /var/lib/apt/lists/* pull: true # Always pull new base image pull_policy: build restart: unless-stopped environment: EULA: true ports: - "25565:25565/tcp" volumes: - ./data:/data ``` Here is an example to add Nvidia GPU support for C2ME: ??? Example "C2ME GPU example" ```yaml title="compose.yaml" services: mc: build: context: . dockerfile_inline: | FROM itzg/minecraft-server:java25 # Install OpenCL loader and NVIDIA driver capabilities RUN apt-get update && apt-get install -y \ ocl-icd-libopencl1 \ opencl-headers \ clinfo \ && rm -rf /var/lib/apt/lists/* # 1. Create the vendor directory # 2. Tell OpenCL to use the NVIDIA library RUN mkdir -p /etc/OpenCL/vendors && \ echo "libnvidia-opencl.so.1" > /etc/OpenCL/vendors/nvidia.icd # Tell the NVIDIA container runtime to expose all GPU capabilities (including compute/utility) ENV NVIDIA_VISIBLE_DEVICES all ENV NVIDIA_DRIVER_CAPABILITIES compute,utility,graphics,video COPY ./mods /mods pull: true # Always pull new base image pull_policy: build restart: unless-stopped deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu] environment: EULA: true TYPE: "FABRIC" VERSION: 1.21.10 MEMORY: 8G MODRINTH_PROJECTS: |- fabric-api c2me ports: - "25565:25565/tcp" volumes: - ./data:/data ``` !!! tip "For advanced use only" 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. Be sure to confirm that the desired [version and variant of Java isn't already provided](../versions/java.md). ## Building image locally with alternate Java base An 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`: ```shell docker build --build-arg BASE_IMAGE=ghcr.io/graalvm/graalvm-ce:ol8-java11 -t IMG_PREFIX/minecraft-server:java11-graalvm . ``` ## Building a multi-architecture image Ensure buildx/BuildKit support is enabled and run the following to build multi-architecture and push to the repository as named by the image: ```shell docker buildx build --platform=linux/arm64 --platform=linux/arm/v7 --platform=linux/amd64 --tag IMG_PREFIX/minecraft-server --push . ``` To build for local images, multi-architecture is not supported, use the following with buildx to load the image into the local daemon: ```shell docker buildx build --tag IMG_PREFIX/minecraft-server --load . ``` or just a plain build ```shell docker build -t IMG_PREFIX/minecraft-server . ``` ## Installing extra packages The following build args can be set to install additional packages for the respective base image distro: - `EXTRA_DEB_PACKAGES` - `EXTRA_DNF_PACKAGES` - `EXTRA_ALPINE_PACKAGES` ================================================ FILE: docs/misc/contributing/development.md ================================================ ## Adding a server type Adding 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: 1. 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 2. Modify the type-specific behavior between the "start-utils" preamble and the hand-off to `start-setupWorld` at the end of the script 3. Develop and test the changes using the [iterative process described below](#iterative-script-development) 4. Add a case-entry to the `case "${TYPE^^}"` in [start-configuration](https://github.com/itzg/docker-minecraft-server/blob/master/scripts/start-configuration) 5. 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 6. [Submit a pull request](https://github.com/itzg/docker-minecraft-server/pulls) ## Iterative script development The 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. ```shell docker compose -f compose-dev.yml run --rm -it [-e key=value] mc-dev ``` !!! tip To speed up the development cycle, it is recommended to set `SETUP_ONLY` to `true` as part of the run command above. ## Using development copy of tools In the cloned repo, such as [`mc-image-helper`](https://github.com/itzg/mc-image-helper), install the distribution locally by running: ```shell ./gradlew installDist ``` The 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. Refer 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: ```shell docker build -t itzg/minecraft-server . ``` Mount the local mc-image-helper distribution directory as a volume in the container at the path `/usr/share/mc-image-helper`, such as ```shell docker run -it --rm \ -v /path/to/mc-image-helper/build/install/mc-image-helper:/usr/share/mc-image-helper \ -e EULA=true \ itzg/minecraft-server ``` In a compose file, include the volume mount in the `volumes` section of the container definition: ```yaml services: mc: # ... usual container definition volumes: - /path/to/mc-image-helper/build/install/mc-image-helper:/usr/share/mc-image-helper:ro ``` For Go base tools, run ```shell goreleaser release --snapshot --clean ``` Clone [itzg/github-releases-proxy](https://github.com/itzg/github-releases-proxy) and run it according to the instructions shown there. In the Docker build, configure the following ```shell --build-arg GITHUB_BASEURL=http://host.docker.internal:8080 \ --build-arg APPS_REV=1 ``` and declare one or more version overrides, such as ``` --build-arg MC_HELPER_VERSION=1.8.1-SNAPSHOT ``` ## Generating release notes The following git command can be used to provide the bulk of release notes content: ```shell script git log --invert-grep --grep "^ci:" --grep "^misc:" --grep "^docs:" --pretty="* %s" 1.1.0..1.2.0 ``` ================================================ FILE: docs/misc/contributing/docs.md ================================================ # Site documentation The 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/). !!! note The README.md rarely needs to be modified and only serves as a brief introduction to the project. The 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. It will be very helpful to view the rendered documentation as you're editing. To do that run the following from the top-level directory: ```shell docker compose -f compose-docs.yml -p zensical up ``` You can access the live documentation rendering at . ================================================ FILE: docs/misc/deployment/index.md ================================================ # More Deployment Info ## On Kubernetes ### Using Helm - itzg Helm Chart: - [GitHub repo](https://github.com/itzg/minecraft-server-charts) - [Helm Chart repo](https://itzg.github.io/minecraft-server-charts/) - [mcsh/server-deployment](https://github.com/mcserverhosting-net/charts) ### Using Shulker [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. ## With Ansible [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. ### Using the MASH playbook [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. - 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. - 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. - 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. ## On CloudFormation (AWS) If 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. ## Supporting Articles Below are supporting articles for server deployment. - "Zero to Minecraft Server with Docker Desktop and Compose" https://dev.to/rela-v/zero-to-minecraft-server-with-docker-desktop-and-compose-500a - 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. ================================================ FILE: docs/misc/examples.md ================================================ # Examples Various examples are [maintained in the repository](https://github.com/itzg/docker-minecraft-server/tree/master/examples). The sections below highlight a few particular ones. ## Bedrock compatible server Using 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". ```yaml title="compose.yaml" services: mc: image: itzg/minecraft-server:latest pull_policy: daily environment: EULA: "true" TYPE: "PAPER" PLUGINS: | https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot ports: - "25565:25565" - "19132:19132/udp" volumes: - ./data:/data ``` [Source](https://github.com/itzg/docker-minecraft-server/blob/master/examples/geyser/docker-compose.yml) ## mc-router with auto-scale Using [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. With `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. ```yaml title="compose.yaml" services: router: image: itzg/mc-router environment: IN_DOCKER: true AUTO_SCALE_DOWN: true AUTO_SCALE_UP: true AUTO_SCALE_DOWN_AFTER: 2h AUTO_SCALE_ASLEEP_MOTD: "Server is asleep. Join again to wake it up!" ports: - "25565:25565" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro vanilla: image: itzg/minecraft-server environment: EULA: "TRUE" labels: mc-router.host: "vanilla.example.com" paper: image: itzg/minecraft-server environment: EULA: "TRUE" TYPE: PAPER labels: mc-router.host: "paper.example.com" ``` [Source](https://github.com/itzg/mc-router/blob/main/examples/docker-autoscale/compose-minimal.yml) [More detailed example](https://github.com/itzg/mc-router/blob/main/examples/docker-autoscale/compose.yml) ## Lazymc - Put your Minecraft server to rest when idle With [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. ```yaml title="compose.yaml" # Lazymc requires that the minecraft server have a static IP. # # To ensure that our servers have a static IP we need to create # a network for our services to use. # # By default, Docker uses 172.17.0.0/16 subnet range. # So we need to create a new network in a different subnet # See the readme for more information. # # Please ensure that the subnet falls within the private CIDRs: # https://datatracker.ietf.org/doc/html/rfc1918#section-3 # # And that it is not in use by anything else. networks: minecraft-network: driver: bridge ipam: config: - subnet: 172.18.0.0/16 services: lazymc: image: ghcr.io/joesturge/lazymc-docker-proxy:latest # the IPs should start at .2 as .1 is reserved for the gateway networks: minecraft-network: ipv4_address: 172.18.0.2 restart: unless-stopped volumes: # you should mount the minecraft server dir under /server, using read only. - data:/server:ro # you need to supply the docker socket, so that the container can run docker command - /var/run/docker.sock:/var/run/docker.sock:ro ports: # lazymc-docker-proxy acts as a proxy, so there is # no need to expose the server port on the Minecraft container - "25565:25565" # Standard Docker Minecraft server, also works with other server types mc: image: itzg/minecraft-server:java21 pull_policy: daily # Assign a static IP to the server container networks: minecraft-network: ipv4_address: 172.18.0.3 # We need to add a label here so that lazymc-docker-proxy knows which # container to manage labels: # Set lazymc.enabled to true to enable lazymc on this container - lazymc.enabled=true # Required to find the container to manage it - lazymc.group=mc # Point to the service name of the Minecraft server - lazymc.server.address=mc:25565 tty: true stdin_open: true # This container should be managed solely by the lazymc container # so set restart to no, or else the container will start again... restart: no environment: EULA: "TRUE" volumes: - data:/data volumes: data: ``` [Source](https://github.com/joesturge/lazymc-docker-proxy/blob/master/docker-compose.yaml) ## Lazytainer - Stop Minecraft container based on traffic Monitors network traffic to the Minecraft containers. If there is traffic, the container runs, otherwise the container is stopped/paused. By 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. ```yaml title="compose.yaml" services: lazytainer: image: ghcr.io/vmorganp/lazytainer:master environment: VERBOSE: false ports: - 25565:25565 volumes: - /var/run/docker.sock:/var/run/docker.sock:ro labels: - lazytainer.group.minecraft.sleepMethod=stop - lazytainer.group.minecraft.ports=25565 - lazytainer.group.minecraft.minPacketThreshold=2 # Start after two incomming packets - lazytainer.group.minecraft.inactiveTimeout=600 # 10 minutes, to allow the server to bootstrap. You can probably make this lower later if you want. restart: unless-stopped network_mode: bridge mc: image: itzg/minecraft-server:latest pull_policy: daily environment: EULA: TRUE TYPE: PAPER MEMORY: 4G volumes: - ./data:/data labels: - lazytainer.group=minecraft depends_on: - lazytainer network_mode: service:lazytainer tty: true stdin_open: true restart: unless-stopped ``` [Source](https://github.com/itzg/docker-minecraft-server/blob/master/examples/lazytainer/docker-compose.yml) ================================================ FILE: docs/misc/healthcheck.md ================================================ ## Healthcheck This 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` ``` CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b418af073764 mc "/start" 43 seconds ago Up 41 seconds (healthy) 0.0.0.0:25565->25565/tcp, 25575/tcp mc ``` You can also query the container's health in a script-friendly way: ``` shell > docker container inspect -f "{{.State.Health.Status}}" mc healthy ``` There'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: ``` yaml healthcheck: test: mc-health start_period: 1m interval: 5s retries: 20 ``` Some 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`. The [health check in a Compose service declaration](https://docs.docker.com/reference/compose-file/services/#healthcheck) can also be disabled using: ```yaml healthcheck: disable: true test: ["NONE"] ``` ### Health checks for older versions This 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). ================================================ FILE: docs/misc/log4j.md ================================================ **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. ================================================ FILE: docs/misc/raspberrypi.md ================================================ # Running on RaspberryPi To 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`. !!! note You may need to lower the memory allocation, such as `-e MEMORY=750m` !!! note 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). ================================================ FILE: docs/misc/related-projects.md ================================================ ### [itzg/minecraft-bedrock-server](https://github.com/itzg/docker-minecraft-bedrock-server) Docker image that runs a Minecraft Bedrock server. ### [mc-router](https://github.com/itzg/mc-router) Lightweight multiplexer/proxy for Minecraft Java servers. Provided as a stand-alone application and a Docker image. ### [itzg/mc-proxy](https://github.com/itzg/docker-bungeecord/) Docker image that runs a proxy powered by Bungeecord, Velocity, or Waterfall ### [itzg/mc-backup](https://github.com/itzg/docker-mc-backup) Docker image that runs as a side-car container to backup world data. ### [rcon-cli](https://github.com/itzg/rcon-cli) A tool that is bundled with this image to provide CLI access to an RCON endpoint. ### [mc-monitor](https://github.com/itzg/mc-monitor) A tool that is bundled with this image that provides health checks and metrics reporting, such as a Prometheus exporter or a telegraf data source. ### [mc-image-helper](https://github.com/itzg/mc-image-helper) A tool that is bundled with this image to provide complex, re-usable preparation operations. ### [itzg/rcon](https://github.com/itzg/docker-rcon-web-admin) An image that dockerizes [rcon-web-admin](https://github.com/rcon-web-admin/rcon-web-admin). ================================================ FILE: docs/misc/troubleshooting.md ================================================ 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). To troubleshoot just the command-line used to start the Minecraft server, set the environment variable `DEBUG_EXEC` to `true`. To troubleshoot any issues with memory allocation reported by the JVM, set the environment variable `DEBUG_MEMORY` to `true`. If you are experiencing any issues with the "Changing ownership of /data" step, that can be disabled by setting `SKIP_CHOWN_DATA` to `true`. To confirm the image version that has been pulled, use the following command, replacing `itzg/minecraft-server` as needed for specific image tags: ## Image labels ```shell docker image inspect itzg/minecraft-server -f "{{json .Config.Labels}}" ``` such as ```json { "org.opencontainers.image.authors": "... <...@gmail.com>", "org.opencontainers.image.created": "2025-04-03T02:15:51.405Z", "org.opencontainers.image.description": "Docker image that provides a Minecraft Server for Java Edition that automatically downloads selected version at startup", "org.opencontainers.image.licenses": "Apache-2.0", "org.opencontainers.image.ref.name": "ubuntu", "org.opencontainers.image.revision": "d6897a649ecbc16b5fb2e1500e24b64ef80270a0", "org.opencontainers.image.source": "https://github.com/itzg/docker-minecraft-server", "org.opencontainers.image.title": "docker-minecraft-server", "org.opencontainers.image.url": "https://github.com/itzg/docker-minecraft-server", "org.opencontainers.image.version": "java21" } ``` The labels that are most interesting are: - `org.opencontainers.image.created` : the date/time the image was built - `org.opencontainers.image.revision` : which maps to - `org.opencontainers.image.version` : image tag and variant [as described in this page](../versions/java.md) ================================================ FILE: docs/misc/world-data.md ================================================ ## Downloadable world Instead 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. docker run -d -e WORLD=http://www.example.com/worlds/MySave.zip ... !!! note 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. !!! note If the archive contains more than one `level.dat`, then the one to select can be picked with `WORLD_INDEX`, which defaults to 1. ## Cloning world from a container path The `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. For 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. ``` docker run ... -v $HOME/worlds:/worlds:ro -e WORLD=/worlds/basic ``` The following diagram shows how this option can be used in a compose deployment with a relative directory: ![](../img/world-copy-compose-project.drawio.png) ## Overwrite world on start The 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. ## Custom worlds directory path To set a custom worlds directory for the Multiverse plugin on a baremetal server, you'd pass the `--world-dir` argument after the jar file. In 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: ``` docker run -d -e EXTRA_ARGS='--world-dir ./worlds/' ``` `--world-container`, `-W`, and `--universe` are aliases to `--world-dir` and can also be used. ## Datapacks Datapacks 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): * `DATAPACKS`: comma-separated list of zip file URL, zip file in container, or directory in container * `DATAPACKS_FILE`: a text file within the container where each line is a zip file URL, zip file in container, or directory in container * `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` * `REMOVE_OLD_DATAPACKS_DEPTH`: default is 16 * `REMOVE_OLD_DATAPACKS_INCLUDE`: default is `*.zip` * `REMOVE_OLD_DATAPACKS_EXCLUDE`: default is empty Datapacks will be placed in `/data/$LEVEL/datapacks` ## VanillaTweaks [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. The share code is the part following the hash sign, as shown here: ``` https://vanillatweaks.net/share/#MGr52E ------ | +- share code MGr52E ``` Accepted Parameters: - `VANILLATWEAKS_FILE`: comma separated list of JSON VanillaTweak pack files accessible within the container - `VANILLATWEAKS_SHARECODE`: comma separated list of share codes Example of expected VanillaTweaks share codes: !!! note ResourcePacks, DataPacks, and CraftingTweaks all have separate sharecodes ``` yaml VANILLATWEAKS_SHARECODE: MGr52E,tF1zL2,LnEDwT ``` !!! note 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. Example of expected VanillaTweaks files: ``` yaml VANILLATWEAKS_FILE: /config/vt-datapacks.json,/config/vt-craftingtweaks.json,/config/vt-resourcepacks.json ``` ``` json title="DataPacks json" { "type": "datapacks", "version": "1.21", "packs": { "gameplay changes": [ "graves", "multiplayer sleep", "armored elytra" ], "teleport commands": ["tpa"] } } ``` ``` json title="ResourcePacks json" { "type": "resourcepacks", "version": "1.21", "packs": { "aesthetic": ["CherryPicking", "BlackNetherBricks", "AlternateBlockDestruction"] } } ``` ``` json title="CraftingTweaks Json" { "type": "craftingtweaks", "version": "1.21", "packs": { "quality of life": [ "dropper to dispenser", "double slabs", "back to blocks" ] } } ``` ================================================ FILE: docs/mods-and-plugins/curseforge-files.md ================================================ # Auto-download from CurseForge Mods 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. A 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. !!! warning "CurseForge API key usage" 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). ## Project-file references !!! tip Individual project files typically represent a version of the mod/plugin, but CurseForge refers to these items broadly as "files" rather than "versions". The following formats are supported in the list of project-file references: - A project page URL, such as `https://www.curseforge.com/minecraft/mc-mods/jei`. _The newest applicable file will be automatically selected._ - A file page URL, such as `https://www.curseforge.com/minecraft/mc-mods/jei/files/4593548` - Project slug, such as `jei`. _The newest applicable file will be automatically selected._ - Project ID, such as `238222`. _The newest applicable file will be automatically selected._ - Project slug or ID, `:`, and a file ID, such as `jei:4593548` or `238222:4593548` - 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. - An `@` followed by the **container path** to a listing file !!! info "More about listing files" Each line in the listing file is processed as one of the references above; however, blank lines and comments that start with `#` are ignored. Make sure to place the listing file in a mounted directory/volume or declare an appropriate mount for it. 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 ```text # This comment is ignored jei:10.2.1.1005 # This and previous blank line are ignore geckolib aquaculture naturalist ``` !!! tip "Multi-line values in Docker Compose" 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 ```yaml CURSEFORGE_FILES: | geckolib aquaculture naturalist ``` ## Dependencies The 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. ================================================ FILE: docs/mods-and-plugins/index.md ================================================ # Working with mods and plugins ## Modpack platforms By 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). The following are some supported modpack platforms: - [Modrinth](../types-and-platforms/mod-platforms/modrinth-modpacks.md) - [CurseForge](../types-and-platforms/mod-platforms/auto-curseforge.md) - [Feed the Beast](../types-and-platforms/mod-platforms/ftb.md) ## Download automation On the left, there are sections describing some download automation options. ## Mods vs Plugins The 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". Typically, 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. ## Optional plugins, mods, and config attach points There are optional volume paths that can be attached to supply content to be copied into the data area: `/plugins` : 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. !!! example "Using Cardboard plugins with Fabric" ```yaml environment: EULA: "TRUE" TYPE: "FABRIC" MODRINTH_PROJECTS: | fabric-api cardboard USES_PLUGINS: true ``` `/mods` : 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`. `/config` : 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`. By 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`. If 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. You can also specify the `REMOVE_OLD_MODS_DEPTH` (default is 16) variable to only delete files up to a certain level. For 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. These 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. !!! information "Multiple source directories" `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. For example, in a compose file: ```yaml environment: # ...EULA, etc TYPE: PAPER # matches up to volumes declared below COPY_PLUGINS_SRC: /plugins-common,/plugins-local volumes: - mc-data:/data # For example, reference a shared directory used by several projects - ../plugins-common:/plugins-common:ro # and add plugins unique to this project - ./plugins:/plugins-local:ro ``` Alternatively, you can declare other directories along with files and URLs to use in [the `MODS` / `PLUGINS` variables](#modsplugins-list). ## Applying extra configuration files You 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. The 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. !!! example With `docker run` ``` -e APPLY_EXTRA_FILES=destination ![Version ID](../img/modrinth-version-id.drawio.png) ### Examples | Description | Example projects entry | |---------------------------------|-------------------------------------------------------| | Select latest version | `fabric-api` | | Select specific version | `fabric-api:bQZpGIz0`
`fabric-api:0.119.2+1.21.4` | | Select latest beta version | `fabric-api:beta` | | Latest version using project ID | `P7dR8mSH` | | Latest version of datapack | `datapack:terralith` | | Specific version of datapack | `datapack:terralith:2.5.5` | | Mod loader override | `fabric:fabric-api`
`fabric:fabric-api:bQZpGIz0` | | Projects Listing File | `@/path/to/modrinth-mods.txt` | ### Notes !!! info "More about listing files" Each line in the listing file is processed as one of the references above; however, blank lines and comments that start with `#` are ignored. Make sure to place the listing file in a mounted directory/volume or declare an appropriate mount for it. 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 ```text # This comment is ignored fabric-api # This and previous blank line are ignore cloth-config datapack:terralith ``` !!! note "Auto-removal" 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. !!! note "Disable processing" To temporarily disable processing of the `MODRINTH_PROJECTS` list, then comment out the `MODRINTH_PROJECTS` environment variable. ## Version from Projects When 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`. !!! example Given the environment variables ```yaml MODRINTH_PROJECTS: | viaversion viabackwards griefprevention discordsrv VERSION_FROM_MODRINTH_PROJECTS: true ``` 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. ## Extra options `MODRINTH_DOWNLOAD_DEPENDENCIES` : Can be set to `none` (the default), `required`, or `optional` to download required and/or optional dependencies. `MODRINTH_PROJECTS_DEFAULT_VERSION_TYPE` : 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. `MODRINTH_LOADER` : When using a custom server, set this to specify which loader type will be requested during lookups ================================================ FILE: docs/mods-and-plugins/packwiz.md ================================================ # Packwiz Modpacks [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. To configure server mods using a packwiz modpack, set the `PACKWIZ_URL` environment variable to the location of your `pack.toml` modpack definition: ``` docker run -d --pull=always \ -v /path/on/host:/data -e TYPE=FABRIC \ -e "PACKWIZ_URL=https://example.com/modpack/pack.toml" \ itzg/minecraft-server ``` packwiz 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). !!! note 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. ================================================ FILE: docs/mods-and-plugins/spiget.md ================================================ # Auto-download using Spiget The `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. !!! important "SPIGET not SPIGOT" The variable is purposely spelled SPIG**E**T with an "E" The **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 https://www.spigotmc.org/resources/luckperms.28140/ ===== For 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: -e SPIGET_RESOURCES=28140,34315 !!! note 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). ================================================ FILE: docs/requirements.txt ================================================ click==8.3.1 colorama==0.4.6 deepmerge==2.0 ghp-import==2.1.0 griffe==2.0.0 Jinja2==3.1.6 Markdown==3.10.2 MarkupSafe==3.0.3 mergedeep==1.3.4 mkdocs==1.6.1 mkdocs-autorefs==1.4.4 mkdocs-get-deps==0.2.2 mkdocstrings==1.0.3 mkdocstrings-python==2.0.3 packaging==26.0 pathspec==1.0.4 platformdirs==4.9.4 Pygments==2.19.2 pymdown-extensions==10.21 python-dateutil==2.9.0.post0 PyYAML==6.0.3 pyyaml_env_tag==1.1 six==1.17.0 watchdog==6.0.0 zensical==0.0.27 ================================================ FILE: docs/sending-commands/commands.md ================================================ --- title: With Docker --- [RCON](http://wiki.vg/RCON) is enabled by default, so you can `exec` into the container to access the Minecraft server console: ``` docker exec -i mc rcon-cli ``` Note: The `-i` is required for interactive use of rcon-cli. To run a simple, one-shot command, such as stopping a Minecraft server, pass the command as arguments to `rcon-cli`, such as: ``` docker exec mc rcon-cli stop ``` _The `-i` is not needed in this case._ ## When RCON is disabled If 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: ```shell docker exec --user 1000 mc mc-send-to-console op player | | +- container name +- Minecraft commands start here ``` ## Enabling interactive console In order to attach and interact with the Minecraft server make sure to enable TTY and keep stdin open. !!! example With `docker run` use the `-it` arguments: ```shell docker run -d -it -p 25565:25565 --name mc itzg/minecraft-server ``` or with a compose file: ```yaml services: minecraft: stdin_open: true tty: true ``` With that you can attach and interact at any time using the following, replacing the `{...}` placeholders. ...when container is created with `docker run` ``` docker attach {container name or ID} ``` ...or when declared using a compose file ``` docker compose attach {service name} ``` and then Control-p Control-q to **detach**. !!! info "RCON is required for fully interactive, color console" RCON must be enabled, which is the default, in order to use a fully interactive console with auto-completion and colorized log output. ================================================ FILE: docs/sending-commands/ssh.md ================================================ --- title: Over SSH --- The container can host an SSH console. It is enabled by setting `ENABLE_SSH` to `true`. The SSH server only supports password based authentication. The password is the same as the RCON password. !!! question 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. The SSH server runs on port `2222` inside the container. ??? tip "Tip: Exposing the SSH port" !!! warning "Security Implications" 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. 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). 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`). 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). ```yaml title="compose.yaml" services: mc: ports: - '25565:25565' - '2222:2222' ``` ## Connecting Connecting should be as simple as running ```bash ssh anyuser@127.0.0.1 -p 2222 ``` and typing in the RCON password. ## Environment variables | Environment Variable | Usage | Default | | -------------------- | ------------------------- | ------- | | `ENABLE_SSH` | Enable remote SSH console | `false` | ## Example ```yaml title="compose.yaml" services: mc: image: itzg/minecraft-server:latest pull_policy: daily tty: true stdin_open: true ports: - "25565:25565" - "2222:2222" environment: EULA: "TRUE" ENABLE_SSH: true RCON_PASSWORD_FILE: /run/secrets/rcon_pass volumes: # attach the relative directory 'data' to the container's /data path - ./data:/data secrets: rcon_pass: file: ./rcon_password ``` ================================================ FILE: docs/sending-commands/websocket.md ================================================ --- title: With WebSocket --- With `WEBSOCKET_CONSOLE` set to `true`, logs can be streamed, and commands sent, over a WebSocket connection. The API is available on `/console`. ## Password A 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`. ??? Example "Examples" ```js title="JavaScript example" let socket = new WebSocket("http://localhost:80/websocket", ["mc-server-runner-ws-v1", "rcon-password"]); ``` ## Allowed origins A list of comma-separated allowed origins should be supplied with `WEBSOCKET_ALLOWED_ORIGINS`. Origin checking can be disabled with `WEBSOCKET_DISABLE_ORIGIN_CHECK`. ## Listen address The 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. ## Log history When a connection is established, the last 50 (by default, configurable with `WEBSOCKET_LOG_BUFFER_SIZE`) log lines are sent with a `logHistory` type message. ??? tip "Tip: Remember to forward the WebSocket port on the host" !!! warning "Security Implications" 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. 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). 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`). 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). ```yaml title="compose.yaml" services: mc: ports: - '25565:25565' - '80:80' ``` ## Environment variables | Environment Variable | Usage | Default | | ---------------------------------- | ---------------------------------------------------------- | ------------ | | `WEBSOCKET_CONSOLE` | Allow remote shell over WebSocket | `false` | | `WEBSOCKET_ADDRESS` | Bind address for WebSocket server | `0.0.0.0:80` | | `WEBSOCKET_DISABLE_ORIGIN_CHECK` | Disable checking if origin is trusted | `false` | | `WEBSOCKET_ALLOWED_ORIGINS` | Comma-separated list of trusted origins | ` ` | | `WEBSOCKET_PASSWORD` | Password will be the same as RCON_PASSWORD if unset | ` ` | | `WEBSOCKET_DISABLE_AUTHENTICATION` | Disable WebSocket authentication | `false` | | `WEBSOCKET_LOG_BUFFER_SIZE` | Number of log lines to save and send to connecting clients | `50` | ## API Schema ```ts title="API Schema" interface StdinMessage { type: "stdin"; data: string; } interface StdoutMessage { type: "stdout"; data: string; } interface StderrMessage { type: "stderr"; data: string; } interface LogHistoryMessage { type: "logHistory"; lines: string[]; } interface AuthFailureMessage { type: "authFailure"; reason: string; } // Messages sent from Client -> Server export type ClientMessage = StdinMessage; // Messages sent from Server -> Client export type ServerMessage = | StdoutMessage | StderrMessage | LogHistoryMessage | AuthFailureMessage; ``` ================================================ FILE: docs/types-and-platforms/index.md ================================================ # Server Types and Modpack Platforms Server types can be selected by setting the `TYPE` environment variable to one of the types described in these sections. The 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". **From the sections to the left...** The **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`. The individual **server types** allow for selecting the mod loader / server type and specific versions of those. !!! important 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. ================================================ FILE: docs/types-and-platforms/mod-platforms/auto-curseforge.md ================================================ # Auto CurseForge To 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. ## API Key !!! warning "CurseForge API key usage" 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`. 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. Example if your key is `$11$22$33aaaaaaaaaaaaaaaaaaaaaaaaaa`: ```yaml title="compose.yaml" environment: CF_API_KEY: '$$11$$22$$33aaaaaaaaaaaaaaaaaaaaaaaaaa' ``` If you use `docker run` you will need to make sure to use single quotes: ```shell docker run ... -e CF_API_KEY='$11$22$33aaaaaaaaaaaaaaaaaaaaaaaaaa' ``` 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**. ```title=".env" CF_API_KEY='$11$22$33aaaaaaaaaaaaaaaaaaaaaaaaaa' ``` The variable should to be referenced from the compose file, such as: ```yaml title="compose.yaml" environment: CF_API_KEY: ${CF_API_KEY} ``` The .env file should be placed in the same directory as your compose file like so: ``` minecraft-server/ ├── .env ├── compose.yaml ├── data/ ``` To use the equivalent with `docker run` you need to specify the `.env` file explicitly: ```shell docker run --env-file=.env itzg/minecraft-server ``` Alternately you can use [docker secrets](https://docs.docker.com/compose/how-tos/use-secrets/) with a `CF_API_KEY_FILE` environment variable: ```yaml title="compose.yaml" service: environment: CF_API_KEY_FILE: /run/secrets/cf_api_key secrets: - cf_api_key secrets: cf_api_key: file: cf_api_key.secret ``` !!! note Be sure to use the appropriate [image tag for the Java version compatible with the modpack](../../versions/java.md). 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. ## Usage Use one of the following to specify the modpack to install: Pass 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". !!! example "Using CF_PAGE_URL" ```yaml title="Using compose.yaml" environment: # ... MODPACK_PLATFORM: AUTO_CURSEFORGE # allocate from https://console.curseforge.com/ and set in .env file CF_API_KEY: ${CF_API_KEY} CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/all-the-mods-8 ``` ```title="Using docker run" 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 ``` Instead 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 ![cf-slug](../../img/cf-slug.png) !!! example "Using CF_SLUG" ```yaml title="Using compose.yaml" environment: # ... MODPACK_PLATFORM: AUTO_CURSEFORGE # allocate from https://console.curseforge.com/ and set in .env file CF_API_KEY: ${CF_API_KEY} CF_SLUG: all-the-mods-8 ``` ```title="Using docker run" docker run -e CF_API_KEY=${CF_API_KEY} -e TYPE=AUTO_CURSEFORGE -e CF_SLUG=all-the-mods-8 ``` ### Pinning modpack and mod loader versions The 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. - Use `CF_PAGE_URL`, but include the full URL to a specific file - Set `CF_FILE_ID` to the numerical file ID - Specify a substring to match the desired filename with `CF_FILENAME_MATCHER` The following shows where to get the URL to the specific file and also shows where the file ID is located: ![cf-file-id](../../img/cf-file-id.png) The following examples all refer to version 1.0.7 of ATM8: ```yaml CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/all-the-mods-8/files/4248390 ``` ```yaml CF_SLUG: all-the-mods-8 CF_FILE_ID: "4248390" ``` ```yaml CF_SLUG: all-the-mods-8 CF_FILENAME_MATCHER: 1.0.7 ``` Pinning modpack version also pins the mod loader (to the version specified by the modpack). Mod loader version cannot be pinned independently of the modpack. ### Custom modloader versions By 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: - `CF_MOD_LOADER_VERSION`: Override the mod loader version (e.g., `43.4.22`) !!! example "Override mod loader version" ```yaml environment: MODPACK_PLATFORM: AUTO_CURSEFORGE CF_API_KEY: ${CF_API_KEY} CF_SLUG: all-the-mods-8 CF_MOD_LOADER_VERSION: "43.4.22" ``` ```title="Using docker run" docker run -e CF_MOD_LOADER_VERSION=43.4.22 -e CF_SLUG=my-fabric-pack ... ``` !!! warning "Compatibility" 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. ## Manual Downloads For 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. !!! warning "Download using a browser" 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. !!! example Assuming Docker compose is being used: 1. Create a directory next to the `compose.yaml` file. The name doesn't matter, but "downloads" is the common convention 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 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 4. Re-run `docker compose up -d` to apply the changes ```yaml volumes: ./downloads:/downloads ``` ## Unpublished Modpacks If 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**. !!! example ```yaml services: mc: image: itzg/minecraft-server:latest pull_policy: daily environment: EULA: true MODPACK_PLATFORM: AUTO_CURSEFORGE # allocate from https://console.curseforge.com/ and set in .env file CF_API_KEY: ${CF_API_KEY} CF_MODPACK_MANIFEST: /manifests/manifest.json CF_SLUG: "custom" volumes: - ./manifests:/manifests:ro ``` where an exported manifest file should look like: ```json { "minecraft": { "version": "1.20.4", "modLoaders": [ { "id": "fabric-0.15.3", "primary": true } ] }, "manifestType": "minecraftModpack", "manifestVersion": 1, "name": "Custom", "author": "", "files": [ { "projectID": 351725, "fileID": 4973035, "required": true }, { "projectID": 306612, "fileID": 5010374, "required": true } ], "overrides": "overrides" } ``` ## Exclude client mods Quite 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: Mods 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` !!! note `CF_FORCE_INCLUDE_MODS` will not download additional mods. For additional mods, refer to [the `CURSEFORGE_FILES` variable](../../mods-and-plugins/curseforge-files.md). A mod's project ID can be obtained from the right hand side of the project page: ![cf-project-id](../../img/cf-project-id.png) For 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). If needing to iterate on the options above, set `CF_FORCE_SYNCHRONIZE` to "true" to ensure the exclude/includes are re-evaluated. !!! important 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. !!! tip "Embedded comments" Comments can be embedded in the list using the `#` character. ```yaml CF_EXCLUDE_MODS: | # Exclude client-side mods not published correctly creative-core default-options ``` ## Excluding Overrides Files Modpack 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. ### Ant-style paths Ant-style paths can include the following globbing/wildcard symbols: | Symbol | Behavior | |--------|---------------------------------------------------------| | `*` | Matches zero, one, or many characters except a slash | | `**` | Matches zero, one, or many characters including slashes | | `?` | Matches one character | !!! example The following compose `environment` entries show how to exclude Iris and Sodium mods from the overrides ```yaml CF_OVERRIDES_EXCLUSIONS: mods/iris*.jar,mods/sodium*.jar ``` or using newline delimiter, which improves maintainability ```yaml CF_OVERRIDES_EXCLUSIONS: | mods/iris*.jar mods/sodium*.jar ``` ## World/Level Data 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 `CF_SET_LEVEL_FROM` to either: - `WORLD_FILE` - `OVERRIDES` ## Ignore missing files Some 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`. !!! hint A warning log will indicate what files were found to be missing from the previous installation run, such as ``` Re-installing due to missing files from modpack: [mods/gregtech-2.6.2-beta.jar] ``` !!! example In a Compose file ```yaml environment: CF_IGNORE_MISSING_FILES: | mods/gregtech-2.6.2-beta.jar mods/*.jar ``` ## Extra options Other configuration available: - `CF_PARALLEL_DOWNLOADS` (default is 4): specify how many parallel mod downloads to perform - `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. - `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. ================================================ FILE: docs/types-and-platforms/mod-platforms/curseforge.md ================================================ # Manual CurseForge server packs !!! warning "Deprecated" This mod platform type is deprecated. Please use [AUTO_CURSEFORGE](auto-curseforge.md) for new deployments. Enable this server mode by setting `MODPACK_PLATFORM`, `MOD_PLATFORM` or `TYPE` to "CURSEFORGE" along with the following specific variables. You need to specify a modpack to run, using the `CF_SERVER_MOD` environment variable. A CurseForge server modpack is available together with its respective client modpack at . Now you can add a `-e CF_SERVER_MOD=name_of_modpack.zip` to your command-line. ```shell docker run -d --pull=always -v /path/on/host:/data -e TYPE=CURSEFORGE \ -e CF_SERVER_MOD=SkyFactory_4_Server_4.1.0.zip \ -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server ``` If you want to keep the pre-download modpacks separate from your data directory, then you can attach another volume at a path of your choosing and reference that. The following example uses `/modpacks` as the container path as the pre-download area: ```shell docker run -d --pull=always -v /path/on/host:/data \ -v /path/to/modpacks:/modpacks -e TYPE=CURSEFORGE \ -e CF_SERVER_MOD=/modpacks/SkyFactory_4_Server_4.1.0.zip \ -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server ``` ### Modpack data directory By 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.) The directory can be changed by setting `CF_BASE_DIR`, such as `-e CF_BASE_DIR=/data`. ### Buggy start scripts Some 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`. ### Fixing "unable to launch forgemodloader" If 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): unable to launch forgemodloader then you apply a workaround by adding this to the run invocation: -e FTB_LEGACYJAVAFIXER=true ================================================ FILE: docs/types-and-platforms/mod-platforms/ftb.md ================================================ # Feed the Beast !!! note Requires one of the Ubuntu with Hotspot images listed in [the Java versions section](../../versions/java.md). [Feed the Beast application](https://www.feed-the-beast.com/) modpacks are supported by setting `MODPACK_PLATFORM`, `MOD_PLATFORM` or `TYPE` to "FTBA" !!! note The "A" at the end of "FTBA" is important. The value "FTB" used to be an alias for "CURSEFORGE". This 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. ### Environment Variables: - `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: ``` https://www.feed-the-beast.com/modpacks/23-ftb-infinity-evolved-17 ^^ ``` - `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). - `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" ### Upgrading If 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. ### Example The following example runs the latest version of [FTB Presents Direwolf20 1.12](https://ftb.neptunepowered.org/pack/ftb-presents-direwolf20-1-12/): ``` shell docker run -d --pull=always -v /path/on/host:/data \ -e EULA=TRUE -e TYPE=FTBA \ -e FTB_MODPACK_ID=31 -p 25565:25565 \ itzg/minecraft-server:java8-multiarch ``` ================================================ FILE: docs/types-and-platforms/mod-platforms/gtnh.md ================================================ # Auto-setup GTNH server [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. As 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". Configuration options with defaults: - `GTNH_PACK_VERSION`=latest - `GTNH_DELETE_BACKUPS`=false - `SKIP_GTNH_UPDATE_CHECK`=false ## Set Modpack version As 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. > 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. ## Resource requirements **Recommended Minimum:** - 2-4 cpu cores - 6GB of RAM +0.5GB per extra player (early game) - 6GB of RAM +1GB per extra player (~UV tier+) - 20GB+ storage. HDD is feasible, SSD is preferred For more details regarding the server setup consult the [modpack wiki](https://wiki.gtnewhorizons.com/wiki/Server_Setup). ## Java Version GTNH 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. For optimal performance choose java25 with GTNH 2.8.0 and later. ## Config backups During 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. ## server.properties defaults To 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. - `LEVEL_TYPE=rwg` - `DIFFICULTY=hard` - `ALLOW_FLIGHT=true` - `ENABLE_COMMAND_BLOCK=true` - `MOTD=Greg Tech New Horizon ` ## Java args With java 17+ the server starts with `-Dfml.readTimeout=180 @java9args.txt -jar lwjgl3ify-forgePatches.jar`. With 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` ================================================ FILE: docs/types-and-platforms/mod-platforms/modrinth-modpacks.md ================================================ # Modrinth Modpacks [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. ## Modpack project The desired modpack project is specified with the `MODRINTH_MODPACK` environment variable and must be one of: - The project "slug", which is located in the URL shown here: ![](../../img/modrinth-project-slug.drawio.png) - The project ID, which is located in the bottom of the left panel, shown here ![](../../img/modrinth-project-id.drawio.png) - The project page URL, such as . As described below, this can further be the page URL of a modpack's version page. - A custom URL of a hosted [mrpack file](https://support.modrinth.com/en/articles/8802351-modrinth-modpack-format-mrpack) - The container path to a local [mrpack file](https://support.modrinth.com/en/articles/8802351-modrinth-modpack-format-mrpack) ## Modpack version The automatic modpack version resolving can be narrowed in a few ways: The 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". The resolved modpack version can be narrowed by setting `VERSION` to a specific Minecraft version, such as "1.19.2". The selected version can also be narrowed to a particular mod loader by setting `MODRINTH_LOADER` to either "forge", "fabric", or "quilt". Instead of auto resolving, a specific version of modpack file can be specified by passing the version's page URL to `MODRINTH_MODPACK`, such as or by setting `MODRINTH_VERSION` to the version ID or number located in the Metadata section, as shown here ![](../../img/modrinth-version-id.drawio.png) ## Ignore missing files Some 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`. !!! example In a Compose file ```yaml environment: MODRINTH_IGNORE_MISSING_FILES: | config/mcinstanceloader/pack.mcinstance mods/*.jar ``` ## Excluding files To 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. !!! example In a Compose file: ```yaml MODRINTH_EXCLUDE_FILES: | notenoughanimations lambdynamiclights euphoriapatcher ``` ## Force-include files To 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. !!! example In a Compose file: ```yaml MODRINTH_FORCE_INCLUDE_FILES: | yet-another-config-lib ``` ## Default exclude/includes The 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. ## Excluding Overrides Files Modrinth 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. ### Ant-style paths Ant-style paths can include the following globbing/wildcard symbols: | Symbol | Behavior | |--------|---------------------------------------------------------| | `*` | Matches zero, one, or many characters except a slash | | `**` | Matches zero, one, or many characters including slashes | | `?` | Matches one character | !!! example The following compose `environment` entries show how to exclude Iris and Sodium mods from the overrides ```yaml MODRINTH_OVERRIDES_EXCLUSIONS: mods/NekosEnchantedBooks-*.jar,mods/citresewn-*.jar ``` or using newline delimiter, which improves maintainability ```yaml MODRINTH_OVERRIDES_EXCLUSIONS: | mods/NekosEnchantedBooks-*.jar mods/citresewn-*.jar ``` ================================================ FILE: docs/types-and-platforms/server-types/bukkit-spigot.md ================================================ # Bukkit/Spigot !!! failure "GetBukkit site no longer supports automated downloads" The downloads provider 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. Run a Bukkit/Spigot server type by setting the environment variable `TYPE` to "BUKKIT" or "SPIGOT". !!! example Command-line ``` docker run ... -e TYPE=SPIGOT ... ``` Compose ```yaml environment: ... TYPE: SPIGOT ``` If 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". If you are hosting your own copy of Bukkit/Spigot you can override the download URLs with: - -e BUKKIT_DOWNLOAD_URL= - -e SPIGOT_DOWNLOAD_URL= !!! note 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. ## Build from source You can build spigot from source by setting the environment variable `BUILD_FROM_SOURCE` to "true". ## Alternatives ### Canyon [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. -e VERSION=b1.7.3 -e TYPE=CANYON !!! important 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`. Canyon 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 -e CANYON_BUILD=6 -e CANYON_BUILD=26 ### Poseidon [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. -e VERSION=b1.7.3 -e TYPE=CANYON !!! important 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`. ### Uberbukkit [Uberbukkit](https://github.com/Moresteck/uberbukkit) is a fork of CraftBukkit for Minecraft Beta with Multi version support, supports b1.0 - b1.7.3 ================================================ FILE: docs/types-and-platforms/server-types/fabric.md ================================================ A [Fabric server](https://fabricmc.net/) can be automatically downloaded, upgraded, and run by setting the environment variable TYPE to "FABRIC" !!! example Using `docker run` command line ```shell docker run -d --pull=always -e EULA=TRUE -e TYPE=FABRIC -p 25565:25565 itzg/minecraft-server ``` In a compose file service: ```yaml environment: EULA: TRUE TYPE: FABRIC ``` By 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). A specific loader or launcher version other than the latest can be requested using `FABRIC_LOADER_VERSION` and `FABRIC_LAUNCHER_VERSION` respectively, such as: !!! example "Using launcher and loader versions" With docker run ```shell docker run -d ... \ -e TYPE=FABRIC \ -e FABRIC_LAUNCHER_VERSION=0.10.2 \ -e FABRIC_LOADER_VERSION=0.13.1 ``` In a compose file service: ```yaml environment: EULA: TRUE TYPE: FABRIC FABRIC_LAUNCHER_VERSION: 0.10.2 FABRIC_LOADER_VERSION: 0.13.1 ``` The 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. !!! note See the [Working with mods and plugins](../../mods-and-plugins/index.md) section to set up Fabric mods and configuration. ## Fabric API As [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: ```yaml TYPE: FABRIC MODRINTH_PROJECTS: | fabric-api ``` ## Alternate launcher If you wish to use an alternative launcher you can: - 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`) - Provide the URL to a custom launcher jar with `FABRIC_LAUNCHER_URL` (such as `-e FABRIC_LAUNCHER_URL=http://HOST/fabric-server-custom.jar`) ## Force re-install If the Fabric launcher jar becomes corrupted you can temporarily set FABRIC_FORCE_REINSTALL to "true" to have it re-installed on next startup. ================================================ FILE: docs/types-and-platforms/server-types/forge.md ================================================ A [Forge server](http://www.minecraftforge.net/) can be automatically downloaded, upgraded, and run by setting the environment variable `TYPE` to "FORGE". !!! note "A note from the installer" > Please do not automate the download and installation of Forge. Our efforts are supported by ads from the download page. If you MUST automate this, please consider supporting the project through Since my project also relies on donations, please pass it along and consider contributing to the Patreon above. !!! example ```shell docker run -e TYPE=FORGE ... ``` or in a compose file ```yaml environment: TYPE: FORGE ``` The 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". !!! example ```shell docker run -e TYPE=FORGE -e VERSION=1.12.2 -e FORGE_VERSION=14.23.5.2854 ... ``` or in a compose file ```yaml environment: TYPE: FORGE VERSION: "1.12.2" FORGE_VERSION: "14.23.5.2854" ``` To 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`. In both of the cases above, there is no need for the `VERSION` or `FORGE_VERSION` variables. !!! note 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. URLs configurable via environment variables: - `FORGE_PROMOTIONS_URL`: default is https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json - `FORGE_MAVEN_REPO_URL`: default is https://maven.minecraftforge.net ## Alternatives ### NeoForge Support 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. !!! example ```shell docker run -e TYPE=NEOFORGE -e VERSION=1.20.1 -e NEOFORGE_VERSION=47.1.79 ... ``` or in a compose file ```yaml environment: TYPE: NEOFORGE VERSION: "1.20.4" NEOFORGE_VERSION: "beta" ``` ### Cleanroom [Cleanroom](https://github.com/CleanroomMC/Cleanroom) isn't fully automated, but can be utilized by... 1. choose the desired release at https://github.com/CleanroomMC/Cleanroom/releases 2. grab the link to the `*-installer.jar` file in that release 3. with `TYPE` set to "FORGE", set `FORGE_INSTALLER_URL` to the installer jar's link !!! example In docker compose `environment` ```yaml TYPE: FORGE FORGE_INSTALLER_URL: https://github.com/CleanroomMC/Cleanroom/releases/download/0.2.4-alpha/cleanroom-0.2.4-alpha-installer.jar ``` [Full example](https://github.com/itzg/docker-minecraft-server/tree/master/examples/cleanroom) ================================================ FILE: docs/types-and-platforms/server-types/hybrids.md ================================================ ### Magma A [Magma](https://magmafoundation.org/) server, which is a combination of Forge and PaperMC, can be used with -e TYPE=MAGMA !!! note 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+. There are limited base versions supported, so you will also need to set `VERSION`, such as "1.12.2", "1.16.5", etc. ### Magma Maintained A [Magma Maintained](https://github.com/magmamaintained/) server, which is a alternative project of Magma, can be used with -e TYPE=MAGMA_MAINTAINED !!! note 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". 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. ### Ketting A [Ketting](https://github.com/kettingpowered/Ketting-1-20-x) server, which is an alternative project of Magma 1.20.1+, can be used with -e TYPE=KETTING There are limited base versions supported, so you will also need to set `VERSION`, such as "1.20.1" or later. `FORGE_VERSION` and `KETTING_VERSION` may be specified; however, they will be defaulted by the [Ketting launcher](https://github.com/kettingpowered/kettinglauncher) otherwise. Available 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). The 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`). ### Mohist A [Mohist](https://github.com/MohistMC/Mohist) server can be used with -e TYPE=MOHIST !!! note There are limited base versions supported, so you will also need to set `VERSION`, such as "1.12.2" By default the latest build will be used; however, a specific build number can be selected by setting `MOHIST_BUILD`, such as -e VERSION=1.16.5 -e MOHIST_BUILD=374 ### Youer A [Youer](https://github.com/MohistMC/Youer) server can be used with -e TYPE=YOUER !!! note There are limited base versions supported, so you will also need to set `VERSION`, such as "1.12.2" By default the latest build will be used; however, a specific build number can be selected by setting `MOHIST_BUILD`, such as -e VERSION=1.16.5 -e MOHIST_BUILD=374 ### Banner A [Banner](https://github.com/MohistMC/Banner) server can be used with -e TYPE=BANNER !!! note There are limited base versions supported, so you will also need to set `VERSION`, such as "1.12.2" By default the latest build will be used; however, a specific build number can be selected by setting `MOHIST_BUILD`, such as -e VERSION=1.16.5 -e MOHIST_BUILD=374 ### Arclight A [Arclight](https://arclight.izzel.io/) type server can be used with -e TYPE=ARCLIGHT -e ARCLIGHT_TYPE=NEOFORGE,FORGE,FABRIC ================================================ FILE: docs/types-and-platforms/server-types/others.md ================================================ ## SpongeVanilla Enable SpongeVanilla server mode by adding a `-e TYPE=SPONGEVANILLA` to your command-line. By default the container will run the latest `STABLE` version. If you want to run a specific version, you can add `-e SPONGEVERSION=1.11.2-6.1.0-BETA-19` to your command-line. Beware that current [Sponge](https://www.spongepowered.org) `STABLE` versions for Minecraft 1.12 require using [the Java 8 tag](../../versions/java.md): ``` shell docker run -d --pull=always -v /path/on/host:/data -e TYPE=SPONGEVANILLA \ -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server:java8-multiarch ``` You can also choose to use the `EXPERIMENTAL` branch. Just change it with `SPONGEBRANCH`, such as: ``` shell $ docker run -d --pull=always -v /path/on/host:/data ... \ -e TYPE=SPONGEVANILLA -e SPONGEBRANCH=EXPERIMENTAL ... ``` ## Limbo A [Limbo](https://github.com/LOOHP/Limbo) server can be run by setting `TYPE` to `LIMBO`. Configuration options with defaults: - `LIMBO_BUILD`=LATEST 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. - `FORCE_REDOWNLOAD`=false - `LIMBO_SCHEMA_FILENAME`=default.schem - `LEVEL`="Default;${LIMBO_SCHEMA_NAME}" !!! note 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. ## NanoLimbo A [NanoLimbo](https://github.com/BoomEaro/NanoLimbo) server can be run by setting `TYPE` to `NANOLIMBO`. Note: it is a fork of the original [NanoLimbo](https://github.com/Nan1t/NanoLimbo) made by Nan1t An alternate Limbo server ## Crucible A [Crucible](https://github.com/CrucibleMC/Crucible) server can be run by setting `TYPE` to `CRUCIBLE`. Configuration options with defaults: - `CRUCIBLE_RELEASE`=latest Crucible is only available for 1.7.10, so be sure to set `VERSION=1.7.10`. ## Custom To use a custom server jar or class files, set `TYPE` to "CUSTOM" and continue with one of the following options: The 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. Alternatively, the final `-jar` invocation can be replaced by setting `CUSTOM_JAR_EXEC` to "`-cp `" or "`-jar `" form, such as ``` -cp worldedit.jar:Carpet-Server.jar net.minecraft.server.MinecraftServer ``` !!! note When using `docker run` make sure to quote the entire value since it has spaces in it, such as -e CUSTOM_JAR_EXEC="-cp worldedit.jar:Carpet-Server.jar net.minecraft.server.MinecraftServer" ================================================ FILE: docs/types-and-platforms/server-types/paper.md ================================================ A [PaperMC server](https://papermc.io/) can be automatically downloaded, upgraded, and run by setting the environment variable TYPE to "PAPER". By 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`. To allow for the selection of experimental builds, set `PAPER_CHANNEL` to "experimental", otherwise only release/default channel builds are selected. !!! example Using `docker run` command line ```shell docker run ... -e TYPE=PAPER ... docker run ... -e TYPE=PAPER -e VERSION=1.20.6 ... docker run ... -e TYPE=PAPER -e VERSION=1.20.6 -e PAPER_BUILD=140 ... docker run ... -e TYPE=PAPER -e PAPER_CHANNEL=experimental ... ``` Using a compose file: ```yaml environment: TYPE: PAPER ``` ```yaml environment: TYPE: PAPER VERSION: 1.20.6 PAPER_BUILD: 140 ``` ```yaml environment: TYPE: PAPER PAPER_CHANNEL: experimental ``` !!! tip If you see the following error, it likely means you need to set the env var `PAPER_CHANNEL` to "experimental" ``` No build found for version 1.21 with channel 'default' ``` If you are hosting your own copy of Paper you can override the download URL with `PAPER_DOWNLOAD_URL=`. If 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. [You can also auto-download plugins using `SPIGET_RESOURCES`.](../../mods-and-plugins/spiget.md) ## Alternatives ### Pufferfish A [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." -e TYPE=PUFFERFISH !!! note The `VERSION` variable is used to select branch latest, 1.18, or 1.17. Use PUFFERFISH_BUILD to really select the SERVER VERSION number. Extra variables: - `PUFFERFISH_BUILD=lastSuccessfulBuild` : set a specific Pufferfish build to use. Example: selecting build 47 => 1.18.1, or build 50 => 1.18.2 etc - `FORCE_REDOWNLOAD=false` : set to true to force the located server jar to be re-downloaded - `USE_FLARE_FLAGS=false` : set to true to add appropriate flags for the built-in [Flare](https://blog.airplane.gg/flare) profiler ### Purpur A [Purpur](https://purpurmc.org/) server, which is "a drop-in replacement for Paper servers designed for configurability and new, fun, exciting gameplay features." -e TYPE=PURPUR !!! note The `VERSION` variable is used to lookup a build of Purpur to download Extra variables: - `PURPUR_BUILD=LATEST` : set a specific Purpur build to use - `FORCE_REDOWNLOAD=false` : set to true to force the located server jar to be re-downloaded - `USE_FLARE_FLAGS=false` : set to true to add appropriate flags for the built-in [Flare](https://blog.airplane.gg/flare) profiler - `PURPUR_DOWNLOAD_URL=` : set URL to download Purpur from custom URL. ### Leaf A [Leaf server](https://www.leafmc.one/) is a Paper fork focused on performance improvements and low-level optimizations for smoother gameplay. To use a Leaf server, set the environment variable `TYPE` to `"LEAF"`. -e TYPE=LEAF !!! note The `VERSION` variable is used to select the Minecraft version to run. To specify a particular Leaf build, use `LEAF_BUILD`. By default the latest build will be used; however, a specific build number can be selected by setting `LEAF_BUILD`, such as -e VERSION=1.21.4 -e LEAF_BUILD=441 ### Folia A [Folia server](https://papermc.io/software/folia) can be used by setting the environment variable `TYPE` to "FOLIA". By 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. !!! example Using `docker run` ```shell docker run -d --pull=always \ -v /path/on/host:/data -e TYPE=FOLIA \ -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server ``` If you are hosting your own copy of Folia you can override the download URL with `FOLIA_DOWNLOAD_URL=`. If 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. [You can also auto-download plugins using `SPIGET_RESOURCES`.](../../mods-and-plugins/spiget.md) !!! note The Folia type inherits from the Paper type. Paper's variables will override the Folia ones. ## Extra config - `SKIP_DOWNLOAD_DEFAULTS`: when set to "true" startup will entirely skip checking for default Paper/Bukkit/Spigot config files to download ## Configuration Repositories You 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., `/1.21.1/`). | Server Type | Variable | | :--- | :--- | | Paper | `PAPER_CONFIG_REPO` | | Pufferfish | `PUFFERFISH_CONFIG_REPO` | | Purpur | `PURPUR_CONFIG_REPO` | ### Server Properties You can also download a base `server.properties` file using `SERVER_PROPERTIES_REPO_URL`. - **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). - **Direct Mode**: If the URL ends in `.properties`, it downloads that specific file. **Note**: Environment variables (like `MOTD`, `DIFFICULTY`, `MAX_PLAYERS`) defined in your Docker configuration will **override** values in the downloaded `server.properties` file. ================================================ FILE: docs/types-and-platforms/server-types/quilt.md ================================================ Enable [Quilt server](https://quiltmc.org/) mode by adding a `-e TYPE=QUILT` to your command-line. ```shell docker run -d --pull=always \ -v /path/on/host:/data -e TYPE=QUILT \ -p 25565:25565 -e EULA=TRUE --name mc itzg/minecraft-server ``` By 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). A specific loader or installer version other than the latest can be requested using `QUILT_LOADER_VERSION` and `QUILT_INSTALLER_VERSION` respectively, such as: ```shell docker run -d --pull=always \ -v /path/on/host:/data ... -e TYPE=QUILT \ -e QUILT_LOADER_VERSION=0.16.0 \ -e QUILT_INSTALLER_VERSION=0.4.1 ``` !!! note If you wish to use an alternative launcher you can: - 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`) - Provide the URL to a custom launcher jar with `QUILT_LAUNCHER_URL` (such as `-e QUILT_LAUNCHER_URL=http://HOST/quilt-server-custom.jar`) See the [Working with mods and plugins](../../mods-and-plugins/index.md) section to set up Quilt mods and configuration. ================================================ FILE: docs/variables.md ================================================ !!! warning The variables listed on this page are manually documented and may be out-of-date or inaccurate. All other documentation pages are actively maintained, so please use the search box above to find the desired topic. ### General options
NAME DESCRIPTION DEFAULT REQUIRED
UID The linux user id to run as 1000 ⬜️
GID The linux group id to run as 1000 ⬜️
MEMORY The image declares an initial and maximum Java memory-heap limit of 1 GB. 1G ⬜️
INIT_MEMORY Independently sets the initial heap size 1G ⬜️
MAX_MEMORY independently sets the max heap size 1G ⬜️
TZ You can configure the timezone to match yours by setting the TZ environment variable. alternatively, you can mount: /etc/localtime:/etc/localtime:ro /etc/timezone:/etc/timezone:ro UTC ⬜️
LOG_LEVEL Root logger level (trace, debug, info, warn, error) info ⬜️
LOG_CONSOLE_FORMAT Log4j2 pattern for console output (what you see in docker logs) [%d{HH:mm:ss}] [%t/%level]: %msg%n ⬜️
LOG_FILE_FORMAT Log4j2 pattern for file logs (written to logs/latest.log) [%d{HH:mm:ss}] [%t/%level]: %msg%n ⬜️
LOG_TERMINAL_FORMAT Log4j2 pattern for interactive terminal console (used with docker attach) [%d{HH:mm:ss} %level]: %msg%n ⬜️
ROLLING_LOG_FILE_PATTERN Pattern for rolled/archived log file names logs/%d{yyyy-MM-dd}-%i.log.gz ⬜️
ROLLING_LOG_MAX_FILES Maximum number of archived log files to keep 1000 ⬜️
ENABLE_ROLLING_LOGS Legacy option. Rolling logs are now enabled by default via templated log4j2 configuration. This option is maintained for backward compatibility but only used for error reporting false ⬜️
ENABLE_JMX To enable remote JMX, such as for profiling with VisualVM or JMC, add the environment variable ENABLE_JMX=true false ⬜️
JMX_HOST If JMX is enabled, set JMX_HOST to the IP/host running the Docker container, and add a port forwarding of TCP port 7091 ⬜️
USE_AIKAR_FLAGS Aikar has done some research into finding the optimal JVM flags for GC tuning, which becomes more important as more users are connected concurrently false ⬜️
USE_MEOWICE_FLAGS MeowIce has created an updated set of JVM flags based on Aikar's flags but with support for optimizations for Java 17 and above false ⬜️
USE_MEOWICE_GRAALVM_FLAGS enables MeowIce's flags for GraalVM if USE_MEOWICE_GRAALVM_FLAGS is TRUE true ⬜️
JVM_OPTS General JVM options can be passed to the Minecraft Server invocation by passing a JVM_OPTS environment variable. The JVM requires -XX options to precede -X options, so those can be declared in JVM_XX_OPTS. Both variables are space-delimited, raw JVM arguments ⬜️
JVM_XX_OPTS General JVM options can be passed to the Minecraft Server invocation by passing a JVM_OPTS environment variable. The JVM requires -XX options to precede -X options, so those can be declared in JVM_XX_OPTS. Both variables are space-delimited, raw JVM arguments ⬜️
JVM_DD_OPTS 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.) ⬜️
EXTRA_ARGS Arguments that would usually be passed to the jar file (those which are written after the filename) ⬜️
LOG_TIMESTAMP To include the timestamp with each log set to true false ⬜️
### Server
NAME DESCRIPTION DEFAULT REQUIRED
TYPE The server type VANILLA ⬜️
EULA You MUST set this to true  
VERSION The minecraft version LATEST ⬜️
MOTD Set the server log in message. ⬜️
DIFFICULTY The difficulty level. Available values: peaceful,easy,normal,hard easy ⬜️
ICON 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. ⬜️
OVERRIDE_ICON The server icon which has been set doesn't get overridden by default. Set this to TRUE to override the icon FALSE ⬜️
MODE Minecraft servers are configured to run in Survival mode by default. You can change the mode using MODE where you can either provide the standard numerical values or the shortcut values:
  • creative
  • survival
  • adventure
  • spectator(minecraft 1.8 or later)
⬜️
LEVEL Maps to the level-name server property. You can either switch between world saves or run multiple containers with different saves by using the LEVEL option world ⬜️
SERVER_PORT Maps to the server-port server property. 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. ⬜️
STOP_SERVER_ANNOUNCE_DELAY To 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. ⬜️
PROXY You may configure the use of an HTTP/HTTPS proxy by passing the proxy's URL false ⬜️
CONSOLE Some older versions (pre-1.14) of Spigot required --noconsole to be passed when detaching stdin TRUE ⬜️
GUI Some older servers get confused and think that the GUI interface is enabled. TRUE ⬜️
STOP_DURATION 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. 60 ⬜️
SETUP_ONLY 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 true false ⬜️
USE_FLARE_FLAGS To enable the JVM flags required to fully support the Flare profiling suite. ⬜️
USE_SIMD_FLAGS Support for optimized SIMD operation false ⬜️
### Server properties This image maps known server properties as described in [this section](configuration/server-properties.md). ### Custom resource pack
NAME DESCRIPTION DEFAULT REQUIRED
RESOURCE_PACK A link to a custom resource pack ⬜️
RESOURCE_PACK_SHA1 The checksum for the custom resource pack ⬜️
RESOURCE_PACK_ENFORCE Enforce the resource pack on clients FALSE ⬜️
### Whitelist
NAME DESCRIPTION DEFAULT REQUIRED
ENABLE_WHITELIST Enable the whitelist to manually manage the whitelist false ⬜️
WHITELIST A list of usernames and/or UUIDs separated by comma ⬜️
WHITELIST_FILE A url or file path to a whitelist json formatted file. ⬜️
OVERRIDE_WHITELIST Enforce regeneration of the whitelist on each server startup. false ⬜️
### RCON
NAME DESCRIPTION DEFAULT REQUIRED
ENABLE_RCON Enable/disable RCON support; however, be aware that disabling RCON will remove and limit some features, such as interactive and color console support. true ⬜️
RCON_PASSWORD You MUST change this value Randomly generated
RCON_PORT The port for RCON 25575 ⬜️
BROADCAST_RCON_TO_OPS Sets broadcast-rcon-to-ops server property false ⬜️
RCON_CMDS_STARTUP RCON commands to execute when the server starts. ⬜️
RCON_CMDS_ON_CONNECT RCON commands to execute whenever a client connects to the server. ⬜️
RCON_CMDS_FIRST_CONNECT RCON commands to execute on the first client connection to the server. ⬜️
RCON_CMDS_ON_DISCONNECT RCON commands to execute whenever a client disconnects from the server. ⬜️
RCON_CMDS_LAST_DISCONNECT RCON commands to execute when the last client disconnects from the server. ⬜️
### Auto-Pause
NAME DESCRIPTION DEFAULT REQUIRED
ENABLE_AUTOPAUSE Enable the Autopause functionality FALSE ⬜️
AUTOPAUSE_TIMEOUT_EST describes the time between the last client disconnect and the pausing of the process 3600 ⬜️
AUTOPAUSE_TIMEOUT_INIT describes the time between server start and the pausing of the process, when no client connects in-between 600 ⬜️
AUTOPAUSE_TIMEOUT_KN 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 120 ⬜️
AUTOPAUSE_PERIOD describes period of the daemonized state machine, that handles the pausing of the process 10 ⬜️
AUTOPAUSE_KNOCK_INTERFACE 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. eth0 ⬜️
DEBUG_AUTOPAUSE Adds additional debugging output for AutoPause false ⬜️
### Auto-Stop !!! note AutoStop function is incompatible with the Autopause functionality, as they basically cancel out each other.
NAME DESCRIPTION DEFAULT REQUIRED
ENABLE_AUTOSTOP Enable the AutoStop functionality FALSE ⬜️
AUTOSTOP_TIMEOUT_EST describes the time between the last client disconnect and the stopping of the server 3600 ⬜️
AUTOSTOP_TIMEOUT_INIT describes the time between server start and the stopping of the server, when no client connects in-between 1800 ⬜️
AUTOSTOP_PERIOD describes period of the daemonized state machine, that handles the stopping of the serve 10 ⬜️
DEBUG_AUTOSTOP Adds additional logging for AutoStop false ⬜️
### CurseForge !!! tip Refer to the [main documentation page](types-and-platforms/mod-platforms/auto-curseforge.md) for more details and up-to-date information.
NAME DESCRIPTION DEFAULT REQUIRED
CF_API_KEY YOUR CurseForge (Eternal) API Key.
CF_API_KEY_FILE A path to a file inside of container that contains YOUR CurseForge (Eternal) API Key.
CF_PAGE_URL Pass a page URL to the modpack or a specific file ⬜️
CF_SLUG Instead of a URL, the modpack slug can be provided. ⬜️
CF_FILE_ID The mod curseforge numerical ID. ⬜️
CF_FILENAME_MATCHER Specify a substring to match the desired filename ⬜️
CF_EXCLUDE_INCLUDE_FILE Global and per modpack exclusions can be declared in a JSON file and referenced with this variable.

By default, the file bundled with the image will be used, but can be disabled by setting this to an empty string. The schema of this file is documented here.
⬜️
CF_EXCLUDE_MODS Mods can be excluded by passing a comma or space delimited list of project slugs or IDs ⬜️
CF_FORCE_INCLUDE_MODS Mods can be included by passing a comma or space delimited list of project slugs or IDs ⬜️
CF_FORCE_SYNCHRONIZE Forces the excludes/includes to be re-evaluated ⬜️
CF_SET_LEVEL_FROM 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:
  • WORLD_FILE
  • OVERRIDES
⬜️
CF_PARALLEL_DOWNLOADS specify how many parallel mod downloads to perform 4 ⬜️
CF_OVERRIDES_SKIP_EXISTING if set, files in the overrides that already exist in the data directory are skipped. world data is always skipped, if present. false ⬜️
CF_MOD_LOADER_VERSION Override the mod loader version declared by the modpack ⬜️
================================================ FILE: docs/versions/java.md ================================================ ## Image tags Image references can either omit the tag, which implies the tag `latest`, such as itzg/minecraft-server or explicitly include the tag, such as itzg/minecraft-server: where `` refers to the first column of this table: | Tag | Java version | Linux | JVM Type | Architecture | Note | |----------------|--------------|--------|--------------------|---------------------|------| | latest | 25 | Ubuntu | Hotspot | amd64, arm64 | | | stable | 25 | Ubuntu | Hotspot | amd64, arm64 | | | java25 | 25 | Ubuntu | Hotspot | amd64, arm64 | | | java25-alpine | 25 | Alpine | Hotspot | amd64, arm64 | | | java25-jdk | 25 | Ubuntu | Hotspot+JDK | amd64, arm64 | | | java21 | 21 | Ubuntu | Hotspot | amd64, arm64 | | | java21-jdk | 21 | Ubuntu | Hotspot+JDK | amd64, arm64 | | | java21-alpine | 21 | Alpine | Hotspot | amd64, arm64 | | | java17 | 17 | Ubuntu | Hotspot | amd64, arm64, armv7 | | | java16 | 16 | Ubuntu | Hotspot | amd64, arm64, armv7 | (1) | | java11 | 11 | Ubuntu | Hotspot | amd64, arm64, armv7 | | | java8 | 8 | Ubuntu | Hotspot | amd64, arm64, armv7 | | Notes 1. This version of Java is [recommended for PaperMC 1.16.5](https://docs.papermc.io/paper/getting-started/#requirements) !!! example "Example using java8" With docker run command-line ``` docker run -it -e EULA=true itzg/minecraft-server:java8 ``` or in a compose file ```yaml services: mc: image: itzg/minecraft-server:java8 ``` !!! note "Latest" 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. !!! tip "Class File Version" 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. ### Release versions Since 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. The syntax of released image tags is: itzg/minecraft-server:- where `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). !!! example For example, the 2024.4.0 release of the Java 17 image would be ``` itzg/minecraft-server:2024.4.0-java17 ``` ### Stable image tag The `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}`. ## Version compatibilities [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. ### Class file version 65.0 If 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`. > 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 or > Error: LinkageError occurred while loading main class net.minecraft.bundler.Main java.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 ### Forge versions Forge and its mods sometimes utilize non-public APIs of the JVM and as such are suspceptible to becoming broken with newer Java versions. #### Java 21 Some 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: ``` Caused by: org.spongepowered.asm.mixin.throwables.ClassMetadataNotFoundException: java.util.List at MC-BOOTSTRAP/org.spongepowered.mixin/org.spongepowered.asm.mixin.transformer.MixinPreProcessorStandard.transformMethod(MixinPreProcessorStandard.java:754) ``` #### Java 8 For Forge versions less than 1.18, you _must_ use the `java8-multiarch` (or other java8) image tag. In 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: ``` Caused by: java.lang.ClassCastException: class jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to class java.net.URLClassLoader ``` Forge also doesn't support openj9 JVM implementation. ## Deprecated Image Tags The following image tags have been deprecated and are no longer receiving updates: - java25-graalvm, java21-graalvm, java17-graalvm - adopt13 - adopt14 - adopt15 - openj9-nightly - multiarch-latest - java16-openj9 - java17-graalvm-ce - java17-openj9 - java17-alpine - java19 - java20-graalvm, java20, java20-alpine - java23-* - java24, java24-graalvm - java8-multiarch is still built and pushed, but please move to java8 instead - java8-alpine, java8-jdk, java8-openj9, java8-graalvm-ce ## JSON Listing Information about the image tags is available for programmatic access at ================================================ FILE: docs/versions/minecraft.md ================================================ To use a different Minecraft version, pass the `VERSION` environment variable (case sensitive), which can have the value - `LATEST` for latest release (the default) - `SNAPSHOT` for latest snapshot - a specific version, such as `1.7.9`, `25w35a`, `26.1`, or `26.1-snapshot-1` - or an alpha and beta version, such as "b1.7.3" (server download might not exist) For example, to use the latest snapshot: ``` docker run -d -e VERSION=SNAPSHOT ... ``` or a specific version: ``` docker run -d -e VERSION=1.7.9 ... ``` When using "LATEST" or "SNAPSHOT" an upgrade can be performed by simply restarting the container. During the next startup, if a newer version is available from the respective release channel, then the new server jar file is downloaded and used. !!! note Over time you might see older versions of the server jar remain in the `/data` directory. It is safe to remove those. ================================================ FILE: examples/.gitignore ================================================ data/ modpacks/ .env downloads/ ================================================ FILE: examples/README.md ================================================ # Deployment Examples This directory contains various deployment examples of the [itzg/minecraft-server](https://hub.docker.com/r/itzg/minecraft-server/) Docker image. ================================================ FILE: examples/apply-extra-configs/compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: "TRUE" TYPE: PAPER MAX_MEMORY: 4G MODRINTH_PROJECTS: | luckperms APPLY_EXTRA_FILES: | plugins/LuckPerms https://luckperms.net/wiki/Configuration | # # | | # # | New options are not added to this file automatically. Default values are used if an | # # | option cannot be found. The latest config versions can be obtained at the link above. | # # +----------------------------------------------------------------------------------------------+ # #################################################################################################### # +----------------------------------------------------------------------------------------------+ # # | | # # | ESSENTIAL SETTINGS | # # | | # # | Important settings that control how LuckPerms functions. | # # | | # # +----------------------------------------------------------------------------------------------+ # # The name of the server, used for server specific permissions. # # - When set to "global" this setting is effectively ignored. # - In all other cases, the value here is added to all players in a "server" context. # - See: https://luckperms.net/wiki/Context server: global # If the servers own UUID cache/lookup facility should be used when there is no record for a player # already in LuckPerms. # # - When this is set to 'false', commands using a player's username will not work unless the player # has joined since LuckPerms was first installed. # - To get around this, you can use a player's uuid directly in the command, or enable this option. # - When this is set to 'true', the server facility is used. This may use a number of methods, # including checking the servers local cache, or making a request to the Mojang API. use-server-uuid-cache: false # +----------------------------------------------------------------------------------------------+ # # | | # # | STORAGE SETTINGS | # # | | # # | Controls which storage method LuckPerms will use to store data. | # # | | # # +----------------------------------------------------------------------------------------------+ # # How the plugin should store data # # - The various options are explained in more detail on the wiki: # https://luckperms.net/wiki/Storage-types # # - Possible options: # # | Remote databases - require connection information to be configured below # |=> MySQL # |=> MariaDB (preferred over MySQL) # |=> PostgreSQL # |=> MongoDB # # | Flatfile/local database - don't require any extra configuration # |=> H2 (preferred over SQLite) # |=> SQLite # # | Readable & editable text files - don't require any extra configuration # |=> YAML (.yml files) # |=> JSON (.json files) # |=> HOCON (.conf files) # |=> TOML (.toml files) # | # | By default, user, group and track data is separated into different files. Data can be combined # | and all stored in the same file by switching to a combined storage variant. # | Just add '-combined' to the end of the storage-method, e.g. 'yaml-combined' # # - A H2 database is the default option. # - If you want to edit data manually in "traditional" storage files, we suggest using YAML. storage-method: h2 # The following block defines the settings for remote database storage methods. # # - You don't need to touch any of the settings here if you're using a local storage method! # - The connection detail options are shared between all remote storage types. data: # Define the address and port for the database. # - The standard DB engine port is used by default # (MySQL: 3306, PostgreSQL: 5432, MongoDB: 27017) # - Specify as "host:port" if differs address: localhost # The name of the database to store LuckPerms data in. # - This must be created already. Don't worry about this setting if you're using MongoDB. database: minecraft # Credentials for the database. username: root password: '' # These settings apply to the MySQL connection pool. # - The default values will be suitable for the majority of users. # - Do not change these settings unless you know what you're doing! pool-settings: # Sets the maximum size of the MySQL connection pool. # - Basically this value will determine the maximum number of actual # connections to the database backend. # - More information about determining the size of connection pools can be found here: # https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing maximum-pool-size: 10 # Sets the minimum number of idle connections that the pool will try to maintain. # - For maximum performance and responsiveness to spike demands, it is recommended to not set # this value and instead allow the pool to act as a fixed size connection pool. # (set this value to the same as 'maximum-pool-size') minimum-idle: 10 # This setting controls the maximum lifetime of a connection in the pool in milliseconds. # - The value should be at least 30 seconds less than any database or infrastructure imposed # connection time limit. maximum-lifetime: 1800000 # 30 minutes # This setting controls how frequently the pool will 'ping' a connection in order to prevent it # from being timed out by the database or network infrastructure, measured in milliseconds. # - The value should be less than maximum-lifetime and greater than 30000 (30 seconds). # - Setting the value to zero will disable the keepalive functionality. keepalive-time: 0 # This setting controls the maximum number of milliseconds that the plugin will wait for a # connection from the pool, before timing out. connection-timeout: 5000 # 5 seconds # This setting allows you to define extra properties for connections. # # By default, the following options are set to enable utf8 encoding. (you may need to remove # these if you are using PostgreSQL) # useUnicode: true # characterEncoding: utf8 # # You can also use this section to disable SSL connections, by uncommenting the 'useSSL' and # 'verifyServerCertificate' options below. properties: useUnicode: true characterEncoding: utf8 #useSSL: false #verifyServerCertificate: false # The prefix for all LuckPerms SQL tables. # # - This only applies for remote SQL storage types (MySQL, MariaDB, etc). # - Change this if you want to use different tables for different servers. table-prefix: 'luckperms_' # The prefix to use for all LuckPerms MongoDB collections. # # - This only applies for the MongoDB storage type. # - Change this if you want to use different collections for different servers. The default is no # prefix. mongodb-collection-prefix: '' # The connection string URI to use to connect to the MongoDB instance. # # - When configured, this setting will override anything defined in the address, database, # username or password fields above. # - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it # below. # - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/ mongodb-connection-uri: '' # Define settings for a "split" storage setup. # # - This allows you to define a storage method for each type of data. # - The connection options above still have to be correct for each type here. split-storage: # Don't touch this if you don't want to use split storage! enabled: false methods: # These options don't need to be modified if split storage isn't enabled. user: h2 group: h2 track: h2 uuid: h2 log: h2 # +----------------------------------------------------------------------------------------------+ # # | | # # | UPDATE PROPAGATION & MESSAGING SERVICE | # # | | # # | Controls the ways in which LuckPerms will sync data & notify other servers of changes. | # # | These options are documented on greater detail on the wiki under "Instant Updates". | # # | | # # +----------------------------------------------------------------------------------------------+ # # This option controls how frequently LuckPerms will perform a sync task. # # - A sync task will refresh all data from the storage, and ensure that the most up-to-date data is # being used by the plugin. # - This is disabled by default, as most users will not need it. However, if you're using a remote # storage type without a messaging service setup, you may wish to set this to something like 3. # - Set to -1 to disable the task completely. sync-minutes: -1 # If the file watcher should be enabled. # # - When using a file-based storage type, LuckPerms can monitor the data files for changes, and # automatically update when changes are detected. # - If you don't want this feature to be active, set this option to false. watch-files: true # Define which messaging service should be used by the plugin. # # - If enabled and configured, LuckPerms will use the messaging service to inform other connected # servers of changes. # - Use the command "/lp networksync" to manually push changes. # - Data is NOT stored using this service. It is only used as a messaging platform. # # - If you decide to enable this feature, you should set "sync-minutes" to -1, as there is no need # for LuckPerms to poll the database for changes. # # - Possible options: # => sql Uses the SQL database to form a queue system for communication. Will only work when # 'storage-method' is set to MySQL or MariaDB. This is chosen by default if the # option is set to 'auto' and SQL storage is in use. Set to 'notsql' to disable this. # => pluginmsg Uses the plugin messaging channels to communicate with the proxy. # LuckPerms must be installed on your proxy & all connected servers backend servers. # Won't work if you have more than one proxy. # => lilypad Uses LilyPad pub-sub to push changes. You need to have the LilyPad-Connect plugin # installed. # => redis Uses Redis pub-sub to push changes. Your server connection info must be configured # below. # => rabbitmq Uses RabbitMQ pub-sub to push changes. Your server connection info must be # configured below. # => nats Uses Nats pub-sub to push changes. Your server connection info must be # configured below. # => custom Uses a messaging service provided using the LuckPerms API. # => auto Attempts to automatically setup a messaging service using redis or sql. messaging-service: auto # If LuckPerms should automatically push updates after a change has been made with a command. auto-push-updates: true # If LuckPerms should push logging entries to connected servers via the messaging service. push-log-entries: true # If LuckPerms should broadcast received logging entries to players on this platform. # # - If you have LuckPerms installed on your backend servers as well as a BungeeCord proxy, you # should set this option to false on either your backends or your proxies, to avoid players being # messaged twice about log entries. broadcast-received-log-entries: true # Settings for Redis. # Port 6379 is used by default; set address to "host:port" if differs # Multiple Redis nodes can be specified in the same format as a string list under the name "addresses". redis: enabled: false address: localhost username: '' password: '' # Settings for Nats. # Port 4222 is used by default; set address to "host:port" if differs nats: enabled: false address: localhost username: '' password: '' # Settings for RabbitMQ. # Port 5672 is used by default; set address to "host:port" if differs rabbitmq: enabled: false address: localhost vhost: '/' username: 'guest' password: 'guest' # +----------------------------------------------------------------------------------------------+ # # | | # # | CUSTOMIZATION SETTINGS | # # | | # # | Settings that allow admins to customize the way LuckPerms operates. | # # | | # # +----------------------------------------------------------------------------------------------+ # # Controls how temporary permissions/parents/meta should be accumulated. # # - The default behaviour is "deny". # - This behaviour can also be specified when the command is executed. See the command usage # documentation for more info. # # - Possible options: # => accumulate durations will be added to the existing expiry time # => replace durations will be replaced if the new duration is later than the current # expiration # => deny the command will just fail if you try to add another node with the same expiry temporary-add-behaviour: deny # Controls how LuckPerms will determine a users "primary" group. # # - The meaning and influence of "primary groups" are explained in detail on the wiki. # - The preferred approach is to let LuckPerms automatically determine a users primary group # based on the relative weight of their parent groups. # # - Possible options: # => stored use the value stored against the users record in the file/database # => parents-by-weight just use the users most highly weighted parent # => all-parents-by-weight same as above, but calculates based upon all parents inherited from # both directly and indirectly primary-group-calculation: parents-by-weight # If the plugin should check for "extra" permissions with users run LP commands. # # - These extra permissions allow finer control over what users can do with each command, and who # they have access to edit. # - The nature of the checks are documented on the wiki under "Argument based command permissions". # - Argument based permissions are *not* static, unlike the 'base' permissions, and will depend upon # the arguments given within the command. argument-based-command-permissions: false # If the plugin should check whether senders are a member of a given group before they're able to # edit the groups data or add/remove other users to/from it. # Note: these limitations do not apply to the web editor! require-sender-group-membership-to-modify: false # If the plugin should send log notifications to users whenever permissions are modified. # # - Notifications are only sent to those with the appropriate permission to receive them # - They can also be temporarily enabled/disabled on a per-user basis using # '/lp log notify ' log-notify: true # Defines a list of log entries which should not be sent as notifications to users. # # - Each entry in the list is a RegEx expression which is matched against the log entry description. log-notify-filtered-descriptions: # - "parent add example" # If LuckPerms should automatically install translation bundles and periodically update them. auto-install-translations: true # Defines the options for prefix and suffix stacking. # # - The feature allows you to display multiple prefixes or suffixes alongside a players username in # chat. # - It is explained and documented in more detail on the wiki under "Prefix & Suffix Stacking". # # - The options are divided into separate sections for prefixes and suffixes. # - The 'duplicates' setting refers to how duplicate elements are handled. Can be 'retain-all', # 'first-only' or 'last-only'. # - The value of 'start-spacer' is included at the start of the resultant prefix/suffix. # - The value of 'end-spacer' is included at the end of the resultant prefix/suffix. # - The value of 'middle-spacer' is included between each element in the resultant prefix/suffix. # # - Possible format options: # => highest Selects the value with the highest weight, from all values # held by or inherited by the player. # # => lowest Same as above, except takes the one with the lowest weight. # # => highest_own Selects the value with the highest weight, but will not # accept any inherited values. # # => lowest_own Same as above, except takes the value with the lowest weight. # # => highest_inherited Selects the value with the highest weight, but will only # accept inherited values. # # => lowest_inherited Same as above, except takes the value with the lowest weight. # # => highest_on_track_ Selects the value with the highest weight, but only if the # value was inherited from a group on the given track. # # => lowest_on_track_ Same as above, except takes the value with the lowest weight. # # => highest_not_on_track_ Selects the value with the highest weight, but only if the # value was inherited from a group not on the given track. # # => lowest_not_on_track_ Same as above, except takes the value with the lowest weight. # # => highest_from_group_ Selects the value with the highest weight, but only if the # value was inherited from the given group. # # => lowest_from_group_ Same as above, except takes the value with the lowest weight. # # => highest_not_from_group_ Selects the value with the highest weight, but only if the # value was not inherited from the given group. # # => lowest_not_from_group_ Same as above, except takes the value with the lowest weight. meta-formatting: prefix: format: - "highest" duplicates: first-only start-spacer: "" middle-spacer: " " end-spacer: "" suffix: format: - "highest" duplicates: first-only start-spacer: "" middle-spacer: " " end-spacer: "" # +----------------------------------------------------------------------------------------------+ # # | | # # | PERMISSION CALCULATION AND INHERITANCE | # # | | # # | Modify the way permission checks, meta lookups and inheritance resolutions are handled. | # # | | # # +----------------------------------------------------------------------------------------------+ # # The algorithm LuckPerms should use when traversing the "inheritance tree". # # - Possible options: # => breadth-first See: https://en.wikipedia.org/wiki/Breadth-first_search # => depth-first-pre-order See: https://en.wikipedia.org/wiki/Depth-first_search # => depth-first-post-order See: https://en.wikipedia.org/wiki/Depth-first_search inheritance-traversal-algorithm: depth-first-pre-order # If a final sort according to "inheritance rules" should be performed after the traversal algorithm # has resolved the inheritance tree. # # "Inheritance rules" refers to things such as group weightings, primary group status, and the # natural contextual ordering of the group nodes. # # Setting this to 'true' will allow for the inheritance rules to take priority over the structure of # the inheritance tree. # # Effectively when this setting is 'true': the tree is flattened, and rules applied afterwards, # and when this setting is 'false':, the rules are just applied during each step of the traversal. post-traversal-inheritance-sort: false # Defines the mode used to determine whether a set of contexts are satisfied. # # - Possible options: # => at-least-one-value-per-key Set A will be satisfied by another set B, if at least one of the # key-value entries per key in A are also in B. # => all-values-per-key Set A will be satisfied by another set B, if all key-value # entries in A are also in B. context-satisfy-mode: at-least-one-value-per-key # LuckPerms has a number of built-in contexts. These can be disabled by adding the context key to # the list below. disabled-contexts: # - "world" # +----------------------------------------------------------------------------------------------+ # # | Permission resolution settings | # # +----------------------------------------------------------------------------------------------+ # # If users on this server should have their global permissions applied. # When set to false, only server specific permissions will apply for users on this server include-global: true # If users on this server should have their global world permissions applied. # When set to false, only world specific permissions will apply for users on this server include-global-world: true # If users on this server should have global (non-server specific) groups applied apply-global-groups: true # If users on this server should have global (non-world specific) groups applied apply-global-world-groups: true # +----------------------------------------------------------------------------------------------+ # # | Meta lookup settings | # # +----------------------------------------------------------------------------------------------+ # # Defines how meta values should be selected. # # - Possible options: # => inheritance Selects the meta value that was inherited first # => highest-number Selects the highest numerical meta value # => lowest-number Selects the lowest numerical meta value meta-value-selection-default: inheritance # Defines how meta values should be selected per key. meta-value-selection: # max-homes: highest-number # +----------------------------------------------------------------------------------------------+ # # | Inheritance settings | # # +----------------------------------------------------------------------------------------------+ # # If the plugin should apply wildcard permissions. # # - If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered # permissions matching the wildcard. apply-wildcards: true # If LuckPerms should resolve and apply permissions according to the Sponge style implicit wildcard # inheritance system. # # - That being: If a user has been granted "example", then the player should have also be # automatically granted "example.function", "example.another", "example.deeper.nesting", # and so on. apply-sponge-implicit-wildcards: false # If the plugin should apply negated Bukkit default permissions before it considers wildcard # assignments. # # - Plugin authors can define permissions which explicitly should not be given automatically to OPs. # This is usually used for so called "anti-permissions" - permissions which, when granted, apply # something negative. # - If this option is set to true, LuckPerms will consider any negated declarations made by # plugins before it considers wildcards. (similar to the way the OP system works) # - If this option is set to false, LuckPerms will consider any wildcard assignments first. apply-default-negated-permissions-before-wildcards: false # If the plugin should parse regex permissions. # # - If set to true, LuckPerms will detect regex permissions, marked with "r=" at the start of the # node, and resolve & apply all registered permissions matching the regex. apply-regex: true # If the plugin should complete and apply shorthand permissions. # # - If set to true, LuckPerms will detect and expand shorthand node patterns. apply-shorthand: true # If the plugin should apply Bukkit child permissions. # # - Plugin authors can define custom permissions structures for their plugin, which will be resolved # and used by LuckPerms if this setting is enabled. apply-bukkit-child-permissions: true # If the plugin should apply Bukkit default permissions. # # - Plugin authors can define permissions which should be given to all users by default, or setup # permissions which should/shouldn't be given to opped players. # - If this option is set to false, LuckPerms will ignore these defaults. apply-bukkit-default-permissions: true # If the plugin should apply attachment permissions. # # - Other plugins on the server are able to add their own "permission attachments" to players. # - This allows them to grant players additional permissions which last until the end of the # session, or until they're removed. # - If this option is set to false, LuckPerms will not include these attachment permissions when # considering if a player should have access to a certain permission. apply-bukkit-attachment-permissions: true # +----------------------------------------------------------------------------------------------+ # # | Extra settings | # # +----------------------------------------------------------------------------------------------+ # # A list of context calculators which will be skipped when calculating contexts. # # - You can disable context calculators by either: # => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator) # => specifying a sub-section of the Java package used by the calculator (e.g. com.example) disabled-context-calculators: [] # Allows you to set "aliases" for the worlds sent forward for context calculation. # # - These aliases are provided in addition to the real world name. Applied recursively. # - Remove the comment characters for the default aliases to apply. world-rewrite: # world_nether: world # world_the_end: world # Define special group weights for this server. # # - Group weights can also be applied directly to group data, using the setweight command. # - This section allows weights to be set on a per-server basis. group-weight: # admin: 10 # +----------------------------------------------------------------------------------------------+ # # | | # # | FINE TUNING OPTIONS | # # | | # # | A number of more niche settings for tweaking and changing behaviour. The section also | # # | contains toggles for some more specialised features. It is only necessary to make changes to | # # | these options if you want to fine-tune LuckPerms behaviour. | # # | | # # +----------------------------------------------------------------------------------------------+ # # +----------------------------------------------------------------------------------------------+ # # | Server Operator (OP) settings | # # +----------------------------------------------------------------------------------------------+ # # Controls whether server operators should exist at all. # # - When set to 'false', all players will be de-opped, and the /op and /deop commands will be # disabled. Note that vanilla features like the spawn-protection require an operator on the # server to work. enable-ops: true # Enables or disables a special permission based system in LuckPerms for controlling OP status. # # - If set to true, any user with the permission "luckperms.autoop" will automatically be granted # server operator status. This permission can be inherited, or set on specific servers/worlds, # temporarily, etc. # - Additionally, setting this to true will force the "enable-ops" option above to false. All users # will be de-opped unless they have the permission node, and the op/deop commands will be # disabled. # - It is recommended that you use this option instead of assigning a single '*' permission. auto-op: false # Defines if "opped" players should be able to use all LuckPerms commands by default. # # - Set to false to only allow users who have the permissions access to the commands commands-allow-op: true # +----------------------------------------------------------------------------------------------+ # # | Vault integration settings | # # +----------------------------------------------------------------------------------------------+ # # If Vault lookups for offline players on the main server thread should be enabled. # # LuckPerms has a "catch" for plugins attempting to perform unsafe offline player data lookups # from the main server thread. This catch raises an exception (causes an error to occur) when unsafe # lookups are made, instead of allowing the lookup to happen, which would likely cause the server # to lag. # # However, if you're willing to accept the consequences, the catch can be disabled by setting this # option to 'true. vault-unsafe-lookups: false # If LuckPerms should use the 'display name' of a group when returning groups in Vault API calls. # # - When this option is set to true, the display name of the group is returned. # - When this option is set to false, the standard name/id of the group is returned. vault-group-use-displaynames: true # Controls which group LuckPerms should use for NPC players when handling Vault requests. # # - As NPCs aren't actually real players, LuckPerms does not load any user data for them. This # becomes an issue when plugins want to check for their permissions using Vault. # - As a solution, Vault checks for NPCs fallback to a group, which is defined below. vault-npc-group: default # Controls how LuckPerms should consider the OP status of NPC players when handing Vault requests. # # - If you want NPCs to have the same permissions as "normal" players, set this option to false. # - If you want NPCs to have OP status, set this option to true. vault-npc-op-status: false # If the vault-server option below should be used. # # - When this option is set to false, the server value defined above under "server" is used. use-vault-server: false # The name of the server used within Vault operations. # # - If you don't want Vault operations to be server specific, set this to "global". # - Will only take effect if use-vault-server is set to true above. vault-server: global # If global permissions should be considered when retrieving meta or player groups vault-include-global: true # If Vault operations should ignore any world arguments if supplied. vault-ignore-world: false # +----------------------------------------------------------------------------------------------+ # # | Miscellaneous (and rarely used) settings | # # +----------------------------------------------------------------------------------------------+ # # If LuckPerms should produce extra logging output when it handles logins. # # - Useful if you're having issues with UUID forwarding or data not being loaded. debug-logins: false # If LuckPerms should allow usernames with non alphanumeric characters. # # - Note that due to the design of the storage implementation, usernames must still be 16 characters # or less. allow-invalid-usernames: false # If LuckPerms should not require users to confirm bulkupdate operations. # # - When set to true, operations will be executed immediately. # - This is not recommended, as bulkupdate has the potential to irreversibly delete large amounts of # data, and is not designed to be executed automatically. # - If automation is needed, users should prefer using the LuckPerms API. skip-bulkupdate-confirmation: false # If LuckPerms should prevent bulkupdate operations. # # - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work. # - When set to false, bulkupdate operations will be allowed via the console. disable-bulkupdate: false # If LuckPerms should allow a users primary group to be removed with the 'parent remove' command. # # - When this happens, the plugin will set their primary group back to default. prevent-primary-group-removal: false # If LuckPerms should update the list of commands sent to the client when permissions are changed. update-client-command-list: true # If LuckPerms should attempt to register "Brigadier" command list data for its commands. register-command-list-data: true # If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands. # See here for more info: https://minecraft.wiki/w/Target_selectors resolve-command-selectors: false # If the plugin should run in "read-only" mode for commands. # # In this mode, players or the console will only be able to execute commands that read or view # data, and will not be able to execute any LP commands that modify data. # # If enabling read-only mode for just players, be aware that some other plugins may allow players # to execute commands as the console, allowing them to run LP commands indirectly. # # Note: This does not affect interactions with LuckPerms via the API. commands-read-only-mode: players: false console: false # If LuckPerms commands should be disabled. When true, this will prevent all LP commands from being # executed. In a sense this is a more extreme version of read-only mode (see above). # # LuckPerms will still act as the permission manager, but server administrators will be unable to # make changes via commands. # # If disabling commands just for players, be aware that some other plugins may allow players to # execute commands as the console, allowing them to run commands indirectly. # # If commands are disabled for both players and the console, LuckPerms will not attempt to register # a command with the server at all & in a sense will be invisible to both players and admins. # # Note: This does not affect interactions with LuckPerms via the API. disable-luckperms-commands: players: false console: false ================================================ FILE: examples/auto-curseforge/aof7/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: true MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/all-of-fabric-7 # CF_FILENAME_MATCHER: 1.2.2 CF_OVERRIDES_EXCLUSIONS: | mods/iris*.jar mods/sodium*.jar MEMORY: 4G ports: - "25565:25565" volumes: - mc-data:/data volumes: mc-data: {} ================================================ FILE: examples/auto-curseforge/atm10/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server tty: true stdin_open: true ports: - "25565:25565" environment: EULA: true MOD_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_SLUG: all-the-mods-10 # Optional: select a specific version/file # CF_FILENAME_MATCHER: "1.17" MEMORY: 4G CF_OVERRIDES_EXCLUSIONS: | # Not applicable for server side shaderpacks/** volumes: # Use managed volume by default, but can change to a relative path like # ./data:/data # to use a host directory - mc-data:/data volumes: mc-data: ================================================ FILE: examples/auto-curseforge/atm8/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server:java17 ports: - "25565:25565" environment: EULA: "true" MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/all-the-mods-8 # CF_FILENAME_MATCHER: "1.1.0" MEMORY: 4G volumes: - mc-data:/data # Since Docker's default volume driver creates volumes owned by root, this # init container will change ownership to match final UID of mc service, above init-filebrowser: image: filebrowser/filebrowser entrypoint: sh -c command: - "chown -R 1000: /database" restart: no volumes: - filebrowser-db:/database filebrowser: image: filebrowser/filebrowser depends_on: init-filebrowser: condition: service_completed_successfully user: "1000:1000" environment: FB_DATABASE: /database/filebrowser.db volumes: # Default FB_ROOT is /srv # In this example, the left-side needs to be the same as /data volume of mc service - mc-data:/srv - filebrowser-db:/database ports: - "25580:80" volumes: mc-data: {} filebrowser-db: {} ================================================ FILE: examples/auto-curseforge/atm8/filebrowser.json ================================================ { "port": 80, "baseURL": "", "address": "", "log": "stdout", "database": "/database/filebrowser.db", "root": "/srv" } ================================================ FILE: examples/auto-curseforge/atm9/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server ports: - "25565:25565" environment: EULA: "true" MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/all-the-mods-9 # Optional: select a specific version/file #CF_FILENAME_MATCHER: "0.2.34" MEMORY: 4G volumes: - mc-data:/data volumes: mc-data: {} ================================================ FILE: examples/auto-curseforge/craftoria/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server tty: true stdin_open: true ports: - "25565:25565" environment: EULA: true ALLOW_FLIGHT: true MOD_PLATFORM: AUTO_CURSEFORGE CF_API_KEY: ${CF_API_KEY} CF_SLUG: craftoria MOTD: | A %TYPE% server on %VERSION% running %MODPACK_NAME% %MODPACK_VERSION% MEMORY: 8G volumes: # Use managed volume by default, but can change to a relative path like # ./data:/data # to use a host directory - mc-data:/data volumes: mc-data: ================================================ FILE: examples/auto-curseforge/fabric-custom-loader/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: "TRUE" MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ CF_API_KEY: ${CF_API_KEY} # Example of overriding modloader version for a Fabric modpack # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_SLUG: "cobblemon-fabric" # Override the mod loader version from the modpack CF_MOD_LOADER_VERSION: "0.16.14" MEMORY: "4G" ports: - "25565:25565" volumes: - ./data:/data stdin_open: true tty: true # See https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#custom-modloader-versions ================================================ FILE: examples/auto-curseforge/ftb-evolution/compose.yaml ================================================ services: mc: image: itzg/minecraft-server tty: true stdin_open: true ports: # Game port - "25565:25565/tcp" environment: EULA: "TRUE" TYPE: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/ftb-evolution # This modpack includes an override client-side only mod that will prevent the server from starting. # For more information on override exclusions see: # https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#excluding-overrides-files CF_OVERRIDES_EXCLUSIONS: mods/rgp_client*.jar MEMORY: 6G volumes: # attach the relative directory 'data' to the container's /data path - ./data:/data ================================================ FILE: examples/auto-curseforge/modpack-manifest/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: true MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_MODPACK_MANIFEST: /manifests/manifest.json CF_SLUG: "custom" MEMORY: 2G volumes: - mc-data:/data - ./manifests:/manifests:ro ports: - "25565:25565" volumes: mc-data: {} ================================================ FILE: examples/auto-curseforge/modpack-manifest/manifests/manifest.json ================================================ { "minecraft": { "version": "1.20.4", "modLoaders": [ { "id": "fabric-0.15.3", "primary": true } ] }, "manifestType": "minecraftModpack", "manifestVersion": 1, "name": "Custom", "author": "", "files": [ { "projectID": 351725, "fileID": 4973035, "required": true }, { "projectID": 306612, "fileID": 5010374, "required": true } ], "overrides": "overrides" } ================================================ FILE: examples/auto-curseforge/one-block-modded/.gitignore ================================================ /.env ================================================ FILE: examples/auto-curseforge/one-block-modded/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server ports: - "25565:25565" environment: EULA: "true" MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_SLUG: one-block-modded # Modpack client zip must be manually downloaded from # https://www.curseforge.com/minecraft/modpacks/one-block-modded # due to authors disallowing project distribution CF_MODPACK_ZIP: /modpacks/Modded+OneBlock.zip CF_SET_LEVEL_FROM: OVERRIDES DEBUG: "false" MEMORY: 4G volumes: - mc-data:/data - ./modpacks:/modpacks:ro volumes: mc-data: {} ================================================ FILE: examples/auto-curseforge/pixelmon/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server:java8-multiarch ports: - "25565:25565" environment: EULA: "true" MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_SLUG: the-pixelmon-modpack CF_FILENAME_MATCHER: "9.1.2" MEMORY: 4G volumes: - mc-data:/data volumes: mc-data: {} ================================================ FILE: examples/auto-curseforge/prominence2/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server ports: - "25565:25565" environment: EULA: "true" MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/prominence-2-rpg CF_FILENAME_MATCHER: "2.8.7" MEMORY: 4G volumes: - mc-data:/data volumes: mc-data: {} ================================================ FILE: examples/auto-curseforge/rad2/compose.yaml ================================================ # This runs the most recent version of Roguelike Adventures and Dungeons 2. Version 1.11 as of writing. services: mc: image: itzg/minecraft-server:java8 ports: - "25565:25565" environment: EULA: "true" MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/roguelike-adventures-and-dungeons-2 # Optional: select a specific version/file #CF_FILENAME_MATCHER: "0.2.34" CF_EXCLUDE_MODS: | # Exclude client-side mods not published correctly creative-core default-options itemphysic-lite konkrete oauth CF_FORCE_SYNCHRONIZE: "true" MEMORY: 4G volumes: - mc-data:/data - C:\Users\geoff\git\mc-image-helper\build\install\mc-image-helper:/usr/share/mc-image-helper:ro volumes: mc-data: {} ================================================ FILE: examples/auto-curseforge/skyfactory4/compose.yml ================================================ services: mc: image: itzg/minecraft-server:java8 environment: EULA: "true" MODPACK_PLATFORM: AUTO_CURSEFORGE CF_SLUG: skyfactory-4 CF_FILENAME_MATCHER: 4.2.4 CF_API_KEY: ${CF_API_KEY} MEMORY: 3G ports: - "25565:25565" volumes: - mc-data:/data volumes: mc-data: ================================================ FILE: examples/auto-curseforge/skyfactory5/compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: true # https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/ MODPACK_PLATFORM: AUTO_CURSEFORGE # CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/skyfactory-5/files/6290684 CF_SLUG: skyfactory-5 # Comment out the following to get the latest version or pick a version from # https://www.curseforge.com/minecraft/modpacks/skyfactory-5/files/all?page=1&pageSize=20 CF_FILENAME_MATCHER: 5.0.8 # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} MEMORY: 4G ports: - "25565:25565" volumes: - mc-data:/data volumes: mc-data: ================================================ FILE: examples/auto-curseforge/using-excludes/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server:java8 ports: - "25565:25565" environment: EULA: "true" MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/minecraft-eternal/files/4102634 MEMORY: 4G volumes: - mc-data:/data volumes: mc-data: {} ================================================ FILE: examples/auto-curseforge/valhesia5/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server ports: - "25565:25565" environment: EULA: "true" MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_FORCE_SYNCHRONIZE: "true" CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/valhelsia-5/files/4429560 MEMORY: 4G volumes: - data:/data volumes: data: {} ================================================ FILE: examples/auto-curseforge/vault-hunters-1-18-2/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server:java17 ports: - "25565:25565" environment: EULA: "true" MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_SLUG: vault-hunters-1-18-2 MOTD: "§4----- §2 Vault Hunters: 1.18.2 §4 -----§r\\n §4------ §e vaulthunters.gg §4------" MEMORY: 6G # 4G for base server + 2G per player ALLOW_FLIGHT: true ENABLE_COMMAND_BLOCK: true DIFFICULTY: hard volumes: - mc-data:/data volumes: mc-data: {} ================================================ FILE: examples/autopause/compose.yml ================================================ services: minecraft: image: itzg/minecraft-server ports: - "25565:25565" volumes: - "mc:/data" environment: EULA: "TRUE" TYPE: PAPER ENABLE_AUTOPAUSE: "TRUE" MAX_TICK_TIME: "-1" # More aggressive settings for demo purposes AUTOPAUSE_TIMEOUT_INIT: "30" AUTOPAUSE_TIMEOUT_EST: "10" JVM_DD_OPTS: "disable.watchdog:true" restart: unless-stopped volumes: mc: {} ================================================ FILE: examples/autostop/compose.yml ================================================ services: minecraft: image: itzg/minecraft-server ports: - "25565:25565" volumes: - "mc:/data" environment: EULA: "TRUE" ENABLE_AUTOSTOP: "TRUE" # More aggressive settings for demo purposes AUTOSTOP_TIMEOUT_INIT: "30" AUTOSTOP_TIMEOUT_EST: "20" # Important not to auto-restart the server!!! restart: "no" volumes: mc: {} ================================================ FILE: examples/bentobox/compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: "TRUE" TYPE: PAPER DEBUG: true PLUGINS: | https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot https://hangarcdn.papermc.io/plugins/BentoboxWorld/bentobox/versions/3.7.4/PAPER/BentoBox-3.7.4.jar DOWNLOAD_EXTRA_CONFIGS: | # Even though it's a jar can be treated just like any other file plugins/BentoBox/addons https://luckperms.net/wiki/Configuration | # # | | # # | New options are not added to this file automatically. Default values are used if an | # # | option cannot be found. The latest config versions can be obtained at the link above. | # # +----------------------------------------------------------------------------------------------+ # #################################################################################################### # +----------------------------------------------------------------------------------------------+ # # | | # # | ESSENTIAL SETTINGS | # # | | # # | Important settings that control how LuckPerms functions. | # # | | # # +----------------------------------------------------------------------------------------------+ # # The name of the server, used for server specific permissions. # # - When set to "global" this setting is effectively ignored. # - In all other cases, the value here is added to all players in a "server" context. # - See: https://luckperms.net/wiki/Context server: global # If the servers own UUID cache/lookup facility should be used when there is no record for a player # already in LuckPerms. # # - When this is set to 'false', commands using a player's username will not work unless the player # has joined since LuckPerms was first installed. # - To get around this, you can use a player's uuid directly in the command, or enable this option. # - When this is set to 'true', the server facility is used. This may use a number of methods, # including checking the servers local cache, or making a request to the Mojang API. use-server-uuid-cache: false # +----------------------------------------------------------------------------------------------+ # # | | # # | STORAGE SETTINGS | # # | | # # | Controls which storage method LuckPerms will use to store data. | # # | | # # +----------------------------------------------------------------------------------------------+ # # How the plugin should store data # # - The various options are explained in more detail on the wiki: # https://luckperms.net/wiki/Storage-types # # - Possible options: # # | Remote databases - require connection information to be configured below # |=> MySQL # |=> MariaDB (preferred over MySQL) # |=> PostgreSQL # |=> MongoDB # # | Flatfile/local database - don't require any extra configuration # |=> H2 (preferred over SQLite) # |=> SQLite # # | Readable & editable text files - don't require any extra configuration # |=> YAML (.yml files) # |=> JSON (.json files) # |=> HOCON (.conf files) # |=> TOML (.toml files) # | # | By default, user, group and track data is separated into different files. Data can be combined # | and all stored in the same file by switching to a combined storage variant. # | Just add '-combined' to the end of the storage-method, e.g. 'yaml-combined' # # - A H2 database is the default option. # - If you want to edit data manually in "traditional" storage files, we suggest using YAML. storage-method: MariaDB # The following block defines the settings for remote database storage methods. # # - You don't need to touch any of the settings here if you're using a local storage method! # - The connection detail options are shared between all remote storage types. data: # Define the address and port for the database. # - The standard DB engine port is used by default # (MySQL: 3306, PostgreSQL: 5432, MongoDB: 27017) # - Specify as "host:port" if differs address: mariadb # The name of the database to store LuckPerms data in. # - This must be created already. Don't worry about this setting if you're using MongoDB. database: luckperms # Credentials for the database. username: luckperms password: 'luckpermspw' # These settings apply to the MySQL connection pool. # - The default values will be suitable for the majority of users. # - Do not change these settings unless you know what you're doing! pool-settings: # Sets the maximum size of the MySQL connection pool. # - Basically this value will determine the maximum number of actual # connections to the database backend. # - More information about determining the size of connection pools can be found here: # https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing maximum-pool-size: 10 # Sets the minimum number of idle connections that the pool will try to maintain. # - For maximum performance and responsiveness to spike demands, it is recommended to not set # this value and instead allow the pool to act as a fixed size connection pool. # (set this value to the same as 'maximum-pool-size') minimum-idle: 10 # This setting controls the maximum lifetime of a connection in the pool in milliseconds. # - The value should be at least 30 seconds less than any database or infrastructure imposed # connection time limit. maximum-lifetime: 1800000 # 30 minutes # This setting controls how frequently the pool will 'ping' a connection in order to prevent it # from being timed out by the database or network infrastructure, measured in milliseconds. # - The value should be less than maximum-lifetime and greater than 30000 (30 seconds). # - Setting the value to zero will disable the keepalive functionality. keepalive-time: 0 # This setting controls the maximum number of milliseconds that the plugin will wait for a # connection from the pool, before timing out. connection-timeout: 5000 # 5 seconds # This setting allows you to define extra properties for connections. # # By default, the following options are set to enable utf8 encoding. (you may need to remove # these if you are using PostgreSQL) # useUnicode: true # characterEncoding: utf8 # # You can also use this section to disable SSL connections, by uncommenting the 'useSSL' and # 'verifyServerCertificate' options below. properties: useUnicode: true characterEncoding: utf8 #useSSL: false #verifyServerCertificate: false # The prefix for all LuckPerms SQL tables. # # - This only applies for remote SQL storage types (MySQL, MariaDB, etc). # - Change this if you want to use different tables for different servers. table-prefix: 'luckperms_' # The prefix to use for all LuckPerms MongoDB collections. # # - This only applies for the MongoDB storage type. # - Change this if you want to use different collections for different servers. The default is no # prefix. mongodb-collection-prefix: '' # The connection string URI to use to connect to the MongoDB instance. # # - When configured, this setting will override anything defined in the address, database, # username or password fields above. # - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it # below. # - For more information, please see https://docs.mongodb.com/manual/reference/connection-string/ mongodb-connection-uri: '' # Define settings for a "split" storage setup. # # - This allows you to define a storage method for each type of data. # - The connection options above still have to be correct for each type here. split-storage: # Don't touch this if you don't want to use split storage! enabled: false methods: # These options don't need to be modified if split storage isn't enabled. user: h2 group: h2 track: h2 uuid: h2 log: h2 # +----------------------------------------------------------------------------------------------+ # # | | # # | UPDATE PROPAGATION & MESSAGING SERVICE | # # | | # # | Controls the ways in which LuckPerms will sync data & notify other servers of changes. | # # | These options are documented on greater detail on the wiki under "Instant Updates". | # # | | # # +----------------------------------------------------------------------------------------------+ # # This option controls how frequently LuckPerms will perform a sync task. # # - A sync task will refresh all data from the storage, and ensure that the most up-to-date data is # being used by the plugin. # - This is disabled by default, as most users will not need it. However, if you're using a remote # storage type without a messaging service setup, you may wish to set this to something like 3. # - Set to -1 to disable the task completely. sync-minutes: -1 # If the file watcher should be enabled. # # - When using a file-based storage type, LuckPerms can monitor the data files for changes, and # automatically update when changes are detected. # - If you don't want this feature to be active, set this option to false. watch-files: true # Define which messaging service should be used by the plugin. # # - If enabled and configured, LuckPerms will use the messaging service to inform other connected # servers of changes. # - Use the command "/lp networksync" to manually push changes. # - Data is NOT stored using this service. It is only used as a messaging platform. # # - If you decide to enable this feature, you should set "sync-minutes" to -1, as there is no need # for LuckPerms to poll the database for changes. # # - Possible options: # => sql Uses the SQL database to form a queue system for communication. Will only work when # 'storage-method' is set to MySQL or MariaDB. This is chosen by default if the # option is set to 'auto' and SQL storage is in use. Set to 'notsql' to disable this. # => pluginmsg Uses the plugin messaging channels to communicate with the proxy. # LuckPerms must be installed on your proxy & all connected servers backend servers. # Won't work if you have more than one proxy. # => lilypad Uses LilyPad pub-sub to push changes. You need to have the LilyPad-Connect plugin # installed. # => redis Uses Redis pub-sub to push changes. Your server connection info must be configured # below. # => rabbitmq Uses RabbitMQ pub-sub to push changes. Your server connection info must be # configured below. # => custom Uses a messaging service provided using the LuckPerms API. # => auto Attempts to automatically setup a messaging service using redis or sql. messaging-service: auto # If LuckPerms should automatically push updates after a change has been made with a command. auto-push-updates: true # If LuckPerms should push logging entries to connected servers via the messaging service. push-log-entries: true # If LuckPerms should broadcast received logging entries to players on this platform. # # - If you have LuckPerms installed on your backend servers as well as a BungeeCord proxy, you # should set this option to false on either your backends or your proxies, to avoid players being # messaged twice about log entries. broadcast-received-log-entries: true # Settings for Redis. # Port 6379 is used by default; set address to "host:port" if differs redis: enabled: false address: localhost username: '' password: '' # Settings for RabbitMQ. # Port 5672 is used by default; set address to "host:port" if differs rabbitmq: enabled: false address: localhost vhost: '/' username: 'guest' password: 'guest' # +----------------------------------------------------------------------------------------------+ # # | | # # | CUSTOMIZATION SETTINGS | # # | | # # | Settings that allow admins to customize the way LuckPerms operates. | # # | | # # +----------------------------------------------------------------------------------------------+ # # Controls how temporary permissions/parents/meta should be accumulated. # # - The default behaviour is "deny". # - This behaviour can also be specified when the command is executed. See the command usage # documentation for more info. # # - Possible options: # => accumulate durations will be added to the existing expiry time # => replace durations will be replaced if the new duration is later than the current # expiration # => deny the command will just fail if you try to add another node with the same expiry temporary-add-behaviour: deny # Controls how LuckPerms will determine a users "primary" group. # # - The meaning and influence of "primary groups" are explained in detail on the wiki. # - The preferred approach is to let LuckPerms automatically determine a users primary group # based on the relative weight of their parent groups. # # - Possible options: # => stored use the value stored against the users record in the file/database # => parents-by-weight just use the users most highly weighted parent # => all-parents-by-weight same as above, but calculates based upon all parents inherited from # both directly and indirectly primary-group-calculation: parents-by-weight # If the plugin should check for "extra" permissions with users run LP commands. # # - These extra permissions allow finer control over what users can do with each command, and who # they have access to edit. # - The nature of the checks are documented on the wiki under "Argument based command permissions". # - Argument based permissions are *not* static, unlike the 'base' permissions, and will depend upon # the arguments given within the command. argument-based-command-permissions: false # If the plugin should check whether senders are a member of a given group before they're able to # edit the groups data or add/remove other users to/from it. # Note: these limitations do not apply to the web editor! require-sender-group-membership-to-modify: false # If the plugin should send log notifications to users whenever permissions are modified. # # - Notifications are only sent to those with the appropriate permission to receive them # - They can also be temporarily enabled/disabled on a per-user basis using # '/lp log notify ' log-notify: true # Defines a list of log entries which should not be sent as notifications to users. # # - Each entry in the list is a RegEx expression which is matched against the log entry description. log-notify-filtered-descriptions: # - "parent add example" # If LuckPerms should automatically install translation bundles and periodically update them. auto-install-translations: true # Defines the options for prefix and suffix stacking. # # - The feature allows you to display multiple prefixes or suffixes alongside a players username in # chat. # - It is explained and documented in more detail on the wiki under "Prefix & Suffix Stacking". # # - The options are divided into separate sections for prefixes and suffixes. # - The 'duplicates' setting refers to how duplicate elements are handled. Can be 'retain-all', # 'first-only' or 'last-only'. # - The value of 'start-spacer' is included at the start of the resultant prefix/suffix. # - The value of 'end-spacer' is included at the end of the resultant prefix/suffix. # - The value of 'middle-spacer' is included between each element in the resultant prefix/suffix. # # - Possible format options: # => highest Selects the value with the highest weight, from all values # held by or inherited by the player. # # => lowest Same as above, except takes the one with the lowest weight. # # => highest_own Selects the value with the highest weight, but will not # accept any inherited values. # # => lowest_own Same as above, except takes the value with the lowest weight. # # => highest_inherited Selects the value with the highest weight, but will only # accept inherited values. # # => lowest_inherited Same as above, except takes the value with the lowest weight. # # => highest_on_track_ Selects the value with the highest weight, but only if the # value was inherited from a group on the given track. # # => lowest_on_track_ Same as above, except takes the value with the lowest weight. # # => highest_not_on_track_ Selects the value with the highest weight, but only if the # value was inherited from a group not on the given track. # # => lowest_not_on_track_ Same as above, except takes the value with the lowest weight. # # => highest_from_group_ Selects the value with the highest weight, but only if the # value was inherited from the given group. # # => lowest_from_group_ Same as above, except takes the value with the lowest weight. # # => highest_not_from_group_ Selects the value with the highest weight, but only if the # value was not inherited from the given group. # # => lowest_not_from_group_ Same as above, except takes the value with the lowest weight. meta-formatting: prefix: format: - "highest" duplicates: first-only start-spacer: "" middle-spacer: " " end-spacer: "" suffix: format: - "highest" duplicates: first-only start-spacer: "" middle-spacer: " " end-spacer: "" # +----------------------------------------------------------------------------------------------+ # # | | # # | PERMISSION CALCULATION AND INHERITANCE | # # | | # # | Modify the way permission checks, meta lookups and inheritance resolutions are handled. | # # | | # # +----------------------------------------------------------------------------------------------+ # # The algorithm LuckPerms should use when traversing the "inheritance tree". # # - Possible options: # => breadth-first See: https://en.wikipedia.org/wiki/Breadth-first_search # => depth-first-pre-order See: https://en.wikipedia.org/wiki/Depth-first_search # => depth-first-post-order See: https://en.wikipedia.org/wiki/Depth-first_search inheritance-traversal-algorithm: depth-first-pre-order # If a final sort according to "inheritance rules" should be performed after the traversal algorithm # has resolved the inheritance tree. # # "Inheritance rules" refers to things such as group weightings, primary group status, and the # natural contextual ordering of the group nodes. # # Setting this to 'true' will allow for the inheritance rules to take priority over the structure of # the inheritance tree. # # Effectively when this setting is 'true': the tree is flattened, and rules applied afterwards, # and when this setting is 'false':, the rules are just applied during each step of the traversal. post-traversal-inheritance-sort: false # Defines the mode used to determine whether a set of contexts are satisfied. # # - Possible options: # => at-least-one-value-per-key Set A will be satisfied by another set B, if at least one of the # key-value entries per key in A are also in B. # => all-values-per-key Set A will be satisfied by another set B, if all key-value # entries in A are also in B. context-satisfy-mode: at-least-one-value-per-key # LuckPerms has a number of built-in contexts. These can be disabled by adding the context key to # the list below. disabled-contexts: # - "world" # +----------------------------------------------------------------------------------------------+ # # | Permission resolution settings | # # +----------------------------------------------------------------------------------------------+ # # If users on this server should have their global permissions applied. # When set to false, only server specific permissions will apply for users on this server include-global: true # If users on this server should have their global world permissions applied. # When set to false, only world specific permissions will apply for users on this server include-global-world: true # If users on this server should have global (non-server specific) groups applied apply-global-groups: true # If users on this server should have global (non-world specific) groups applied apply-global-world-groups: true # +----------------------------------------------------------------------------------------------+ # # | Meta lookup settings | # # +----------------------------------------------------------------------------------------------+ # # Defines how meta values should be selected. # # - Possible options: # => inheritance Selects the meta value that was inherited first # => highest-number Selects the highest numerical meta value # => lowest-number Selects the lowest numerical meta value meta-value-selection-default: inheritance # Defines how meta values should be selected per key. meta-value-selection: # max-homes: highest-number # +----------------------------------------------------------------------------------------------+ # # | Inheritance settings | # # +----------------------------------------------------------------------------------------------+ # # If the plugin should apply wildcard permissions. # # - If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered # permissions matching the wildcard. apply-wildcards: true # If LuckPerms should resolve and apply permissions according to the Sponge style implicit wildcard # inheritance system. # # - That being: If a user has been granted "example", then the player should have also be # automatically granted "example.function", "example.another", "example.deeper.nesting", # and so on. apply-sponge-implicit-wildcards: false # If the plugin should apply negated Bukkit default permissions before it considers wildcard # assignments. # # - Plugin authors can define permissions which explicitly should not be given automatically to OPs. # This is usually used for so called "anti-permissions" - permissions which, when granted, apply # something negative. # - If this option is set to true, LuckPerms will consider any negated declarations made by # plugins before it considers wildcards. (similar to the way the OP system works) # - If this option is set to false, LuckPerms will consider any wildcard assignments first. apply-default-negated-permissions-before-wildcards: false # If the plugin should parse regex permissions. # # - If set to true, LuckPerms will detect regex permissions, marked with "r=" at the start of the # node, and resolve & apply all registered permissions matching the regex. apply-regex: true # If the plugin should complete and apply shorthand permissions. # # - If set to true, LuckPerms will detect and expand shorthand node patterns. apply-shorthand: true # If the plugin should apply Bukkit child permissions. # # - Plugin authors can define custom permissions structures for their plugin, which will be resolved # and used by LuckPerms if this setting is enabled. apply-bukkit-child-permissions: true # If the plugin should apply Bukkit default permissions. # # - Plugin authors can define permissions which should be given to all users by default, or setup # permissions which should/shouldn't be given to opped players. # - If this option is set to false, LuckPerms will ignore these defaults. apply-bukkit-default-permissions: true # If the plugin should apply attachment permissions. # # - Other plugins on the server are able to add their own "permission attachments" to players. # - This allows them to grant players additional permissions which last until the end of the # session, or until they're removed. # - If this option is set to false, LuckPerms will not include these attachment permissions when # considering if a player should have access to a certain permission. apply-bukkit-attachment-permissions: true # +----------------------------------------------------------------------------------------------+ # # | Extra settings | # # +----------------------------------------------------------------------------------------------+ # # A list of context calculators which will be skipped when calculating contexts. # # - You can disable context calculators by either: # => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator) # => specifying a sub-section of the Java package used by the calculator (e.g. com.example) disabled-context-calculators: [] # Allows you to set "aliases" for the worlds sent forward for context calculation. # # - These aliases are provided in addition to the real world name. Applied recursively. # - Remove the comment characters for the default aliases to apply. world-rewrite: # world_nether: world # world_the_end: world # Define special group weights for this server. # # - Group weights can also be applied directly to group data, using the setweight command. # - This section allows weights to be set on a per-server basis. group-weight: # admin: 10 # +----------------------------------------------------------------------------------------------+ # # | | # # | FINE TUNING OPTIONS | # # | | # # | A number of more niche settings for tweaking and changing behaviour. The section also | # # | contains toggles for some more specialised features. It is only necessary to make changes to | # # | these options if you want to fine-tune LuckPerms behaviour. | # # | | # # +----------------------------------------------------------------------------------------------+ # # +----------------------------------------------------------------------------------------------+ # # | Server Operator (OP) settings | # # +----------------------------------------------------------------------------------------------+ # # Controls whether server operators should exist at all. # # - When set to 'false', all players will be de-opped, and the /op and /deop commands will be # disabled. Note that vanilla features like the spawn-protection require an operator on the # server to work. enable-ops: true # Enables or disables a special permission based system in LuckPerms for controlling OP status. # # - If set to true, any user with the permission "luckperms.autoop" will automatically be granted # server operator status. This permission can be inherited, or set on specific servers/worlds, # temporarily, etc. # - Additionally, setting this to true will force the "enable-ops" option above to false. All users # will be de-opped unless they have the permission node, and the op/deop commands will be # disabled. # - It is recommended that you use this option instead of assigning a single '*' permission. auto-op: false # Defines if "opped" players should be able to use all LuckPerms commands by default. # # - Set to false to only allow users who have the permissions access to the commands commands-allow-op: true # +----------------------------------------------------------------------------------------------+ # # | Vault integration settings | # # +----------------------------------------------------------------------------------------------+ # # If Vault lookups for offline players on the main server thread should be enabled. # # LuckPerms has a "catch" for plugins attempting to perform unsafe offline player data lookups # from the main server thread. This catch raises an exception (causes an error to occur) when unsafe # lookups are made, instead of allowing the lookup to happen, which would likely cause the server # to lag. # # However, if you're willing to accept the consequences, the catch can be disabled by setting this # option to 'true. vault-unsafe-lookups: false # If LuckPerms should use the 'display name' of a group when returning groups in Vault API calls. # # - When this option is set to true, the display name of the group is returned. # - When this option is set to false, the standard name/id of the group is returned. vault-group-use-displaynames: true # Controls which group LuckPerms should use for NPC players when handling Vault requests. # # - As NPCs aren't actually real players, LuckPerms does not load any user data for them. This # becomes an issue when plugins want to check for their permissions using Vault. # - As a solution, Vault checks for NPCs fallback to a group, which is defined below. vault-npc-group: default # Controls how LuckPerms should consider the OP status of NPC players when handing Vault requests. # # - If you want NPCs to have the same permissions as "normal" players, set this option to false. # - If you want NPCs to have OP status, set this option to true. vault-npc-op-status: false # If the vault-server option below should be used. # # - When this option is set to false, the server value defined above under "server" is used. use-vault-server: false # The name of the server used within Vault operations. # # - If you don't want Vault operations to be server specific, set this to "global". # - Will only take effect if use-vault-server is set to true above. vault-server: global # If global permissions should be considered when retrieving meta or player groups vault-include-global: true # If Vault operations should ignore any world arguments if supplied. vault-ignore-world: false # +----------------------------------------------------------------------------------------------+ # # | Miscellaneous (and rarely used) settings | # # +----------------------------------------------------------------------------------------------+ # # If LuckPerms should produce extra logging output when it handles logins. # # - Useful if you're having issues with UUID forwarding or data not being loaded. debug-logins: false # If LuckPerms should allow usernames with non alphanumeric characters. # # - Note that due to the design of the storage implementation, usernames must still be 16 characters # or less. allow-invalid-usernames: false # If LuckPerms should not require users to confirm bulkupdate operations. # # - When set to true, operations will be executed immediately. # - This is not recommended, as bulkupdate has the potential to irreversibly delete large amounts of # data, and is not designed to be executed automatically. # - If automation is needed, users should prefer using the LuckPerms API. skip-bulkupdate-confirmation: false # If LuckPerms should prevent bulkupdate operations. # # - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work. # - When set to false, bulkupdate operations will be allowed via the console. disable-bulkupdate: false # If LuckPerms should allow a users primary group to be removed with the 'parent remove' command. # # - When this happens, the plugin will set their primary group back to default. prevent-primary-group-removal: false # If LuckPerms should update the list of commands sent to the client when permissions are changed. update-client-command-list: true # If LuckPerms should attempt to register "Brigadier" command list data for its commands. register-command-list-data: true # If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands. # See here for more info: https://minecraft.wiki/Commands#Target_selectors resolve-command-selectors: false ================================================ FILE: examples/multi-project/servers/velocity-config/velocity.toml ================================================ # Config version. Do not change this config-version = "2.6" # What port should the proxy be bound to? By default, we'll bind to all addresses on port 25565. bind = "0.0.0.0:25565" # What should be the MOTD? This gets displayed when the player adds your server to # their server list. Only MiniMessage format is accepted. motd = "<#09add3>A Velocity Server" # What should we display for the maximum number of players? (Velocity does not support a cap # on the number of players online.) show-max-players = 500 # Should we authenticate players with Mojang? By default, this is on. online-mode = true # Should the proxy enforce the new public key security standard? By default, this is on. force-key-authentication = true # If client's ISP/AS sent from this proxy is different from the one from Mojang's # authentication server, the player is kicked. This disallows some VPN and proxy # connections but is a weak form of protection. prevent-client-proxy-connections = false # Should we forward IP addresses and other data to backend servers? # Available options: # - "none": No forwarding will be done. All players will appear to be connecting # from the proxy and will have offline-mode UUIDs. # - "legacy": Forward player IPs and UUIDs in a BungeeCord-compatible format. Use this # if you run servers using Minecraft 1.12 or lower. # - "bungeeguard": Forward player IPs and UUIDs in a format supported by the BungeeGuard # plugin. Use this if you run servers using Minecraft 1.12 or lower, and are # unable to implement network level firewalling (on a shared host). # - "modern": Forward player IPs and UUIDs as part of the login process using # Velocity's native forwarding. Only applicable for Minecraft 1.13 or higher. player-info-forwarding-mode = "NONE" # If you are using modern or BungeeGuard IP forwarding, configure a file that contains a unique secret here. # The file is expected to be UTF-8 encoded and not empty. forwarding-secret-file = "forwarding.secret" # Announce whether or not your server supports Forge. If you run a modded server, we # suggest turning this on. # # If your network runs one modpack consistently, consider using ping-passthrough = "mods" # instead for a nicer display in the server list. announce-forge = false # If enabled (default is false) and the proxy is in online mode, Velocity will kick # any existing player who is online if a duplicate connection attempt is made. kick-existing-players = false # Should Velocity pass server list ping requests to a backend server? # Available options: # - "disabled": No pass-through will be done. The velocity.toml and server-icon.png # will determine the initial server list ping response. # - "mods": Passes only the mod list from your backend server into the response. # The first server in your try list (or forced host) with a mod list will be # used. If no backend servers can be contacted, Velocity won't display any # mod information. # - "description": Uses the description and mod list from the backend server. The first # server in the try (or forced host) list that responds is used for the # description and mod list. # - "all": Uses the backend server's response as the proxy response. The Velocity # configuration is used if no servers could be contacted. ping-passthrough = "DISABLED" # If not enabled (default is true) player IP addresses will be replaced by in logs enable-player-address-logging = true [servers] # Configure your servers here. Each key represents the server's name, and the value # represents the IP address of the server to connect to. lobby = "127.0.0.1:30066" factions = "127.0.0.1:30067" minigames = "127.0.0.1:30068" # In what order we should try servers when a player logs in or is kicked from a server. try = [ "lobby" ] [forced-hosts] # Configure your forced hosts here. "lobby.example.com" = [ "lobby" ] "factions.example.com" = [ "factions" ] "minigames.example.com" = [ "minigames" ] [advanced] # How large a Minecraft packet has to be before we compress it. Setting this to zero will # compress all packets, and setting it to -1 will disable compression entirely. compression-threshold = 256 # How much compression should be done (from 0-9). The default is -1, which uses the # default level of 6. compression-level = -1 # How fast (in milliseconds) are clients allowed to connect after the last connection? By # default, this is three seconds. Disable this by setting this to 0. login-ratelimit = 3000 # Specify a custom timeout for connection timeouts here. The default is five seconds. connection-timeout = 5000 # Specify a read timeout for connections here. The default is 30 seconds. read-timeout = 30000 # Enables compatibility with HAProxy's PROXY protocol. If you don't know what this is for, then # don't enable it. haproxy-protocol = false # Enables TCP fast open support on the proxy. Requires the proxy to run on Linux. tcp-fast-open = false # Enables BungeeCord plugin messaging channel support on Velocity. bungee-plugin-message-channel = true # Shows ping requests to the proxy from clients. show-ping-requests = false # By default, Velocity will attempt to gracefully handle situations where the user unexpectedly # loses connection to the server without an explicit disconnect message by attempting to fall the # user back, except in the case of read timeouts. BungeeCord will disconnect the user instead. You # can disable this setting to use the BungeeCord behavior. failover-on-unexpected-server-disconnect = true # Declares the proxy commands to 1.13+ clients. announce-proxy-commands = true # Enables the logging of commands log-command-executions = false # Enables logging of player connections when connecting to the proxy, switching servers # and disconnecting from the proxy. log-player-connections = true [query] # Whether to enable responding to GameSpy 4 query responses or not. enabled = false # If query is enabled, on what port should the query protocol listen on? port = 25565 # This is the map name that is reported to the query services. map = "Velocity" # Whether plugins should be shown in query response by default or not show-plugins = false ================================================ FILE: examples/multi-project/servers/waterfall-config/config.yml ================================================ server_connect_timeout: 5000 enforce_secure_profile: false remote_ping_cache: -1 forge_support: true player_limit: -1 permissions: default: - bungeecord.command.server - bungeecord.command.list admin: - bungeecord.command.alert - bungeecord.command.end - bungeecord.command.ip - bungeecord.command.reload - bungeecord.command.kick timeout: 30000 log_commands: false network_compression_threshold: 256 online_mode: true disabled_commands: - disabledcommandhere servers: mc: motd: 'Lobby' address: mc:25565 restricted: false listeners: - query_port: 25577 motd: '&1Another Bungee server' tab_list: GLOBAL_PING query_enabled: false proxy_protocol: false forced_hosts: pvp.md-5.net: pvp ping_passthrough: false priorities: - mc bind_local_address: true host: 0.0.0.0:25577 max_players: 1 tab_size: 60 force_default_server: false ip_forward: false remote_ping_timeout: 5000 prevent_proxy_connections: false groups: md_5: - admin connection_throttle: 4000 stats: 39440d41-8d83-40cb-a6a7-d95c3197c4d2 connection_throttle_limit: 3 log_pings: true ================================================ FILE: examples/multi-project/servers/waterfall-config/waterfall.yml ================================================ plugin_channel_name_limit: 128 use_netty_dns_resolver: true disable_modern_tab_limiter: true log_initial_handler_connections: true throttling: tab_complete: 1000 game_version: '' disable_tab_list_rewrite: true registered_plugin_channels_limit: 128 disable_entity_metadata_rewrite: false ================================================ FILE: examples/multiline-motd/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: true MOTD: | A §l§cMinecraft§r server line two # or # MOTD: "line one\nline two" ports: - "25565:25565" ================================================ FILE: examples/neoforge/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: true TYPE: NEOFORGE VERSION: latest NEOFORGE_VERSION: beta ports: - "25565:25565" volumes: - mc:/data volumes: mc: {} ================================================ FILE: examples/ngrok/compose.yml ================================================ services: mc: image: itzg/minecraft-server tty: true stdin_open: true ports: - "25565:25565" environment: EULA: "TRUE" restart: unless-stopped volumes: # attach the relative directory 'data' to the container's /data path - ./data:/data ngrok: image: ngrok/ngrok:latest command: - "tcp" - "mc:25565" environment: NGROK_AUTHTOKEN: ports: - 4551:4551 ================================================ FILE: examples/optimized-server-config/README.md ================================================ # Optimized Server Configuration (Paper, Purpur, Pufferfish) This 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. ## Features - **Automatic Download**: Fetches `bukkit.yml`, `spigot.yml`, and server-specific configs (`paper-global.yml`, `purpur.yml`, `pufferfish.yml`) on startup. - **Version Aware**: Downloads configurations from a folder matching your Minecraft version (e.g., `1.21.1`). - **Base `server.properties`**: Optionally download a base `server.properties` file while still allowing environment variable overrides. ## Repository Structure Your configuration repository should be structured by Minecraft version. For example: ```text my-config-repo/ ├── 1.21.1/ │ ├── bukkit.yml │ ├── spigot.yml │ ├── purpur.yml │ ├── pufferfish.yml │ └── server.properties ├── 1.20.4/ │ └── ... └── README.md ``` ## Docker Compose Examples ### 1. Paper Server For Paper, use `PAPER_CONFIG_REPO`. ```yaml version: "3.8" services: mc: image: itzg/minecraft-server environment: EULA: "TRUE" TYPE: "PAPER" VERSION: "1.21.1" # URL to the root of your config repository (e.g. raw.githubusercontent.com) # The script automatically appends "//" to this URL. PAPER_CONFIG_REPO: "https://raw.githubusercontent.com/Alpha018/paper-config-optimized/refs/heads/main" ``` ### 2. Purpur Server For Purpur, use `PURPUR_CONFIG_REPO`. ```yaml version: "3.8" services: mc: image: itzg/minecraft-server environment: EULA: "TRUE" TYPE: "PURPUR" VERSION: "1.21.1" # URL to the root of your config repository (e.g. raw.githubusercontent.com) # The script automatically appends "//" to this URL. PURPUR_CONFIG_REPO: "https://raw.githubusercontent.com/Alpha018/paper-config-optimized/refs/heads/main" ``` ### 2. Pufferfish Server For Pufferfish, use `PUFFERFISH_CONFIG_REPO`. ```yaml version: "3.8" services: mc: image: itzg/minecraft-server environment: EULA: "TRUE" TYPE: "PUFFERFISH" VERSION: "1.21.1" # Must match a folder in your repo # Automagically download optimized configs (bukkit.yml, spigot.yml, pufferfish.yml) PUFFERFISH_CONFIG_REPO: "https://raw.githubusercontent.com/Alpha018/paper-config-optimized/refs/heads/main" ``` ### 3. Downloading `server.properties` (Optional) If you also want to download a base `server.properties` file from your repository, you must explicitly set `SERVER_PROPERTIES_REPO_URL`. **Smart Feature**: - If you provide a **base URL** (like the repo root), the script will automatically append `//server.properties`. - If you provide a **full URL** (ending in `.properties`), it will download that specific file. **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. ```yaml environment: # ... other vars ... # Option A: Base URL (Smart) # Will look for: https://.../main/1.21.1/server.properties SERVER_PROPERTIES_REPO_URL: "https://raw.githubusercontent.com/Alpha018/paper-config-optimized/refs/heads/main" # Option B: Specific URL (Manual) # SERVER_PROPERTIES_REPO_URL: "https://gist.githubusercontent.com/.../server.properties" # These will OVERRIDE settings in the downloaded file MOTD: "My Custom Instance" MAX_PLAYERS: 50 ``` ## Supported Variables | Variable | Description | | :--- | :--- | | `PAPER_CONFIG_REPO` | Base URL for Paper configs. Downloads `paper-global.yml`, `bukkit.yml`, `spigot.yml`, etc. | | `PURPUR_CONFIG_REPO` | Base URL for Purpur configs. Downloads `purpur.yml`, `bukkit.yml`, `spigot.yml`. | | `PUFFERFISH_CONFIG_REPO` | Base URL for Pufferfish configs. Downloads `pufferfish.yml`, `bukkit.yml`, `spigot.yml`. | | `SERVER_PROPERTIES_REPO_URL` | Boolean/URL. Set to download `server.properties`. Can be a base URL or direct file link. | ## Contribution Got ideas to squeeze even more performance out of these configs? Or maybe you found a better way to structure things? I'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. ================================================ FILE: examples/optimized-server-config/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server container_name: paper environment: EULA: "true" TYPE: "PAPER" VIEW_DISTANCE: 10 MEMORY: 2G PAPER_CONFIG_REPO: "https://raw.githubusercontent.com/Alpha018/paper-config-optimized/main" ports: - "25565:25565" volumes: - mc-paper:/data restart: unless-stopped volumes: mc-paper: {} ================================================ FILE: examples/packwiz/compose.yml ================================================ services: mc: image: itzg/minecraft-server tty: true stdin_open: true environment: EULA: true # Match loader from versions section of https://github.com/packwiz/packwiz-example-pack/blob/v1/pack.toml TYPE: QUILT VERSION: "1.19" QUILT_LOADER_VERSION: "0.17.0" PACKWIZ_URL: https://raw.githubusercontent.com/packwiz/packwiz-example-pack/refs/heads/v1/pack.toml volumes: - ./data:/data ports: - "25565:25565" ================================================ FILE: examples/paper/compose.yml ================================================ services: mc: image: itzg/minecraft-server tty: true stdin_open: true environment: EULA: "true" TYPE: PAPER MEMORY: 2G ports: - "25565:25565" volumes: - mc-data:/data restart: unless-stopped volumes: mc-data: {} ================================================ FILE: examples/paper/paper-build-plugins/Dockerfile ================================================ FROM itzg/minecraft-server ENV TYPE=PAPER COPY plugins/*.jar /plugins/ ================================================ FILE: examples/paper/paper-build-plugins/docker-compose.yml ================================================ services: mc: build: . environment: EULA: "TRUE" ports: - 25565:25565 stdin_open: true tty: true ================================================ FILE: examples/paper/paper-build-plugins/plugins/.gitignore ================================================ *.jar ================================================ FILE: examples/paper/paper-build-plugins/plugins/README.md ================================================ 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. ================================================ FILE: examples/paper/paper-proxy-protocol/config/paper-global.yml ================================================ proxies: proxy-protocol: true ================================================ FILE: examples/paper/paper-proxy-protocol/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: true TYPE: PAPER ports: # Use host port 25566 assuming mc-router or similar is bound to 25565 - "25566:25565" volumes: - mc-data:/data - ./config:/config:ro volumes: mc-data: {} ================================================ FILE: examples/proxied/compose.yml ================================================ services: mc: # build: . image: itzg/minecraft-server ports: - 25565:25565 environment: EULA: "TRUE" # TYPE: SPIGOT # VERSION: 1.11.2 PROXY: proxy:3128 proxy: image: sameersbn/squid ================================================ FILE: examples/quilt/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: "true" TYPE: QUILT ports: - "25565:25565" volumes: - data:/data volumes: data: {} ================================================ FILE: examples/rlcraft/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server:java8-multiarch environment: EULA: true MODPACK_PLATFORM: AUTO_CURSEFORGE # Allocate API key from https://console.curseforge.com/ # and set in .env file making sure to double up dollar signs, such as # CF_API_KEY=$$2a$$10$$.... # Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key CF_API_KEY: ${CF_API_KEY} CF_SLUG: rlcraft CF_FILENAME_MATCHER: 2.9.3 CF_FORCE_SYNCHRONIZE: true MEMORY: 4G volumes: - ./data:/data ports: - "25565:25565" ================================================ FILE: examples/simple-voice-chat/fabric-compose.yaml ================================================ services: mc: image: itzg/minecraft-server tty: true stdin_open: true ports: # Game port - "25565:25565/tcp" # Voice chat port - "24454:24454/udp" environment: EULA: "TRUE" TYPE: "FABRIC" VERSION: "1.21.1" # This will select the latest version of simple voice chat for 1.21.1. # You can specify a version by appending :versionID (e.g. simple-voice-chat:anabvqRL) MODRINTH_PROJECTS: "simple-voice-chat" MEMORY: 8G volumes: # attach the relative directory 'data' to the container's /data path - ./data:/data ================================================ FILE: examples/simple-voice-chat/neoforge-compose.yaml ================================================ services: mc: image: itzg/minecraft-server tty: true stdin_open: true ports: # Game port - "25565:25565/tcp" # Voice chat port - "24454:24454/udp" environment: EULA: "TRUE" TYPE: "NEOFORGE" VERSION: "1.21.1" # This will select the latest version of simple voice chat for 1.21.1. # You can specify a version by appending :versionID (e.g. simple-voice-chat:anabvqRL) MODRINTH_PROJECTS: "simple-voice-chat" MEMORY: 8G volumes: # attach the relative directory 'data' to the container's /data path - ./data:/data ================================================ FILE: examples/simple-voice-chat/paper-compose.yaml ================================================ services: mc: image: itzg/minecraft-server tty: true stdin_open: true ports: # Game port - "25565:25565/tcp" # Voice chat port - "24454:24454/udp" environment: EULA: "TRUE" TYPE: "PAPER" VERSION: "1.21.1" # This will select the latest version of simple voice chat for 1.21.1. # You can specify a version by appending :versionID (e.g. simple-voice-chat:anabvqRL) MODRINTH_PROJECTS: "simple-voice-chat" MEMORY: 8G volumes: # attach the relative directory 'data' to the container's /data path - ./data:/data ================================================ FILE: examples/skript/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: true TYPE: PAPER PLUGINS: https://github.com/SkriptLang/Skript/releases/download/2.8.4/Skript.jar SPIGET_RESOURCES: 103749 ports: - "25565:25565" ================================================ FILE: examples/spiget/docker-compose.yml ================================================ # NOTE # This file is purposely named spiget with an "e" since it provides an example of the # feature https://docker-minecraft-server.readthedocs.io/en/latest/mods-and-plugins/spiget/ # which uses the Spiget API at https://spiget.org/ services: mc: image: itzg/minecraft-server ports: - "25565:25565" environment: EULA: "TRUE" TYPE: PAPER SPIGET_RESOURCES: 34315,3836 REMOVE_OLD_MODS: true volumes: - ./data:/data ================================================ FILE: examples/spigot/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server environment: EULA: true TYPE: SPIGOT VERSION: 1.20.4 ports: - "25565:25565" volumes: - data:/data volumes: data: {} ================================================ FILE: examples/tailscale/compose.yml ================================================ services: tailscale-client: image: tailscale/tailscale:latest container_name: tailscale hostname: tailscale-minecraft # This name will be the one on the tailscale network environment: TS_AUTHKEY: "tskey-auth-PLACE-YOUR-KEY-HERE" TS_STATE_DIR: "/var/lib/tailscale" TS_USERSPACE: "FALSE" # If not using Auto-pause it can be set as true volumes: - ./ts-minecraft/state:/var/lib/tailscale - /dev/net/tun:/dev/net/tun cap_add: - net_admin - sys_module restart: unless-stopped # ports: # Not needed, tailscale is directly linking to the container. # - "25565:25565" minecraft-server: image: itzg/minecraft-server network_mode: container:tailscale stdin_open: true tty: true environment: EULA: "TRUE" # ENABLE_AUTOPAUSE: "TRUE" # AUTOPAUSE_KNOCK_INTERFACE: "tailscale0" # MAX_TICK_TIME: "-1" volumes: - minecraftserver:/data restart: unless-stopped volumes: minecraftserver: ================================================ FILE: examples/valhelsia2/docker-compose.yml ================================================ services: mc: image: itzg/minecraft-server:java8-multiarch volumes: - data:/data - ./modpacks:/modpacks:ro environment: EULA: "true" TYPE: FORGE VERSION: "1.15.2" GENERIC_PACK: /modpacks/Valhelsia_2-2.3.4-SERVER.zip MEMORY: "2G" ports: - "25565:25565" healthcheck: test: NONE volumes: data: {} ================================================ FILE: examples/vanilla-tweaks/docker-compose.yml ================================================ services: vanillatweaks_file: restart: "no" image: itzg/minecraft-server ports: - "25565:25565/tcp" environment: EULA: "TRUE" VERSION: ${MINECRAFT_VERSION:-LATEST} VANILLATWEAKS_FILE: /config/vanillatweaks-datapacks.json REMOVE_OLD_VANILLATWEAKS: "TRUE" volumes: - data:/data - ./vanillatweaks-datapacks.json:/config/vanillatweaks-datapacks.json:ro vanillatweaks_sharecode: # port is set to 25566 to not conflict with vanillatweaks_file example ports: - "25566:25565/tcp" restart: "no" image: itzg/minecraft-server environment: EULA: "TRUE" VERSION: ${MINECRAFT_VERSION:-LATEST} VANILLATWEAKS_SHARECODE: MGr52E REMOVE_OLD_VANILLATWEAKS: "TRUE" vanillatweaks_file_datapacks_and_resourcepacks_and_craftingtweaks: # port is set to 25567 to not conflict with vanillatweaks_file example restart: "no" image: itzg/minecraft-server ports: - "25567:25565/tcp" environment: EULA: "TRUE" VERSION: ${MINECRAFT_VERSION:-LATEST} VANILLATWEAKS_FILE: /config/vanillatweaks-datapacks.json,/config/vanillatweaks-resourcepacks.json,/config/vanillatweaks-craftingtweaks.json REMOVE_OLD_VANILLATWEAKS: "TRUE" volumes: - data:/data - ./vanillatweaks-datapacks.json:/config/vanillatweaks-datapacks.json:ro - ./vanillatweaks-resourcepacks.json:/config/vanillatweaks-resourcepacks.json:ro - ./vanillatweaks-craftingtweaks.json:/config/vanillatweaks-craftingtweaks.json:ro ================================================ FILE: examples/vanilla-tweaks/vanillatweaks-craftingtweaks.json ================================================ { "type": "craftingtweaks", "version": "1.18", "packs": { "quality of life": [ "dropper to dispenser", "double slabs", "back to blocks" ] }, "result": "ok" } ================================================ FILE: examples/vanilla-tweaks/vanillatweaks-datapacks.json ================================================ { "type": "datapacks", "version": "1.18", "packs": { "survival": [ "graves", "multiplayer sleep", "afk display", "armor statues", "unlock all recipes", "fast leaf decay", "coordinates hud" ], "items": ["armored elytra"] }, "result": "ok" } ================================================ FILE: examples/vanilla-tweaks/vanillatweaks-resourcepacks.json ================================================ { "type": "resourcepacks", "version": "1.18", "packs": { "aesthetic": ["CherryPicking", "BlackNetherBricks", "AlternateBlockDestruction"] }, "result": "ok" } ================================================ FILE: files/cf-exclude-include.json ================================================ { "globalExcludes": [ "advancement-plaques", "ambience-music-mod", "ambientsounds", "appleskin", "armor-chroma", "armor-toughness-bar", "audio-extension-for-fancymenu-forge", "auudio-forge", "beehivetooltips", "better-advancements", "better-foliage", "better-modlist-neoforge", "better-placement", "better-sprinting", "better-third-person", "better-tips-nbt-tag", "betterf3", "betterfps", "bettergrassify", "biomeinfo", "block-drops-jei-addon", "blur-forge", "cartography", "chattoggle", "cherished-worlds", "chunk-animator", "clickable-advancements", "colorwheel", "colorwheel-patcher", "compass-coords", "config-menus-forge", "configured", "controllable", "controlling", "craftpresence", "crash-assistant", "cull-less-leaves", "ctm", "custom-main-menu", "dark-mode-everywhere", "defensive-measures", "ding", "distraction-free-recipes", "drippy-loading-screen", "dynamic-surroundings", "dynamic-view", "dynamiclights-reforged", "easiervillagertrading", "effective-forge", "embeddium", "embeddium-extension", "embeddium-extras", "enchantment-descriptions", "enhanced-boss-bars", "enhancedvisuals", "entity-collision-fps-fix", "entity-model-features", "entity-texture-features-fabric", "entityculling", "equipment-compare", "essential-mod", "euphoria-patches", "extreme-sound-muffler", "ezzoom", "fading-night-vision", "falling-leaves-forge", "fancymenu", "farsight", "faster-ladder-climbing", "fastquit", "fastquit-forge", "flerovium", "foamfix-optimization-mod", "forgeskyboxes", "fps-reducer", "free-cam", "ftb-backups-2", "fullscreen-windowed-borderless-for-minecraft", "hwyla", "iceberg", "ignitioncoil", "illager-raid-music", "inmisaddon", "iris-flywheel-compat", "irisshaders", "item-borders", "item-highlighter", "item-obliterator", "itemphysic-lite", "itemzoom", "just-enough-harvestcraft", "just-enough-mekanism-multiblocks", "just-enough-resources-jer", "just-zoom", "legendary-tooltips", "lighty", "loot-capacitor-tooltips", "loot-journal", "lootbeams", "magnesium-extras", "make-bubbles-pop", "mekalus-oculus-fork-with-fixed-mekanism-mekasuit", "menumobs", "minecraft-rich-presence", "mining-speed-tooltips", "model-gap-fix", "more-overlays", "mouse-tweaks", "neat", "nekos-enchanted-books", "neoculus", "no-nv-flash", "no-recipe-book", "not-enough-animations", "oculus", "ok-zoomer", "oldjavawarning", "overloaded-armor-bar", "packmenu", "packmodemenu", "particle-effects", "particle-effects-reforged", "particle-rain", "particular", "particular-reforged", "reauth", "rebind-narrator", "reblured", "reeses-sodium-options", "reforgium", "resource-reloader", "rubidium", "rubidium-extra", "ryoamiclights", "schematica", "seamless-loading-screen", "seamless-loading-screen-forge", "searchables", "seasonhud", "shulkerboxviewer", "skin-layers-3d", "smart-hud", "smithing-template-viewer", "smooth-font", "smoothwater", "sodium", "sodium-extra", "sodium-options-api", "sodium-rubidium-occlusion-culling-fix", "sound", "sound-filters", "sound-physics-remastered", "sound-reloader", "status-effect-bars-reforged", "stellar-sky", "swingthroughgrass", "textrues-embeddium-options", "textrues-rubidium-options", "thaumic-jei", "tips", "toast-control", "torohealth-damage-indicators", "true-darkness", "ungrab-mouse-mod", "vanillafix", "visuality", "waila-harvestability", "waila-stages", "wakes-reforged", "wawla", "welcome-screen", "xaeroplus", "yungs-menu-tweaks", "zume" ], "modpacks": { "all-of-fabric-6": { "forceIncludes": ["revelationary"] }, "beyond-depth": { "forceIncludes": ["particular-reforged"] }, "create-arcane-engineering": { "forceIncludes": ["just-enough-resources-jer"] }, "ftb-stoneblock-4": { "forceIncludes": [ "particular-reforged", "ctm" ] }, "mc-eternal-2": { "forceIncludes": [ "particular-reforged", "mob-player-animator" ] }, "reclamation-reclaim-the-world": { "forceIncludes": [ "more-overlays-updated" ] }, "skyfactory-5": { "forceIncludes": [ "colored-torches", "dye-mob-dye", "openstairs" ] }, "valhelsia-5": { "excludes": ["modernfix"] } } } ================================================ FILE: files/knockd-config.cfg ================================================ [options] logfile = /dev/null [unpauseMCServer-server] sequence = 25565 seq_timeout = 1 command = /image/scripts/auto/resume.sh %IP% tcpflags = syn [unpauseMCServer-rcon] sequence = 25575 seq_timeout = 1 command = /image/scripts/auto/resume.sh %IP% tcpflags = syn [unpauseMCServer-bedrock] sequence = 19132:udp command = /image/scripts/auto/resume.sh %IP% seq_cooldown = 60 ================================================ FILE: files/modrinth-exclude-include.json ================================================ { "globalExcludes": [ "3dskinlayers", "ae2-emi-crafting", "AmbientSounds", "amecs", "Animation_Overhaul", "appleskin", "auudio", "axolotlbuckets", "BadOptimizations", "BetterAdvancements", "betterbeds", "BetterF3", "BetterGrassify", "BetterThirdPerson", "BHMenu", "blur", "Boat-Item-View", "bobby", "cat_jam", "chat_heads", "chatanimation", "cherishedworlds", "citresewn", "clickadv", "cobblemon-ui-tweaks", "compass-coords", "colorwheel", "connectedness", "connector", "continuity", "controlling", "craftpresence", "CrashAssistant", "Cull Less Leaves", "cwb", "DisableCustomWorldsAdvice", "distraction_free_recipes", "drippyloadingscreen", "eating-animation", "emi", "emi_loot", "emi_trade", "emiffect", "emitrades", "entity_model_features", "entity_texture_features", "entityculling", "euphoriapatcher", "fallingleaves", "fancymenu", "fast-ip-ping", "fastquit", "FauxCustomEntityData", "feytweaks", "figura", "ForgeConfigScreens", "GeckoLibIrisCompat", "gpumemleakfix", "Highlighter", "ImmediatelyFast", "indium", "inventory-profiles-next", "iris", "iris-flywheel", "ItemBorders", "ItemLocks", "justzoom", "language-reload", "lazy-language-loader", "LegendaryTooltips", "loadmyresources", "lootbeams", "MindfulDarkness", "miningspeedtooltips", "MouseTweaks", "nicer-skies", "notenoughanimations", "oculus", "ok_zoomer", "OverflowingBars", "particlerain", "PickUpNotifier", "PresenceFootsteps", "Prism", "reeses_sodium_options", "reforgium", "ResourcePackOverrides", "roughly-enough-items", "ryoamiclights", "screenshot_viewer", "Searchables", "seasonhud", "ShoulderSurfing", "skinlayers3d", "sodium", "sorted_enchantments", "tooltipfix", "visuality", "VR-Combat", "WailaStages", "welcomescreen", "Xaeros_Minimap", "XaerosWorldMap", "YeetusExperimentus", "yungsmenutweaks", "Zoomify", "zume" ], "globalForceIncludes": [], "modpacks": { "cobbleverse": { "excludes": [ "cloth-config" ] } } } ================================================ FILE: files/nanolimbo-settings-patch.json ================================================ { "file": "/data/settings.yml", "ops": [ { "$set": { "path": "$.bind.port", "value": "${SERVER_PORT}", "value-type": "int" } } ] } ================================================ FILE: files/property-definitions.json ================================================ { "accepts-transfers": {"env": "ACCEPTS_TRANSFERS"}, "allow-flight": {"env": "ALLOW_FLIGHT"}, "allow-nether": {"env": "ALLOW_NETHER"}, "announce-player-achievements": {"env": "ANNOUNCE_PLAYER_ACHIEVEMENTS"}, "broadcast-console-to-ops": {"env": "BROADCAST_CONSOLE_TO_OPS"}, "broadcast-rcon-to-ops": {"env": "BROADCAST_RCON_TO_OPS"}, "bug-report-link": {"env": "BUG_REPORT_LINK"}, "difficulty": {"env": "DIFFICULTY"}, "enable-command-block": {"env": "ENABLE_COMMAND_BLOCK"}, "enable-jmx-monitoring": {"env": "ENABLE_JMX"}, "enable-query": {"env": "ENABLE_QUERY"}, "enable-rcon": {"env": "ENABLE_RCON"}, "enable-status": {"env": "ENABLE_STATUS"}, "enforce-secure-profile": {"env": "ENFORCE_SECURE_PROFILE"}, "enforce-whitelist": {"env": "ENFORCE_WHITELIST"}, "entity-broadcast-range-percentage": {"env": "ENTITY_BROADCAST_RANGE_PERCENTAGE"}, "force-gamemode": {"env": "FORCE_GAMEMODE"}, "function-permission-level": {"env": "FUNCTION_PERMISSION_LEVEL"}, "gamemode": {"env": "MODE"}, "generate-structures": {"env": "GENERATE_STRUCTURES"}, "generator-settings": {"env": "GENERATOR_SETTINGS"}, "hardcore": {"env": "HARDCORE"}, "hide-online-players": {"env": "HIDE_ONLINE_PLAYERS"}, "initial-disabled-packs": {"env": "INITIAL_DISABLED_PACKS"}, "initial-enabled-packs": {"env": "INITIAL_ENABLED_PACKS"}, "level-name": {"env": "LEVEL"}, "level-seed": {"env": "SEED"}, "level-type": {"env": "LEVEL_TYPE"}, "log-ips": {"env": "LOG_IPS"}, "management-server-allowed-origins": {"env": "MANAGEMENT_SERVER_ALLOWED_ORIGINS"}, "management-server-enabled": {"env": "MANAGEMENT_SERVER_ENABLED"}, "management-server-host": {"env": "MANAGEMENT_SERVER_HOST"}, "management-server-port": {"env": "MANAGEMENT_SERVER_PORT"}, "management-server-secret": {"env": "MANAGEMENT_SERVER_SECRET"}, "management-server-tls-enabled": {"env": "MANAGEMENT_SERVER_TLS_ENABLED"}, "management-server-tls-keystore": {"env": "MANAGEMENT_SERVER_TLS_KEYSTORE"}, "management-server-tls-keystore-password": {"env": "MANAGEMENT_SERVER_TLS_KEYSTORE_PASSWORD"}, "max-build-height": {"env": "MAX_BUILD_HEIGHT"}, "max-chained-neighbor-updates": {"env": "MAX_CHAINED_NEIGHBOR_UPDATES"}, "max-players": {"env": "MAX_PLAYERS"}, "max-tick-time": {"env": "MAX_TICK_TIME"}, "max-world-size": {"env": "MAX_WORLD_SIZE"}, "motd": {"env": "MOTD"}, "network-compression-threshold": {"env": "NETWORK_COMPRESSION_THRESHOLD"}, "online-mode": {"env": "ONLINE_MODE"}, "op-permission-level": {"env": "OP_PERMISSION_LEVEL"}, "pause-when-empty-seconds": {"env": "PAUSE_WHEN_EMPTY_SECONDS"}, "player-idle-timeout": {"env": "PLAYER_IDLE_TIMEOUT"}, "prevent-proxy-connections": {"env": "PREVENT_PROXY_CONNECTIONS"}, "previews-chat": {"env": "PREVIEWS_CHAT"}, "pvp": {"env": "PVP"}, "query.port": {"env": "QUERY_PORT"}, "rate-limit": {"env": "RATE_LIMIT"}, "rcon.password": {"env": "RCON_PASSWORD"}, "rcon.port": {"env": "RCON_PORT"}, "region-file-compression": {"env": "REGION_FILE_COMPRESSION"}, "require-resource-pack": {"env": "RESOURCE_PACK_ENFORCE"}, "resource-pack-id": {"env": "RESOURCE_PACK_ID"}, "resource-pack-prompt": {"env": "RESOURCE_PACK_PROMPT"}, "resource-pack-sha1": {"env": "RESOURCE_PACK_SHA1"}, "resource-pack": {"env": "RESOURCE_PACK"}, "server-ip": {"env": "SERVER_IP"}, "server-name": {"env": "SERVER_NAME"}, "server-port": {"env": "SERVER_PORT"}, "simulation-distance": {"env": "SIMULATION_DISTANCE"}, "snooper-enabled": {"env": "SNOOPER_ENABLED"}, "spawn-animals": {"env": "SPAWN_ANIMALS"}, "spawn-monsters": {"env": "SPAWN_MONSTERS"}, "spawn-npcs": {"env": "SPAWN_NPCS"}, "spawn-protection": {"env": "SPAWN_PROTECTION"}, "status-heartbeat-interval": {"env": "STATUS_HEARTBEAT_INTERVAL"}, "sync-chunk-writes": {"env": "SYNC_CHUNK_WRITES"}, "use-native-transport": {"env": "USE_NATIVE_TRANSPORT"}, "view-distance": {"env": "VIEW_DISTANCE"}, "white-list": {"env": "WHITELIST_PROP"} } ================================================ FILE: files/server.properties ================================================ op-permission-level=4 allow-nether=true level-name=world enable-query=false allow-flight=false announce-player-achievements=true server-port=25565 rcon.port=25575 query.port=25565 level-type=DEFAULT enable-rcon=false force-gamemode=false level-seed= server-ip= max-tick-time=60000 max-build-height=256 spawn-npcs=true white-list=false spawn-animals=true hardcore=false snooper-enabled=true texture-pack= online-mode=true resource-pack= resource-pack-sha1= pvp=true difficulty=1 enable-command-block=true player-idle-timeout=0 gamemode=0 max-players=20 spawn-monsters=true generate-structures=true view-distance=10 spawn-protection=16 motd=A Minecraft Server powered by Docker generator-settings= rcon.password= max-world-size=29999984 broadcast-console-to-ops=true broadcast-rcon-to-ops=true enable-jmx-monitoring=false sync-chunk-writes=true enable-status=true entity-broadcast-range-percentage=100 function-permission-level=2 network-compression-threshold=256 op-permission-level=4 prevent-proxy-connections=false use-native-transport=true enforce-whitelist=false ================================================ FILE: images.json ================================================ [ { "tag": "latest", "notes": "Always tracks the most recent commit on the main branch", "java": "25", "distribution": "ubuntu", "jvm": "hotspot", "architectures": ["amd64", "arm64"], "lts": true }, { "tag": "stable", "notes": "Always tracks the most recent release", "java": "25", "distribution": "ubuntu", "jvm": "hotspot", "architectures": ["amd64", "arm64"], "lts": true }, { "tag": "java25", "java": "25", "distribution": "ubuntu", "jvm": "hotspot", "architectures": ["amd64", "arm64"] }, { "tag": "java25-alpine", "java": "25", "distribution": "alpine", "jvm": "hotspot", "architectures": ["amd64", "arm64"] }, { "tag": "java25-graalvm", "java": "25", "distribution": "oracle", "jvm": "graalvm", "architectures": ["amd64", "arm64"], "deprecated": true }, { "tag": "java24", "java": "24", "distribution": "ubuntu", "jvm": "hotspot", "architectures": ["amd64", "arm64"], "deprecated": true }, { "tag": "java24-graalvm", "java": "24", "distribution": "oracle", "jvm": "graalvm", "architectures": ["amd64", "arm64"], "deprecated": true }, { "tag": "java21", "java": "21", "distribution": "ubuntu", "jvm": "hotspot", "architectures": ["amd64", "arm64"], "lts": true }, { "tag": "java21-alpine", "java": "21", "distribution": "alpine", "jvm": "hotspot", "architectures": ["amd64", "arm64"] }, { "tag": "java21-graalvm", "java": "21", "distribution": "oracle", "jvm": "graalvm", "architectures": ["amd64", "arm64"], "deprecated": true }, { "tag": "java21-jdk", "java": "21", "distribution": "ubuntu", "jvm": "hotspot", "architectures": ["amd64", "arm64"], "jdk": true }, { "tag": "java17", "java": "17", "distribution": "ubuntu", "jvm": "hotspot", "architectures": ["amd64", "arm64"], "lts": true }, { "tag": "java17-alpine", "java": "17", "distribution": "alpine", "jvm": "hotspot", "architectures": ["amd64", "arm64"], "deprecated": true }, { "tag": "java17-graalvm", "java": "17", "distribution": "oracle", "jvm": "graalvm", "architectures": ["amd64", "arm64"] }, { "tag": "java16", "java": "16", "distribution": "ubuntu", "jvm": "hotspot", "architectures": ["amd64", "arm64", "armv7"] }, { "tag": "java11", "java": "11", "distribution": "ubuntu", "jvm": "hotspot", "architectures": ["amd64", "arm64", "armv7"], "lts": true }, { "tag": "java8", "java": "8", "distribution": "ubuntu", "jvm": "hotspot", "architectures": ["amd64", "arm64", "armv7"], "lts": true }, {"tag": "adopt13", "deprecated": true}, {"tag": "adopt14", "deprecated": true}, {"tag": "adopt15", "deprecated": true}, {"tag": "openj9-nightly", "deprecated": true}, {"tag": "multiarch-latest", "deprecated": true}, {"tag": "java16-openj9", "deprecated": true}, {"tag": "java17-graalvm-ce", "deprecated": true}, {"tag": "java17-openj9", "deprecated": true}, {"tag": "java19", "deprecated": true}, {"tag": "java20-graalvm", "deprecated": true}, {"tag": "java20", "deprecated": true}, {"tag": "java20-alpine", "deprecated": true}, {"tag": "java23", "deprecated": true}, {"tag": "java8-multiarch", "deprecated": true}, {"tag": "java8-alpine", "deprecated": true}, {"tag": "java8-jdk", "deprecated": true}, {"tag": "java8-openj9", "deprecated": true}, {"tag": "java8-graalvm-ce", "deprecated": true} ] ================================================ FILE: kustomize/README.md ================================================ This directory provides a base to use with [kubectl kustomize](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/). ## Example overlay content ### kustomization.yml ```yaml nameSuffix: "-forge" commonLabels: server: forge bases: - https://github.com/itzg/docker-minecraft-server.git/kustomize/base configMapGenerator: - name: mc envs: - mc.env patches: # Example of using a patch to set external service name for mc-router to pick up - path: set-external-servername.yml ``` ### mc.env ``` EULA=true TYPE=FORGE ``` ### ```yaml apiVersion: v1 kind: Service metadata: name: mc annotations: mc-router.itzg.me/externalServerName: forge.example.com ``` ================================================ FILE: kustomize/base/kustomization.yml ================================================ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - statefulset.yml - service.yml ================================================ FILE: kustomize/base/service.yml ================================================ apiVersion: v1 kind: Service metadata: labels: service: mc annotations: {} # Such as # mc-router.itzg.me/externalServerName: $(EXTERNAL_SERVICE_NAME) name: mc spec: ports: - name: minecraft port: 25565 targetPort: 25565 type: NodePort selector: server: mc ================================================ FILE: kustomize/base/statefulset.yml ================================================ apiVersion: apps/v1 kind: StatefulSet metadata: name: mc spec: replicas: 1 serviceName: mc selector: matchLabels: server: mc template: metadata: labels: server: mc spec: containers: - name: mc envFrom: - configMapRef: name: mc optional: true env: [] image: itzg/minecraft-server stdin: true tty: true volumeMounts: - mountPath: /data name: data resources: requests: cpu: 150m livenessProbe: exec: command: ["mc-health"] initialDelaySeconds: 120 periodSeconds: 60 readinessProbe: exec: command: ["mc-health"] initialDelaySeconds: 20 periodSeconds: 10 failureThreshold: 12 volumeClaimTemplates: - metadata: name: data spec: accessModes: - ReadWriteOnce resources: requests: storage: 500Mi ================================================ FILE: notes/.gitignore ================================================ /*.bkp ================================================ FILE: notes/Minecraft server containment.xml ================================================ 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 ================================================ FILE: notes/rbac-processing.mmd ================================================ [Scia Reto](https://sciareto.org) mind map > __version__=`1.1`,showJumps=`true` --- # RBAC processing ## inputs ### whitelist #### command or space limited ##### uuid ##### username ### ops #### username #### uuid ### whitelist file #### url? ##### yes ###### download ##### no ###### copy ### override whitelist? #### yes ##### replace all with given input list #### no ##### append only ## format ### version \< 1\.7\.6? #### yes ##### text file listing usernames ###### white\-list\.txt ###### ops\.txt #### no > leftSide=`true` ##### json file ###### array of objects ####### name ######## can be any string, even an empty one ####### uuid > leftSide=`true` ######## username to UUID API - LINK
https://wiki.vg/Mojang_API#Username_to_UUID
######## needs to be "dashed" UUID syntax > leftSide=`true` ####### ops? ######## yes ######### also includes ########## level ########### integer, usually a 4 ########## bypassesPlayerLimit ########### boolean ================================================ FILE: renovate.json5 ================================================ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "enabledManagers": [ "custom.regex" ], "customManagers": [ { "customType": "regex", "managerFilePatterns": ["/^Dockerfile$/"], "matchStrings": [ "# renovate: datasource=(?[a-z-]+?)(?: packageName=(?.+?))?(?: versioning=(?[a-z-]+))?\\s(?:ENV|ARG) .+?_VERSION=(?.+?)\\s" ] } ], "labels": ["dependencies"] } ================================================ FILE: scripts/auto/autopause-daemon.sh ================================================ #!/bin/bash . "$(dirname "$0")/autopause-fcns.sh" # shellcheck source=../start-utils . "$(dirname "$0")/../start-utils" if isTrue "${DEBUG_AUTOPAUSE}"; then set -x fi autopause_error_loop() { logAutopause "Available interfaces within the docker container:" logAutopause " $(mc-image-helper network-interfaces)" logAutopause "Please set the environment variable AUTOPAUSE_KNOCK_INTERFACE to the interface that handles incoming connections." logAutopause "If unsure which interface to choose, run the ifconfig command in the container." logAutopause "Autopause failed to initialize. This log entry will be printed every 30 minutes." while : do sleep 1800 logAutopause "Autopause failed to initialize." done } # wait for java process to be started while : do if java_process_exists ; then break fi sleep 0.1 done # check for interface existence if [[ -z "$AUTOPAUSE_KNOCK_INTERFACE" ]] ; then logAutopause "AUTOPAUSE_KNOCK_INTERFACE variable must not be empty!" autopause_error_loop fi if ! mc-image-helper network-interfaces --check="$AUTOPAUSE_KNOCK_INTERFACE" ; then logAutopause "Selected interface \"$AUTOPAUSE_KNOCK_INTERFACE\" does not exist!" autopause_error_loop fi knockdArgs=(-c /tmp/knockd-config.cfg -d -i "$AUTOPAUSE_KNOCK_INTERFACE") if isTrue "${DEBUG_AUTOPAUSE}"; then knockdArgs+=(-D) fi /usr/local/sbin/knockd "${knockdArgs[@]}" if [ $? -ne 0 ] ; then logAutopause "Failed to start knockd daemon." logAutopause "Probable cause: Unable to attach to interface \"$AUTOPAUSE_KNOCK_INTERFACE\"." autopause_error_loop fi STATE=INIT while : do isTrue "${DEBUG_AUTOPAUSE}" && log "DEBUG: autopause state = $STATE" case X$STATE in XINIT) # Server startup if mc_server_listening ; then TIME_THRESH=$(($(current_uptime)+$AUTOPAUSE_TIMEOUT_INIT)) if [ -e /data/.skip-pause ] ; then logAutopause "'/data/.skip-pause' file is present - skipping pausing" else logAutopause "MC Server listening for connections - pausing in $AUTOPAUSE_TIMEOUT_INIT seconds" fi STATE=K fi ;; XK) # Knocked if java_clients_connected ; then logAutopause "Client connected - waiting for disconnect" STATE=E elif [ -e /data/.skip-pause ] ; then logAutopause "'/data/.skip-pause' file is present - skipping pausing" STATE=E else if [[ $(current_uptime) -ge $TIME_THRESH ]] ; then logAutopause "No client connected since startup / knocked - pausing" "$(dirname "$0")/pause.sh" STATE=S fi fi ;; XE) # Established if ! java_clients_connected ; then TIME_THRESH=$(($(current_uptime)+$AUTOPAUSE_TIMEOUT_EST)) logAutopause "All clients disconnected - pausing in $AUTOPAUSE_TIMEOUT_EST seconds" STATE=I fi ;; XI) # Idle if java_clients_connected ; then logAutopause "Client reconnected - waiting for disconnect" STATE=E elif [ -e /data/.skip-pause ] ; then TIME_THRESH=$(($(current_uptime)+$AUTOPAUSE_TIMEOUT_EST)) logAutopause "'/data/.skip-pause' file is present - skipping pausing" STATE=E else if [[ $(current_uptime) -ge $TIME_THRESH ]] ; then logAutopause "No client reconnected - pausing" "$(dirname "$0")/pause.sh" STATE=S fi fi ;; XS) # Stopped if rcon_client_exists ; then "$(dirname "$0")/resume.sh" fi if java_running ; then if java_clients_connected ; then logAutopause "Client connected - waiting for disconnect" STATE=E else TIME_THRESH=$(($(current_uptime)+$AUTOPAUSE_TIMEOUT_KN)) logAutopause "Server was knocked - waiting for clients or timeout" STATE=K fi fi ;; *) logAutopause "Error: invalid state: $STATE" ;; esac if [[ "$STATE" == "S" ]] ; then # before rcon times out sleep 5 else sleep "$AUTOPAUSE_PERIOD" fi done ================================================ FILE: scripts/auto/autopause-fcns.sh ================================================ #!/bin/bash # shellcheck source=../scripts/start-utils . "$(dirname "$0")/../start-utils" current_uptime() { awk '{print $1}' /proc/uptime | cut -d . -f 1 } java_running() { [[ $( ps -ax -o stat,comm | grep 'java' | awk '{ print $1 }') =~ ^S.*$ ]] } java_process_exists() { [[ -n "$(ps -ax -o comm | grep 'java')" ]] } rcon_client_exists() { [[ -n "$(ps -ax -o comm | grep 'rcon-cli')" ]] } use_proxy() { if isTrue "$USES_PROXY_PROTOCOL"; then echo "--use-proxy" fi } use_server_list_ping() { if [[ "${VERSION^^}" == "LATEST" || "${VERSION^^}" == "SNAPSHOT" ]]; then # Don't use server-list ping for unknown version return 1 fi if versionLessThan 1.7; then echo "--use-server-list-ping" fi } mc_server_listening() { mc-monitor status $(use_proxy) --host "${SERVER_HOST:-localhost}" --port "$SERVER_PORT" $(use_server_list_ping) --timeout 10s >&/dev/null } java_clients_connections() { local connections if java_running; then if ! connections=$(mc-monitor status \ --host "${SERVER_HOST:-localhost}" \ --port "$SERVER_PORT" \ --retry-limit "${AUTOPAUSE_STATUS_RETRY_LIMIT:-10}" --retry-interval "${AUTOPAUSE_STATUS_RETRY_INTERVAL:-2s}" \ $(use_proxy) $(use_server_list_ping) \ --show-player-count); then # consider it a non-zero player count if the ping fails # otherwise a laggy server with players connected could get paused connections=1 fi else connections=0 fi echo $connections } java_clients_connected() { (( $(java_clients_connections) > 0 )) } ================================================ FILE: scripts/auto/autostop-daemon.sh ================================================ #!/bin/bash # needed for the clients connected function residing in autopause . "$(dirname "$0")/autopause-fcns.sh" # shellcheck source=../../scripts/start-utils . "$(dirname "$0")/../start-utils" if isTrue "${DEBUG_AUTOSTOP}"; then set -x fi # wait for java process to be started while : do if java_process_exists ; then break fi sleep 0.1 done STATE=INIT while : do isTrue "${DEBUG_AUTOSTOP}" && log "DEBUG: autostop state = $STATE" case X$STATE in XINIT) # Server startup if mc_server_listening ; then TIME_THRESH=$(($(current_uptime)+AUTOSTOP_TIMEOUT_INIT)) if [ -e /data/.skip-stop ] ; then logAutostop "'/data/.skip-stop' file is present - skipping stopping" else logAutostop "MC Server listening for connections - stopping in $AUTOSTOP_TIMEOUT_INIT seconds" fi STATE=II fi ;; XII) # Initial idle if java_clients_connected ; then logAutostop "Client connected - waiting for disconnect" STATE=E elif [ -e /data/.skip-stop ] ; then logAutostop "'/data/.skip-stop' file is present - skipping stopping" STATE=E else if [[ $(current_uptime) -ge $TIME_THRESH ]] ; then logAutostop "No client connected since startup - stopping server" "$(dirname "$0")"/stop.sh exit 0 fi fi ;; XE) # Established if ! java_clients_connected ; then TIME_THRESH=$(($(current_uptime)+$AUTOSTOP_TIMEOUT_EST)) logAutostop "All clients disconnected - stopping in $AUTOSTOP_TIMEOUT_EST seconds" STATE=I fi ;; XI) # Idle if java_clients_connected ; then logAutostop "Client reconnected - waiting for disconnect" STATE=E elif [ -e /data/.skip-stop ] ; then TIME_THRESH=$(($(current_uptime)+$AUTOSTOP_TIMEOUT_EST)) logAutostop "'/data/.skip-stop' file is present - skipping stopping" STATE=E else if [[ $(current_uptime) -ge $TIME_THRESH ]] ; then logAutostop "No client reconnected - stopping" "$(dirname "$0")"/stop.sh exit 0 fi fi ;; *) logAutostop "Error: invalid state: $STATE" ;; esac sleep $AUTOSTOP_PERIOD done ================================================ FILE: scripts/auto/pause.sh ================================================ #!/bin/bash . "$(dirname "$0")/../start-utils" if isTrue "${DEBUG_AUTOPAUSE}"; then set -x fi if [[ $( ps -ax -o stat,comm | grep 'java' | awk '{ print $1 }') =~ ^S.*$ ]] ; then # save world rcon-cli save-all >/dev/null # wait until mc-monitor is no longer connected to the server while : do if [[ -z "$(netstat -nt | grep "127.0.0.1:$SERVER_PORT" | grep 'ESTABLISHED')" ]]; then break fi sleep 0.1 done # finally pause the process logAutopauseAction "Pausing Java process" pkill -STOP java # create .paused file in data directory touch /data/.paused fi ================================================ FILE: scripts/auto/rcon-cmds-daemon.sh ================================================ #!/bin/bash : "${RCON_CMDS_STARTUP:=}" : "${RCON_CMDS_ON_CONNECT:=}" : "${RCON_CMDS_ON_DISCONNECT:=}" : "${RCON_CMDS_FIRST_CONNECT:=}" : "${RCON_CMDS_LAST_DISCONNECT:=}" : "${RCON_CMDS_PERIOD:=10}" # needed for the clients connected function residing in autopause # shellcheck source=../auto/autopause-fcns.sh . /image/scripts/auto/autopause-fcns.sh # shellcheck source=start-utils . /image/scripts/start-utils run_command(){ rcon_cmd="$1" logRcon "running - $rcon_cmd" output=$(rcon-cli "$rcon_cmd") logRcon "$output" } # wait for java process to be started while : do if java_process_exists ; then break fi sleep 0.1 done CLIENTCONNECTIONS=0 STATE=INIT while : do case X$STATE in XINIT) # Server startup if mc_server_listening ; then logRcon "MCServer is listening, running startup" if [[ "$RCON_CMDS_STARTUP" ]]; then while read -r cmd; do run_command "$cmd" done <<< "$(echo -e "$RCON_CMDS_STARTUP")" fi if [[ -z "$RCON_CMDS_ON_CONNECT" ]] && [[ -z "$RCON_CMDS_ON_DISCONNECT" ]] && [[ -z "$RCON_CMDS_FIRST_CONNECT" ]] && [[ -z "$RCON_CMDS_LAST_DISCONNECT" ]] then logRcon "No addition rcon commands are given, stopping rcon cmd service" exit 0 fi STATE=II fi ;; XII) CURR_CLIENTCONNECTIONS=$(java_clients_connections) # First client connection # Setting priority run order: on first client connection is usually to STOP maintence, aka DO THIS FIRST if (( CURR_CLIENTCONNECTIONS > 0 )) && (( CLIENTCONNECTIONS == 0 )) && [[ "$RCON_CMDS_FIRST_CONNECT" ]]; then logRcon "First Clients has Connected, running first connect cmds" while read -r cmd; do run_command "$cmd" done <<< "$(echo -e "$RCON_CMDS_FIRST_CONNECT")" fi # When a client joins if (( CURR_CLIENTCONNECTIONS > CLIENTCONNECTIONS )) && [[ "$RCON_CMDS_ON_CONNECT" ]]; then logRcon "Clients have Connected, running connect cmds" while read -r cmd; do run_command "$cmd" done <<< "$(echo -e "$RCON_CMDS_ON_CONNECT")" # When a client leaves elif (( CURR_CLIENTCONNECTIONS < CLIENTCONNECTIONS )) && [[ "$RCON_CMDS_ON_DISCONNECT" ]]; then logRcon "Clients have Disconnected, running disconnect cmds" while read -r cmd; do run_command "$cmd" done <<< "$(echo -e "$RCON_CMDS_ON_DISCONNECT")" fi # Last client connection # Setting priority run order: on last client connection is usually to START maintence, aka DO THIS LAST if (( CURR_CLIENTCONNECTIONS == 0 )) && (( CLIENTCONNECTIONS > 0 )) && [[ "$RCON_CMDS_LAST_DISCONNECT" ]]; then logRcon "ALL Clients have Disconnected, running last disconnect cmds" while read -r cmd; do run_command "$cmd" done <<< "$(echo -e "$RCON_CMDS_LAST_DISCONNECT")" fi CLIENTCONNECTIONS=$CURR_CLIENTCONNECTIONS ;; *) logRcon "Error: invalid state: $STATE" ;; esac sleep "$RCON_CMDS_PERIOD" done ================================================ FILE: scripts/auto/resume.sh ================================================ #!/bin/bash . "$(dirname "$0")/../start-utils" if isTrue "${DEBUG_AUTOPAUSE}"; then set -x fi if [[ $( ps -ax -o stat,comm | grep 'java' | awk '{ print $1 }') =~ ^T.*$ ]] ; then pkill -CONT java # remove .paused file from data directory rm -f /data/.paused fi ================================================ FILE: scripts/auto/stop.sh ================================================ #!/bin/bash . "$(dirname "$0")/../start-utils" if isTrue "${DEBUG_AUTOSTOP}"; then set -x fi logAutostopAction "Stopping Java process" pkill -f --signal SIGTERM mc-server-runner ================================================ FILE: scripts/shims/mc-health ================================================ #!/bin/bash . "/image/scripts/start-utils" if [ -f /data/.mc-health.env ]; then . /data/.mc-health.env fi if isTrue "${DISABLE_HEALTHCHECK}"; then echo "Healthcheck disabled" exit 0 elif isTrue "${ENABLE_AUTOPAUSE}" && [[ "$( ps -ax -o stat,comm | grep 'java' | awk '{ print $1 }')" =~ ^T.*$ ]]; then echo "Java process suspended by Autopause function" exit 0 else mc-monitor status "${MC_HEALTH_EXTRA_ARGS[@]}" --host "${SERVER_HOST:-localhost}" --port "${SERVER_PORT:-25565}" exit $? fi ================================================ FILE: scripts/shims/mc-send-to-console ================================================ #!/bin/bash . "/image/scripts/start-utils" : "${CONSOLE_IN_NAMED_PIPE:=/tmp/minecraft-console-in}" if isFalse "${CREATE_CONSOLE_IN_PIPE:-false}"; then error "Console pipe needs to be enabled by setting CREATE_CONSOLE_IN_PIPE to true" fi if [ $# = 0 ]; then error "Pass console commands as arguments" exit 1 fi if [ ! -p "${CONSOLE_IN_NAMED_PIPE}" ]; then error "Named pipe ${CONSOLE_IN_NAMED_PIPE} is missing" exit 1 fi if [[ "$(id -u)" = 0 ]] && [[ $UID != 0 ]]; then error "Exec needs to be run with user ID $UID" exit 2 else echo "$@" >"${CONSOLE_IN_NAMED_PIPE}" fi ================================================ FILE: scripts/shims/mcstatus ================================================ #!/bin/bash warning "mcstatus is deprecated; calling mc-monitor instead" ##### mcstatus shim for mc-monitor # handles translating calls to # mcstatus (host:port) (command) # where the actual command is ignore, but is typically ping or status addr="$1" IFS=':' read -a parts <<< "${addr}" args=(--host ${parts[0]}) if [[ ${#parts[*]} -gt 1 ]]; then args+=(--port ${parts[1]}) fi exec mc-monitor ${args[@]} ================================================ FILE: scripts/start ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" # The Dockerfile ENVs take precedence here, but defaulting for testing consistency : "${UID:=1000}" : "${GID:=1000}" : "${SKIP_CHOWN_DATA:=false}" umask "${UMASK:=0002}" # Remove from previous run and do this as elevated user since file used to be created before demoting rm -f "$HOME/.rcon-cli.env" if ! isTrue "${SKIP_SUDO:-false}" && [ "$(id -u)" = 0 ]; then runAsUser=minecraft runAsGroup=minecraft if [[ -v UID ]]; then if [[ $UID != 0 ]]; then if [[ $UID != $(id -u minecraft) ]]; then log "Changing uid of minecraft to $UID" usermod -u $UID minecraft fi else runAsUser=root fi fi if [[ -v GID ]]; then if [[ $GID != 0 ]]; then if [[ $GID != $(id -g minecraft) ]]; then log "Changing gid of minecraft to $GID" groupmod -o -g "$GID" minecraft fi else runAsGroup=root fi fi if isFalse "${SKIP_CHOWN_DATA}" && [[ $(stat -c "%u" /data) != "$UID" ]]; then log "Changing ownership of /data to $UID ..." chown -R ${runAsUser}:${runAsGroup} /data fi if [[ ${SKIP_NSSWITCH_CONF^^} != TRUE ]]; then echo 'hosts: files dns' > /etc/nsswitch.conf fi exec gosu ${runAsUser}:${runAsGroup} "$(dirname "$0")/start-configuration" "$@" else exec "$(dirname "$0")/start-configuration" "$@" fi ================================================ FILE: scripts/start-autopause ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" : "${SERVER_PORT:=25565}" : "${ENABLE_AUTOPAUSE:=false}" : "${AUTOPAUSE_TIMEOUT_EST:=3600}" : "${AUTOPAUSE_TIMEOUT_KN:=120}" : "${AUTOPAUSE_TIMEOUT_INIT:=600}" : "${AUTOPAUSE_PERIOD:=10}" : "${AUTOPAUSE_KNOCK_INTERFACE:=eth0}" : "${DEBUG_AUTOPAUSE:=false}" export SERVER_PORT export ENABLE_AUTOPAUSE export AUTOPAUSE_TIMEOUT_EST export AUTOPAUSE_TIMEOUT_KN export AUTOPAUSE_TIMEOUT_INIT export AUTOPAUSE_PERIOD export AUTOPAUSE_KNOCK_INTERFACE export DEBUG_AUTOPAUSE log "Autopause functionality enabled" isDebugging && set -x cp /image/knockd-config.cfg /tmp/knockd-config.cfg function updatePort() { regseq="^\s*sequence\s*=\s*$1\s*$" linenum=$(grep -nm${2} sequence /tmp/knockd-config.cfg | cut -d : -f 1 | tail -n1) if ! [[ $(awk "NR==$linenum" /tmp/knockd-config.cfg) =~ $regseq ]]; then sed -i "${linenum}s/sequence.*/sequence = $1/" /tmp/knockd-config.cfg log "Updated $3 port in knockd config" fi } # update server port to listen to updatePort $SERVER_PORT 1 "server" # update rcon port to listen to updatePort $RCON_PORT 2 "rcon" isNumericElseSetToDefault "AUTOPAUSE_PERIOD" 10 checkIfNotZeroElseSetToDefault "AUTOPAUSE_PERIOD" 10 isNumericElseSetToDefault "AUTOPAUSE_TIMEOUT_KN" 120 isNumericElseSetToDefault "AUTOPAUSE_TIMEOUT_EST" 3600 isNumericElseSetToDefault "AUTOPAUSE_TIMEOUT_INIT" 600 if [[ "$AUTOPAUSE_KNOCK_INTERFACE" == "lo" ]] ; then logWarning "AUTOPAUSE_KNOCK_INTERFACE is set to the local loopback interface." logWarning " This is not advisable, as incoming connections are likely not picked up there." logWarning " Continuing with this setting." fi if [[ -n "$MAX_TICK_TIME" && "$MAX_TICK_TIME" != "-1" ]] ; then logWarning "MAX_TICK_TIME is non-default, for autopause to work properly, this check should be disabled (-1 for versions >= 1.8.1)" elif [[ -z "$MAX_TICK_TIME" ]] ; then MAX_TICK_TIME=-1 if versionLessThan 1.8.1; then # 10 years MAX_TICK_TIME=315360000000 fi export MAX_TICK_TIME fi # seq_cooldown cannot be larger than AUTOPAUSE_TIMEOUT_KN, otherwise the server may # become paused while knockd is still ignoring packets, preventing players from joining. let COOLDOWN=$AUTOPAUSE_TIMEOUT_KN/2 sed -i "s/\(seq_cooldown *= *\).*/\1$COOLDOWN/" /tmp/knockd-config.cfg "$(dirname "$0")/auto/autopause-daemon.sh" & ================================================ FILE: scripts/start-autostop ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" : "${SERVER_PORT:=25565}" : "${AUTOSTOP_TIMEOUT_EST:=3600}" : "${AUTOSTOP_TIMEOUT_INIT:=1800}" : "${AUTOSTOP_PERIOD:=10}" : "${DEBUG_AUTOSTOP:=false}" export SERVER_PORT export AUTOSTOP_TIMEOUT_EST export AUTOSTOP_TIMEOUT_INIT export AUTOSTOP_PERIOD export DEBUG_AUTOSTOP log "Autostop functionality enabled" isDebugging && set -x isNumericElseSetToDefault "AUTOSTOP_PERIOD" 10 checkIfNotZeroElseSetToDefault "AUTOSTOP_PERIOD" 10 isNumericElseSetToDefault "AUTOSTOP_TIMEOUT_EST" 3600 isNumericElseSetToDefault "AUTOSTOP_TIMEOUT_INIT" 1800 "$(dirname "$0")/auto/autostop-daemon.sh" & ================================================ FILE: scripts/start-configuration ================================================ #!/bin/bash set -euo pipefail IFS=$'\n\t' # shellcheck source=start-utils . "$(dirname "$0")/start-utils" : "${EULA:=}" : "${PROXY:=}" : "${ENABLE_AUTOPAUSE:=false}" : "${ENABLE_AUTOSTOP:=false}" : "${RCON_CMDS_STARTUP:=}" : "${RCON_CMDS_ON_CONNECT:=}" : "${RCON_CMDS_ON_DISCONNECT:=}" : "${RCON_CMDS_FIRST_CONNECT:=}" : "${RCON_CMDS_LAST_DISCONNECT:=}" : "${RCON_CMDS_PERIOD:=10}" : "${ENABLE_RCON:=true}" : "${RCON_PORT:=25575}" export ENABLE_RCON RCON_PORT : "${MEMORY=1G}" : "${INIT_MEMORY=${MEMORY}}" : "${MAX_MEMORY=${MEMORY}}" export MEMORY INIT_MEMORY MAX_MEMORY shopt -s nullglob isDebugging && set -x if (( $(mc-image-helper java-release) >= 17 )); then MC_IMAGE_HELPER_OPTS+=" --enable-native-access=ALL-UNNAMED" export MC_IMAGE_HELPER_OPTS fi export HOME=/data log "Running as uid=$(id -u) gid=$(id -g) with /data as '$(ls -lnd /data)'" log "Image info: $(paste -d, -s /etc/image.properties)" if [ ! -e /data/eula.txt ]; then if ! isTrue "$EULA"; then log "" logError "Please accept the Minecraft EULA at" logError " https://account.mojang.com/documents/minecraft_eula" logError "by adding the following immediately after 'docker run':" logError " -e EULA=TRUE" log "" exit 1 fi writeEula fi if isTrue "${DEBUG_MEMORY:-false}"; then log "Memory usage and availability (in MB)" uname -pars | applyLogPrefix free -m | applyLogPrefix fi ########################################## # Setup RCON password if isTrue "${ENABLE_RCON:-true}"; then # turn off debug output set +x if [[ -v RCON_PASSWORD_FILE ]]; then if [ ! -e "${RCON_PASSWORD_FILE}" ]; then log "" logError "Initial RCON password file ${RCON_PASSWORD_FILE} does not seems to exist." logError "Please ensure your configuration." logError "If you are using Docker Secrets feature, please check this for further information: " logError " https://docs.docker.com/engine/swarm/secrets" log "" exit 1 else RCON_PASSWORD=$(cat "${RCON_PASSWORD_FILE}") export RCON_PASSWORD fi elif ! [[ -v RCON_PASSWORD ]]; then RCON_PASSWORD=$(openssl rand -hex 12) export RCON_PASSWORD fi # For rcon-cli access running via exec, which by default is running as root echo "password=${RCON_PASSWORD}" > "$HOME/.rcon-cli.env" echo "password: \"${RCON_PASSWORD}\"" > "$HOME/.rcon-cli.yaml" isDebugging && set -x else rm -f "$HOME/.rcon-cli.env" "$HOME/.rcon-cli.yaml" fi ########################################## # Auto-pause/stop if isTrue "${ENABLE_AUTOPAUSE}" && isTrue "${ENABLE_AUTOSTOP}"; then logError "ENABLE_AUTOPAUSE=true is incompatible with ENABLE_AUTOSTOP=true" exit 1 fi proxyArgs=() if [[ $PROXY ]]; then export http_proxy="$PROXY" export https_proxy="$PROXY" IFS=":" read -ra parts <<< "$PROXY" IFS=" " : "${PROXY_HOST=$(firstArrayElement parts)}" shiftArray parts : "${PROXY_PORT=$(firstArrayElement parts)}" fi # https://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html proxyArgs=() function addToProxyArgs() { if [[ $2 ]]; then proxyArgs+=("-D$1=$2") fi } addToProxyArgs http.proxyHost "${PROXY_HOST:-}" addToProxyArgs http.proxyPort "${PROXY_PORT:-}" addToProxyArgs http.nonProxyHosts "${PROXY_NON_PROXY_HOSTS:-}" export MC_IMAGE_HELPER_OPTS+=" ${proxyArgs[*]}" function fixJavaPath() { # Some Docker management UIs grab all the image declared variables and present them for configuration. # When upgrading images across Java versions, that creates parts mismatch in PATH's expected by base image. if ! which java > /dev/null; then logError " Your Docker provider has an annoying flaw where it" logError " tries to set PATH even though the container establishes" logError " parts very specific value." sleep 2 # now find where java might be for d in /opt/java/openjdk/bin /usr/bin; do if [ -x "${d}/java" ]; then PATH="${PATH}:${d}" return 0 fi done return 1 fi } if ! fixJavaPath; then logError "could not locate path that contains java" exit 1 fi cd /data || exit 1 export DECLARED_TYPE=${TYPE^^} export DECLARED_VERSION="$VERSION" if isTrue "${VERSION_FROM_MODRINTH_PROJECTS:-}" && [[ ${MODRINTH_PROJECTS:-} ]]; then if ! VERSION=$(mc-image-helper version-from-modrinth-projects --projects "${MODRINTH_PROJECTS}"); then logError "failed to resolve version from MODRINTH_PROJECTS: ${MODRINTH_PROJECTS}" exit 1 fi log "Resolved Minecraft version $VERSION from Modrinth projects" export VERSION fi if isTrue "${ENABLE_AUTOPAUSE}"; then "$(dirname "$0")/start-autopause" elif isTrue "${ENABLE_AUTOSTOP}"; then "$(dirname "$0")/start-autostop" fi if [[ "$RCON_CMDS_STARTUP" ]] || [[ "$RCON_CMDS_ON_CONNECT" ]] || [[ "$RCON_CMDS_ON_DISCONNECT" ]] || [[ "$RCON_CMDS_FIRST_CONNECT" ]] || [[ "$RCON_CMDS_LAST_DISCONNECT" ]] then log "Starting RCON commands" # shellcheck source=start-rconcmds "$(dirname "$0")/start-rconcmds" fi : "${MODPACK_PLATFORM:=${MOD_PLATFORM:-}}" if [[ $MODPACK_PLATFORM && $TYPE && $TYPE != VANILLA ]]; then logWarning "Avoid setting TYPE and MODPACK_PLATFORM" fi case "${TYPE^^}" in AUTO_CURSEFORGE|MODRINTH|CURSEFORGE|FTB|FTBA|GTNH) MODPACK_PLATFORM="$TYPE" ;; esac if [[ $MODPACK_PLATFORM ]]; then case "${MODPACK_PLATFORM^^}" in FTB|CURSEFORGE) exec "$(dirname "$0")/start-deployCF" "$@" ;; FTBA) exec "$(dirname "$0")/start-deployFTBA" "$@" ;; AUTO_CURSEFORGE) exec "$(dirname "$0")/start-deployAutoCF" "$@" ;; MODRINTH) exec "$(dirname "$0")/start-deployModrinth" "$@" ;; GTNH) exec "$(dirname "$0")/start-deployGTNH" "$@" ;; *) logError "Invalid MODPACK_PLATFORM: '$MODPACK_PLATFORM'" exit 1 ;; esac fi log "Resolving type given ${TYPE}" case "${TYPE^^}" in *BUKKIT|SPIGOT) exec "$(dirname "$0")/start-deployBukkitSpigot" "$@" ;; PAPER) exec "$(dirname "$0")/start-deployPaper" "$@" ;; FOLIA) exec "$(dirname "$0")/start-deployFolia" "$@" ;; FORGE) exec "$(dirname "$0")/start-deployForge" "$@" ;; NEOFORGE|NEOFORGED) exec "$(dirname "$0")/start-deployNeoForge" "$@" ;; FABRIC) exec "$(dirname "$0")/start-deployFabric" "$@" ;; QUILT) exec "$(dirname "$0")/start-deployQuilt" "$@" ;; VANILLA) exec "$(dirname "$0")/start-deployVanilla" "$@" ;; SPONGEVANILLA) exec "$(dirname "$0")/start-deploySpongeVanilla" "$@" ;; CUSTOM) exec "$(dirname "$0")/start-deployCustom" "$@" ;; MAGMA) exec "$(dirname "$0")/start-deployMagma" "$@" ;; MAGMA_MAINTAINED) exec "$(dirname "$0")/start-deployMagmaMaintained" "$@" ;; KETTING) exec "$(dirname "$0")/start-deployKetting" "$@" ;; MOHIST|YOUER|BANNER) exec "$(dirname "$0")/start-deployMohist" "$@" ;; PURPUR) exec "$(dirname "$0")/start-deployPurpur" "$@" ;; PUFFERFISH) exec "$(dirname "$0")/start-deployPufferfish" "$@" ;; CANYON) exec "$(dirname "$0")/start-deployCanyon" "$@" ;; LIMBO) exec "$(dirname "$0")/start-deployLimbo" "$@" ;; NANOLIMBO) exec "$(dirname "$0")/start-deployNanoLimbo" "$@" ;; CRUCIBLE) exec "$(dirname "$0")/start-deployCrucible" "$@" ;; LEAF) exec "$(dirname "$0")/start-deployLeaf" "$@" ;; ARCLIGHT) exec "$(dirname "$0")/start-deployArcLight" "$@" ;; POSEIDON) exec "$(dirname "$0")/start-deployPoseidon" "$@" ;; *) logError "Invalid TYPE: '$TYPE'" logError "Must be: VANILLA, FORGE, BUKKIT, SPIGOT, PAPER, FOLIA, PURPUR, FABRIC, QUILT," logError " SPONGEVANILLA, CUSTOM, MAGMA, MOHIST, GTNH, AIRPLANE, PUFFERFISH," logError " CANYON, LIMBO, NANOLIMBO, CRUCIBLE, LEAF, YOUER, BANNER, NEOFORGE" exit 1 ;; esac ================================================ FILE: scripts/start-deployArcLight ================================================ #!/bin/bash # shellcheck source=start-utils . "${SCRIPTS:-$(dirname "$0")}/start-utils" set -o pipefail set -e isDebugging && set -x resolveVersion : "${ARCLIGHT_RELEASE:=latest}" : "${ARCLIGHT_TYPE:=neoforge}" arclightReleasesUrl=https://api.github.com/repos/IzzelAliz/Arclight/releases if [[ ${ARCLIGHT_RELEASE^^} = LATEST ]]; then arclightReleaseUrl=${arclightReleasesUrl}/latest else arclightReleaseUrl=${arclightReleasesUrl}/tags/${ARCLIGHT_RELEASE} fi if ! downloadUrl=$(get --json-path "$.assets[?(@.name =~ /arclight-${ARCLIGHT_TYPE,,}-${VERSION}-.*\.jar/)].browser_download_url" \ --accept "application/vnd.github.v3+json" "$arclightReleaseUrl"); then logError "Failed to access ${ARCLIGHT_RELEASE} release of Arclight" exit 1 fi if [[ $downloadUrl = null ]]; then logError "Failed to locate Arclight jar for $VERSION from ${ARCLIGHT_RELEASE}" exit 1 fi log "Downloading Arclight from $downloadUrl" if ! SERVER=$(get --skip-existing --output-filename -o /data "$downloadUrl"); then logError "Arclight jar from $downloadUrl" exit 1 fi export SERVER export FAMILY=HYBRID export HYBRIDTYPE="${ARCLIGHT_TYPE,,}" exec "$(dirname "$0")/start-spiget" "$@" ================================================ FILE: scripts/start-deployAutoCF ================================================ #!/bin/bash set -eu # shellcheck source=start-utils . "$(dirname "$0")/start-utils" : "${CF_PAGE_URL:=}" : "${CF_SLUG:=}" : "${CF_FILE_ID:=}" : "${CF_FILENAME_MATCHER:=}" : "${CF_PARALLEL_DOWNLOADS:=4}" : "${CF_FORCE_SYNCHRONIZE:=false}" : "${CF_FORCE_REINSTALL_MODLOADER:=false}" : "${CF_IGNORE_MISSING_FILES:=}" : "${CF_EXCLUDE_INCLUDE_FILE=/image/cf-exclude-include.json}" : "${CF_EXCLUDE_MODS:=}" : "${CF_EXCLUDE_ALL_MODS:=}" : "${CF_FORCE_INCLUDE_MODS:=}" : "${CF_SET_LEVEL_FROM:=}" # --set-level-from : "${CF_OVERRIDES_SKIP_EXISTING:=false}" # --overrides-skip-existing : "${CF_OVERRIDES_EXCLUSIONS:=}" # --overrides-exclusions : "${CF_DOWNLOADS_REPO=$([ -d /downloads ] && echo '/downloads' || echo '')}" : "${CF_MODPACK_MANIFEST:=}" : "${CF_API_CACHE_DEFAULT_TTL:=}" # as ISO-8601 duration, such as P2D or PT12H : "${CF_API_KEY_FILE:=}" # Path to file containing CurseForge API key : "${CF_MOD_LOADER_VERSION:=}" # Override mod loader version resultsFile=/data/.install-curseforge.env if [[ -n ${CF_API_KEY_FILE} ]]; then if [[ -r "${CF_API_KEY_FILE}" ]]; then CF_API_KEY="$(cat "${CF_API_KEY_FILE}")" export CF_API_KEY else logError "CF_API_KEY_FILE is not readable: ${CF_API_KEY_FILE}" exit 1 fi fi isDebugging && set -x ensureRemoveAllModsOff "MODPACK_PLATFORM=AUTO_CURSEFORGE" args=( --results-file="$resultsFile" --force-synchronize="$CF_FORCE_SYNCHRONIZE" --force-reinstall-modloader="$CF_FORCE_REINSTALL_MODLOADER" --overrides-skip-existing="$CF_OVERRIDES_SKIP_EXISTING" ) setArg() { arg="${1?}" var="${2?}" if [[ ${!var} ]]; then args+=("${arg}=${!var}") fi } setArg --modpack-page-url CF_PAGE_URL setArg --file-id CF_FILE_ID setArg --slug CF_SLUG setArg --modpack-manifest CF_MODPACK_MANIFEST setArg --filename-matcher CF_FILENAME_MATCHER setArg --set-level-from CF_SET_LEVEL_FROM setArg --overrides-exclusions CF_OVERRIDES_EXCLUSIONS setArg --ignore-missing-files CF_IGNORE_MISSING_FILES setArg --api-cache-default-ttl CF_API_CACHE_DEFAULT_TTL setArg --exclude-mods CF_EXCLUDE_MODS setArg --exclude-all-mods CF_EXCLUDE_ALL_MODS setArg --force-include-mods CF_FORCE_INCLUDE_MODS setArg --exclude-include-file CF_EXCLUDE_INCLUDE_FILE setArg --mod-loader-version CF_MOD_LOADER_VERSION setArg --downloads-repo CF_DOWNLOADS_REPO if ! mc-image-helper install-curseforge "${args[@]}"; then logError "Failed to auto-install CurseForge modpack" exit 1 fi applyResultsFile ${resultsFile} resolveFamily exec "$(dirname "$0")/start-setupWorld" "$@" ================================================ FILE: scripts/start-deployBukkitSpigot ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" isDebugging && set -x set -eo pipefail spigotBuildLog="/data/spigot_build.log" function handleFailedSpigotBuild { logError "Failed to build Spigot" cat ${spigotBuildLog} exit 1 } function buildSpigotFromSource { local tempDir="/data/temp" if [[ ${TYPE^^} = *BUKKIT ]] && ! versionLessThan "1.14"; then logError "Craftbukkit build is only supported for versions less than 1.14" exit 1 fi log "Building Spigot $VANILLA_VERSION from source, might take a while, get some coffee" rm -rf ${tempDir} mkdir ${tempDir} cd ${tempDir} jvmOpts="" if isPercentage "${INIT_MEMORY:-$MEMORY}"; then jvmOpts+="-XX:InitialRAMPercentage=$(getPercentageValue "${INIT_MEMORY:-$MEMORY}") " else jvmOpts+="-Xms${INIT_MEMORY:-$MEMORY} " fi if isPercentage "${MAX_MEMORY:-$MEMORY}"; then jvmOpts+="-XX:MaxRAMPercentage=$(getPercentageValue "${MAX_MEMORY:-$MEMORY}")" else jvmOpts+="-Xmx${MAX_MEMORY:-$MEMORY}" fi logn '' curl -sSL -o ${tempDir}/BuildTools.jar https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar && \ java $jvmOpts -jar ${tempDir}/BuildTools.jar --rev "$VERSION" 2>&1 |tee ${spigotBuildLog}| while read l; do echo -n .; done; log "done" case ${TYPE^^} in SPIGOT) if ! mv spigot-*.jar "/data/${SERVER}"; then handleFailedSpigotBuild fi ;; *BUKKIT) if ! mv craftbukkit-*.jar "/data/${SERVER}"; then handleFailedSpigotBuild fi ;; esac log "Cleaning up" rm -rf ${tempDir} cd /data } function downloadSpigot { local match local getBukkitBaseUrl="https://getbukkit.org/download/" local getBukkitSpigotUrl="${getBukkitBaseUrl}spigot" case "$TYPE" in *BUKKIT|*bukkit) match="CraftBukkit" downloadUrl=${BUKKIT_DOWNLOAD_URL} getbukkitFlavor=craftbukkit ;; *) match="Spigot" downloadUrl=${SPIGOT_DOWNLOAD_URL} getbukkitFlavor=spigot ;; esac if [[ ${VERSION^^} = LATEST ]]; then if ! downloadUrl=$(restify "${getBukkitBaseUrl}/${getbukkitFlavor}" --id='downloadr' | jq -r '.[0] | .href '); then logError "Failed to retrieve latest version from ${getBukkitSpigotUrl} -- site might be down" exit 1 fi fi if [[ -z $downloadUrl ]]; then downloadUrl="https://cdn.getbukkit.org/${getbukkitFlavor}/${getbukkitFlavor}-${VERSION}.jar" fi log "Downloading $match from $downloadUrl if missing ..." if ! SERVER=$(get -o /data --skip-existing --output-filename "$downloadUrl"); then cat < $installMarker fi 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) if [[ -z "${SERVER}" || ! -f "${SERVER}" ]]; then logError "Unable to locate installed forge server jar" isDebugging && find "${FTB_BASE_DIR}" -name "forge*.jar" exit 2 fi export SERVER FTB_DIR=$(dirname "${SERVER}") export FTB_DIR exec "$(dirname "$0")/start-setupWorld" "$@" fi findStartScript() { entryScriptExpr=( -name ServerStart.sh -o -name serverstart.sh -o -name ServerStartLinux.sh -o -name LaunchServer.sh -o -name server-start.sh -o -name SERVER-START.sh -o -name start-server.sh -o -name startserver.sh -o -name StartServer.sh -o -name run.sh -o -name start.sh -o -name launch.sh ) if [ -d "${FTB_BASE_DIR}" ]; then find "${FTB_BASE_DIR}" \( "${entryScriptExpr[@]}" \) -print -quit fi } startScript=$(findStartScript) # only download and install if a mod pack isn't already installed # also check for the start script rather than just the folder # this allows saving just the world separate from the rest of the data directory if [[ ! $startScript ]]; then downloadModpack srv_modpack=${FTB_SERVER_MOD} log "Unpacking FTB server modpack ${srv_modpack} ..." mkdir -p "${FTB_BASE_DIR}" unzip -o "${srv_modpack}" -d "${FTB_BASE_DIR}" | awk '{printf "."} END {print ""}' installScriptExpr=( -name install.sh -o -name FTBInstall.sh -o -name Install.sh ) installScript=$(find "${FTB_BASE_DIR}" -maxdepth 2 -type f \( "${installScriptExpr[@]}" \) -print -quit) if [[ "$installScript" ]]; then ( cd "$(dirname "${installScript}")" chmod +x "${installScript}" log "Running included $(basename "${installScript}"). This might take a minute or two..." "${installScript}" > install.log ) fi startScript=$(findStartScript) fi # start script provided by unzipped+installed modpack? if [[ ! $startScript ]]; then # no, then look for a forge jar to run # Allow up to 2 levels since some modpacks have a top-level directory named for the modpack 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) if [[ "$forgeJar" ]]; then FTB_BASE_DIR=$(dirname "${forgeJar}") export FTB_BASE_DIR log "No entry script found, so building one for ${forgeJar}" cat > "${FTB_BASE_DIR}/ServerStart.sh" < /dev/null || \ sed -i 's/-jar/-Dfml.queryResult=confirm -jar/' "${FTB_SERVER_START}" sed -i 's/.*read.*Restart now/#\0/' "${FTB_SERVER_START}" legacyJavaFixerPath="${FTB_DIR}/mods/legacyjavafixer.jar" if isTrue "${FTB_LEGACYJAVAFIXER}" && [ ! -e "${legacyJavaFixerPath}" ]; then log "Installing legacy java fixer to ${legacyJavaFixerPath}" if ! get -o "${legacyJavaFixerPath}" ${legacyJavaFixerUrl}; then logError "Failed to download legacy java fixer from ${legacyJavaFixerUrl}" exit 1 fi fi export FAMILY=FORGE exec "$(dirname "$0")/start-setupWorld" "$@" ================================================ FILE: scripts/start-deployCanyon ================================================ #!/bin/bash set -euo pipefail IFS=$'\n\t' # shellcheck source=start-utils . "$(dirname "$0")/start-utils" isDebugging && set -x : "${CANYON_BUILD:=lastSuccessfulBuild}" if [ "${VERSION}" != "b1.7.3" ]; then logError "Canyon server type only supports VERSION=b1.7.3" exit 1 fi canyonJob="https://canyonmodded.p0rtal.gay/job/Canyonmodded" githubUrl="https://github.com/KoboDev/SupplyAndDemand/releases/download" canyonBuildJSON=$(curl -fsSL "${canyonJob}/${CANYON_BUILD}/api/json") buildRelPath=$( jq -r '.artifacts[0].relativePath' <<< "$canyonBuildJSON" ) buildNumber=$( jq -r '.number'<<< "$canyonBuildJSON" ) baseName=$(basename "${buildRelPath}") # Add build tag to non-tagged builds for version detection if [ ${baseName%.*} = 'canyon-server' ]; then export SERVER=${baseName%.*}_build${buildNumber}.jar else export SERVER="${baseName}" fi if [ ! -f "$SERVER" ]; then # If CANYON_BUILD is old artifact, then download from GitHub if [ "${CANYON_BUILD}" = "final" ]; then downloadUrl="${githubUrl}/finalcanyon/canyon-server.jar" log "Downloading final Canyon build from $downloadUrl ..." elif [ "${CANYON_BUILD}" = "6" ]; then downloadUrl="${githubUrl}/CanyonRelease/Canyon-Build-6.jar" log "Downloading Canyon build 6 from $downloadUrl ..." elif [ "${CANYON_BUILD}" = "26" ]; then downloadUrl="${githubUrl}/CanyonRelease/canyon-build-26.jar" log "Downloading Canyon build 26 from $downloadUrl ..." # Builds greater than 32 are on Jenkins else downloadUrl="${canyonJob}/${CANYON_BUILD}/artifact/${buildRelPath}" log "Downloading Canyon build ${buildNumber} from $downloadUrl ..." fi curl -fsSL -o "$SERVER" "$downloadUrl" if [ ! -f "$SERVER" ]; then logError "Failed to download from $downloadUrl" exit 3 fi fi # Normalize on Spigot for later operations export FAMILY=SPIGOT exec "$(dirname "$0")/start-spiget" "$@" ================================================ FILE: scripts/start-deployCrucible ================================================ #!/bin/bash # shellcheck source=start-utils . "${SCRIPTS:-$(dirname "$0")}/start-utils" set -o pipefail set -e isDebugging && set -x resolveVersion : "${CRUCIBLE_RELEASE:=latest}" crucibleReleasesUrl=https://api.github.com/repos/CrucibleMC/Crucible/releases if [[ ${CRUCIBLE_RELEASE^^} = LATEST ]]; then crucibleReleaseUrl=${crucibleReleasesUrl}/latest else crucibleReleaseUrl=${crucibleReleasesUrl}/tags/${CRUCIBLE_RELEASE} fi if ! downloadUrl=$(get --json-path "$.assets[?(@.name =~ /Crucible-${VERSION}-.*\.jar/)].browser_download_url" \ --accept "application/vnd.github.v3+json" "$crucibleReleaseUrl"); then logError "Failed to access ${CRUCIBLE_RELEASE} release of Crucible" exit 1 fi if [[ $downloadUrl = null ]]; then logError "Failed to locate Crucible jar for $VERSION from ${CRUCIBLE_RELEASE}" exit 1 fi log "Downloading Crucible from $downloadUrl" if ! SERVER=$(get --skip-existing --output-filename -o /data "$downloadUrl"); then logError "Crucible jar from $downloadUrl" exit 1 fi librariesDir=/data/libraries if [ ! -d "$librariesDir" ]; then if ! librariesUrl=$(get --json-path "$.assets[?(@.name == 'libraries.zip')].browser_download_url" \ --accept "application/vnd.github.v3+json" "$crucibleReleaseUrl"); then logError "Failed to access ${CRUCIBLE_RELEASE} release of Crucible for libraries" exit 1 fi log "Downloading Crucible libraries" if ! get -o /tmp/libraries.zip "$librariesUrl"; then logError "Failed to download Crucible libraries from $librariesUrl" exit 1 fi if ! unzip /tmp/libraries.zip -d "$librariesDir"; then logError "Failed to unzip Crucible libraries" exit 1 fi rm /tmp/libraries.zip fi export SERVER export FAMILY=HYBRID export HYBRIDTYPE=forge exec "$(dirname "$0")/start-spiget" "$@" ================================================ FILE: scripts/start-deployCustom ================================================ #!/bin/bash : "${CUSTOM_SERVER:=}" : "${GENERIC_PACK:=}" : "${CUSTOM_JAR_EXEC:=}" # shellcheck source=start-utils . "$(dirname "$0")/start-utils" isDebugging && set -x if isURL "${CUSTOM_SERVER}"; then filename=$(basename "${CUSTOM_SERVER}") export SERVER=/data/${filename} if [[ -f ${SERVER} ]] && ! isTrue "$FORCE_REDOWNLOAD"; then log "Using previously downloaded jar at ${SERVER}" else log "Downloading custom server jar from ${CUSTOM_SERVER} ..." if ! curl -sSL -o "${SERVER}" "${CUSTOM_SERVER}"; then log "Failed to download from ${CUSTOM_SERVER}" exit 2 fi fi elif [[ -f ${CUSTOM_SERVER} ]]; then export SERVER=${CUSTOM_SERVER} elif [[ ${GENERIC_PACK} ]]; then log "Using custom server jar from generic pack at ${CUSTOM_SERVER} ..." export SERVER=${CUSTOM_SERVER} elif [[ ${CUSTOM_JAR_EXEC} ]]; then log "CUSTOM_JAR_EXEC is in use, so \$SERVER will not be set" else log "CUSTOM_SERVER is not properly set to a URL or existing jar file" exit 2 fi # Allow for overriding Family on custom for testing. export FAMILY="${FAMILY:-HYBRID}" export HYBRIDTYPE="${HYBRIDTYPE:-any}" exec "$(dirname "$0")/start-spiget" "$@" ================================================ FILE: scripts/start-deployFTBA ================================================ #!/bin/bash : "${FTB_FORCE_REINSTALL:=false}" : "${FTB_BASE_URL:=https://api.feed-the-beast.com/v1/modpacks}" # Legacy base URL was https://api.modpacks.ch ftbInstallMarker=".ftb-installed" # shellcheck source=start-utils . "$(dirname "$0")/start-utils" isDebugging && set -x set -e # #{ # "id": 119, # "name": "FTB Presents Direwolf20 1.20", # "versionName": "1.16.0", # "versionId": 12252, # "modPackTargets": { # "modLoader": { # "name": "neoforge", # "version": "47.1.84" # }, # "javaVersion": "17.0.7+7", # "mcVersion": "1.20.1" # }, ftbManifest=.manifest.json function getModLoaderName() { jq -r ".modPackTargets.modLoader.name" ${ftbManifest} } function getModLoaderVersion() { jq -r ".modPackTargets.modLoader.version" ${ftbManifest} } function getMinecraftVersion() { jq -r ".modPackTargets.mcVersion" ${ftbManifest} } function getModpackName() { jq -r ".name" ${ftbManifest} } function getModpackVersion() { jq -r ".versionName" ${ftbManifest} } if [[ $(getDistro) = alpine ]]; then logError "The FTBA installer is not supported on Alpine. Use the java8-multiarch image tag instead." exit 1 fi if ! [[ -v FTB_MODPACK_ID ]]; then logError "FTB_MODPACK_ID is required with TYPE=FTB" exit 1 fi if ! [[ ${FTB_MODPACK_ID} =~ [0-9]+ ]]; then logError "FTB_MODPACK_ID needs to be numeric" exit 1 fi if [[ ! $FTB_MODPACK_VERSION_ID ]]; then if ! FTB_MODPACK_VERSION_ID=$(curl -fsSL "${FTB_BASE_URL}/public/modpack/${FTB_MODPACK_ID}" | jq -r '.versions | sort_by(.updated)[-1].id'); then logError "Unable to resolve latest modpack version ID for modpack ${FTB_MODPACK_ID}" exit 1 fi elif ! [[ ${FTB_MODPACK_VERSION_ID} =~ [0-9]+ ]]; then logError "FTB_MODPACK_VERSION_ID needs to be numeric" exit 1 fi if isTrue "$FTB_FORCE_REINSTALL" || ! [ -f "${ftbManifest}" ] || ! [ -f "${ftbInstallMarker}" ] || [ "$(cat "${ftbInstallMarker}")" != "${FTB_MODPACK_ID}=${FTB_MODPACK_VERSION_ID}" ]; then ftbInstaller=/data/ftb-installer-v2 arm= if ! [[ -f "${ftbInstaller}" ]]; then if [ "$(uname -m)" == "aarch64" ]; then log "Downloading FTB installer for ARM" arm="/arm" else log "Downloading FTB installer for x86" fi # Example: https://api.feed-the-beast.com/v1/modpacks/public/modpack/119/12252/server/linux # https://api.feed-the-beast.com/v1/modpacks/public/modpack/119/12252/server/arm/linux # 1-1 is a placeholder modpack for just grabbing the installer if ! get -o "${ftbInstaller}" "${FTB_BASE_URL}/public/modpack/1/1/server${arm}/linux"; then logError "Failed to download FTB installer" exit 1 fi chmod +x "${ftbInstaller}" fi log "Installing modpack ID ${FTB_MODPACK_ID}, version ID ${FTB_MODPACK_VERSION_ID}" log "This could take a while..." ${ftbInstaller} -pack "${FTB_MODPACK_ID}" -version "${FTB_MODPACK_VERSION_ID}" -auto -force -no-java | tee ftb-installer.log rm -f forge*installer.jar echo "${FTB_MODPACK_ID}=${FTB_MODPACK_VERSION_ID}" > ${ftbInstallMarker} writeEula else log "FTB modpack ID ${FTB_MODPACK_ID}, version ID ${FTB_MODPACK_VERSION_ID} is ready to go" fi modLoader="$(getModLoaderName)" modLoaderVersion="$(getModLoaderVersion)" mcVersion=$(getMinecraftVersion) VERSION="$mcVersion" MODPACK_NAME=$(getModpackName) MODPACK_VERSION=$(getModpackVersion) export VERSION MODPACK_NAME MODPACK_VERSION variants=( run.sh fabric-server-launch.jar "${modLoader}-${mcVersion}-${modLoaderVersion}-universal.jar" "${modLoader}-${mcVersion}-${modLoaderVersion}-${mcVersion}-universal.jar" "${modLoader}-${mcVersion}-${modLoaderVersion}-server-launch.jar" "${modLoader}-${mcVersion}-${modLoaderVersion}.jar" ) for f in "${variants[@]}"; do if [ -f $f ]; then export SERVER=$f break fi done if ! [ -v SERVER ]; then logError "Unable to locate the installed FTB server jar" logError " Tried looking for ${variants[*]}" exit 2 fi # Remap the TYPE and FAMILY based on discovered server jar if [[ $SERVER = run.sh ]]; then if grep -q neoforge "$SERVER"; then export FAMILY=FORGE export TYPE=NEOFORGE elif grep -q forge "$SERVER"; then export FAMILY=FORGE export TYPE=FORGE elif grep -q fabric run.s; then export FAMILY=FABRIC export TYPE=FABRIC else logError "Unrecognized loader type in $SERVER" cat "$SERVER" exit 1 fi elif [[ $SERVER = forge* ]]; then export FAMILY=FORGE export TYPE=FORGE elif [[ $SERVER = fabric* ]]; then export FAMILY=FABRIC export TYPE=FABRIC else logError "Unrecognized loader type from $SERVER" exit 1 fi exec "$(dirname "$0")/start-setupWorld" "$@" ================================================ FILE: scripts/start-deployFabric ================================================ #!/bin/bash set -eu # shellcheck source=start-utils . "$(dirname "$0")/start-utils" export TYPE=FABRIC : "${FABRIC_LAUNCHER_VERSION:=${FABRIC_INSTALLER_VERSION:-LATEST}}" : "${FABRIC_LAUNCHER:=}" : "${FABRIC_LAUNCHER_URL:=}" : "${FABRIC_LOADER_VERSION:=LATEST}" : "${FABRIC_FORCE_REINSTALL:=false}" resultsFile=/data/.install-fabric.env isDebugging && set -x commonArgs=( --results-file="${resultsFile}" ) if isTrue "${FABRIC_FORCE_REINSTALL}"; then log "Forcing re-install of Fabric" commonArgs+=(--force-reinstall) fi # Custom fabric jar if [[ $FABRIC_LAUNCHER ]]; then if ! mc-image-helper install-fabric-loader \ "${commonArgs[@]}" \ --from-local-file="$FABRIC_LAUNCHER"; then logError "Failed to use provided Fabric launcher" exit 1 fi # Custom fabric jar url elif [[ $FABRIC_LAUNCHER_URL ]]; then if ! mc-image-helper install-fabric-loader \ "${commonArgs[@]}" \ --from-url="$FABRIC_LAUNCHER_URL"; then logError "Failed to installFabric launcher from $FABRIC_LAUNCHER_URL" exit 1 fi # Official fabric launcher else if ! mc-image-helper install-fabric-loader \ "${commonArgs[@]}" \ --minecraft-version="${VERSION}" \ --installer-version="${FABRIC_LAUNCHER_VERSION}" \ --loader-version="${FABRIC_LOADER_VERSION}"; then logError "Failed to installFabric launcher given $VERSION, $FABRIC_LAUNCHER_VERSION, $FABRIC_LOADER_VERSION" exit 1 fi fi applyResultsFile ${resultsFile} export FAMILY=FABRIC exec "$(dirname "$0")/start-setupWorld" "$@" ================================================ FILE: scripts/start-deployFolia ================================================ #!/bin/bash : "${FOLIA_CHANNEL:=default}" # shellcheck source=start-utils . "$(dirname "$0")/start-utils" set -o pipefail handleDebugMode if [[ $FOLIA_DOWNLOAD_URL ]]; then export PAPER_DOWNLOAD_URL="$FOLIA_DOWNLOAD_URL" fi if [[ $FOLIABUILD ]]; then export PAPERBUILD="$FOLIABUILD" fi PAPER_PROJECT="folia" \ PAPER_NAME="FoliaMC" \ PAPER_CHANNEL="${FOLIA_CHANNEL}" \ exec "$(dirname "$0")/start-deployPaper" "$@" ================================================ FILE: scripts/start-deployForge ================================================ #!/bin/bash : "${FORGE_VERSION:=${FORGEVERSION:-RECOMMENDED}}" : "${FORGE_FORCE_REINSTALL:=false}}" # shellcheck source=start-utils . "${SCRIPTS:-$(dirname "$0")}/start-utils" isDebugging && set -x resultsFile=/data/.run-forge.env function mc-image-helper-forge() { mc-image-helper install-forge \ --output-directory=/data \ --results-file="${resultsFile}" \ --minecraft-version="${VERSION}" \ --force-reinstall="${FORGE_FORCE_REINSTALL}" "$@" } if [[ ${FORGE_INSTALLER} ]]; then if ! mc-image-helper-forge --forge-installer="${FORGE_INSTALLER}" ; then logError "Failed to installForge given installer ${FORGE_INSTALLER}" exit 1 fi elif [[ ${FORGE_INSTALLER_URL:-} ]]; then mkdir -p tmp if ! installer=$(get -o tmp --output-filename "${FORGE_INSTALLER_URL}"); then logError "Failed to download installer from $FORGE_INSTALLER_URL" exit 1 fi # shellcheck disable=SC2064 trap "rm $installer" EXIT if ! mc-image-helper-forge --forge-installer="${installer}" ; then logError "Failed to install forge from ${FORGE_INSTALLER_URL}" exit 1 fi else if ! mc-image-helper-forge --forge-version="${FORGE_VERSION}"; then logError "Failed to install Forge" exit 1 fi fi applyResultsFile ${resultsFile} export FAMILY=FORGE exec "$(dirname "$0")/start-setupWorld" "$@" ================================================ FILE: scripts/start-deployGTNH ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" # Define setup functions function getGTNHdownloadPath(){ gtnh_download_path="" current_java_version=$(mc-image-helper java-release) if ! packs_data="$( curl -fsSL "https://downloads.gtnewhorizons.com/versions.json" \ | jq -r '.versions[]?.server? | .[]? | select(type=="string" and test("Server"))' )"; then logError "Failed to retrieve data from https://downloads.gtnewhorizons.com/versions.json" exit 1 fi mapfile -t packs <<< "$packs_data" log "Start locating server files..." for pack in "${packs[@]}"; do # Extract the Java version(s) from the pack filename if ! pack_java_version=$(basename "$pack" | grep -Eo 'Java_[0-9]+(-[0-9]+)?' | sed 's/Java_//'); then logWarning "Could not parse java version of $pack" fi # Skip the pack if the current Java version is not compatible if [[ "$pack_java_version" == *-* ]]; then # Handle range of Java versions (e.g., "17-21") java_min_version=$(echo "$pack_java_version" | cut -d'-' -f1) java_max_version=$(echo "$pack_java_version" | cut -d'-' -f2) if (( current_java_version < java_min_version || current_java_version > java_max_version )); then debug "Skipping $pack due to incompatible Java version: $current_java_version not in range $java_min_version-$java_max_version" continue fi else # Handle single Java version (e.g., "8") if (( current_java_version != pack_java_version )); then debug "Skipping $pack due to incompatible Java version: $current_java_version != $pack_java_version" continue fi fi # Extract version numbers and release type (beta or RC) from the file names if ! pack_version=$(basename "$pack" | grep -Eo '[0-9]+(\.[0-9]+)+'); then logWarning "Could not parse version of $pack" fi if ! pack_release_type=$(basename "$pack" | grep -Eo '(beta|RC)(-[0-9]+)?' || echo ""); then logWarning "Could not parse release type of $pack" fi if ! current_version=$(basename "$gtnh_download_path" | grep -Eo '[0-9]+(\.[0-9]+)+'); then debug "Could not parse version of selected download path. String might be empty." fi if ! current_release_type=$(basename "$gtnh_download_path" | grep -Eo '(beta|RC)(-[0-9]+)?' || echo ""); then debug "Could not parse release type of selected download path. String might be empty." fi # Check if the pack matches the desired type based on GTNH_PACK_VERSION: # - If GTNH_PACK_VERSION is "latest-dev", only consider beta packs (path contains "/betas/"). # - If GTNH_PACK_VERSION is "latest", only consider non-beta packs (path does not contain "/betas/"). if [[ ($pack == *"/betas/"* && $GTNH_PACK_VERSION == "latest-dev") || ($pack != *"/betas/"* && $GTNH_PACK_VERSION == "latest") ]]; then # Compare versions and update gtnh_download_path if pack is newer # Check if the current version is unset or if the pack version is newer than the current version. # This comparison uses version sorting to determine the latest version. if [[ -z "$current_version" || "$(printf '%s\n' "$pack_version" "$current_version" | sort -V | tail -n 1)" == "$pack_version" ]]; then # If the pack version is the same as the current version, prioritize based on release type. # Full versions are preferred over RC (Release Candidate), and RC is preferred over beta. # Within the same release type, higher numbered versions are preferred. if [[ "$pack_version" == "$current_version" ]]; then if [[ -z "$pack_release_type" || ("$pack_release_type" == "RC" && "$current_release_type" == "beta") || ("$pack_release_type" == "$current_release_type" && "$(printf '%s\n' "$pack_release_type" "$current_release_type" | sort -V | tail -n 1)" == "$pack_release_type") ]]; then debug "$current_version-$current_release_type is older than $pack_version-$pack_release_type! Update latest version to: $pack_version-$pack_release_type" gtnh_download_path="$pack" fi else # If the pack version is newer than the current version, set it as the download path. debug "$current_version is older than $pack_version! Update latest version to: $pack_version" gtnh_download_path="$pack" fi fi else if [[ "$pack_version" == "$GTNH_PACK_VERSION" || "$pack_version-$pack_release_type" == "$GTNH_PACK_VERSION" ]]; then log "Found exact match $pack_version = $GTNH_PACK_VERSION! Select $pack_version for download." gtnh_download_path="$pack" break fi fi done } function deleteGTNHbackup(){ log "Start deleting all config backups" if ! find . -maxdepth 1 -type d -name 'gtnh-upgrade-*' -exec rm -rf {} + ; then logWarning "Can not delete config backup!" fi } function updateGTNH(){ # Get the current date and time current_datetime=$(date +%Y-%m-%dT%H:%M) # Define folders and files to update folders_to_update=("libraries" "mods" "resources" "scripts") 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") config_folder="config" backup_folder="/data/gtnh-upgrade-${current_version}${current_release_type:+-$current_release_type}-$current_datetime" journey_map_folder="JourneyMapServer" # Delete specified folders if they exist for folder in "${folders_to_update[@]}"; do folder_path="/data/$folder" if [[ -d "$folder_path" ]]; then log "Deleting folder: $folder_path" rm -rf "$folder_path" fi done # Delete specific files if they exist for file in "${files_to_update[@]}"; do file_path="/data/$file" if [[ -f "$file_path" ]]; then log "Deleting file: $file_path" rm -f "$file_path" fi done # Backup the config folder if [[ -d "/data/$config_folder" ]]; then log "Creating backup of /data/$config_folder at $backup_folder" cp -r "/data/$config_folder" "$backup_folder" log "Deleting original /data/$config_folder" rm -rf "/data/$config_folder" fi # Updating the required folders in data directory for folder in "${folders_to_update[@]}" "$config_folder"; do if [[ -d "$base_dir/$folder" ]]; then log "Copying $folder to /data" cp -r "$base_dir/$folder" "/data/" else logWarning "Folder $folder not found in the unzipped data!" fi done # Copy specific files to the /data directory for file in "${files_to_update[@]}"; do if [[ -f "$base_dir/$file" ]]; then log "Copying $file to /data" cp "$base_dir/$file" "/data/" else logWarning "File $file not found in the unzipped data!" fi done # Ensure the config folder exists if [[ ! -d "$config_folder" ]]; then log "$config_folder does not exist. Creating it now." mkdir -p "$config_folder" fi # Restore JourneyMapServer folder from backup if [[ -d "$backup_folder/$journey_map_folder" ]]; then log "Restoring $journey_map_folder to $config_folder" cp -r "$backup_folder/$journey_map_folder" "$config_folder/" else logWarning "$journey_map_folder not found in backup!" fi # Copy the changelog file to /data gtnh_changelog_file=$(mc-image-helper find --max-depth=1 --type=file --name=changelog*.md "$base_dir") if [[ -n "$gtnh_changelog_file" ]]; then log "Copying changelog file to /data" cp -f "$gtnh_changelog_file" /data/ fi } function handleGTNH() { : "${GTNH_PACK_VERSION:=latest}" : "${GTNH_DELETE_BACKUPS:=false}" : "${SKIP_GTNH_UPDATE_CHECK:=false}" 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" if isTrue "$GTNH_DELETE_BACKUPS"; then deleteGTNHbackup fi if [[ -n $GTNH_PACK_VERSION ]] && isFalse "$SKIP_GTNH_UPDATE_CHECK" ; then getGTNHdownloadPath if [[ -z $gtnh_download_path ]]; then logError "Server files not found for GTNH_PACK_VERSION=$GTNH_PACK_VERSION! Download not possible!" exit 1 fi log "Server files located! Will proceed update $gtnh_download_path." # Decide if update or install is needed or not. if [[ ! -f /data/.gtnh-version || "$(basename "$gtnh_download_path")" != "$(cat /data/.gtnh-version)" ]]; then log "Update/Install required: /data/.gtnh-version is missing or does not match the selected version $(basename "$gtnh_download_path")." mkdir -p /data/packs log "Downloading $gtnh_download_path." if ! gtnh_download=$(mc-image-helper get -o /data/packs --output-filename --skip-up-to-date "$gtnh_download_path"); then logError "Failed to download $gtnh_download_path" exit 1 fi # Unpacking Server files into temporary directory log "Unpacking Server Files..." original_base_dir=/data/.tmp/gtnh_base base_dir=$original_base_dir rm -rf "${base_dir}" mkdir -p "${base_dir}" extract "${gtnh_download}" "${base_dir}" trap 'rm -rf /data/.tmp' EXIT # Removing downloaded zip rm -f "$gtnh_download" # Remove any eula file since container manages it rm -f "${base_dir}/eula.txt" # recalculate the actual base directory of content if ! base_dir=$(mc-image-helper find \ --max-depth=3 --type=directory --name=mods,config \ --only-shallowest --fail-no-matches --format '%h' \ "$base_dir"); then logError "Unable to find content base of downloaded Server Files" exit 1 fi # Split installation from update path. Check for version file. if [[ -f /data/.gtnh-version ]]; then log ".gtnh-version file detected! Assuming old version already exists. Proceed updating existing server..." updateGTNH else log "No .gtnh-version file detected! Assuming no old server exists. Proceed installing new server..." cp -R -f "${base_dir}"/* /data fi # Update .gtnh-version basename "$gtnh_download_path" > /data/.gtnh-version # Cleaning up rm -rf "$original_base_dir" else log "No update required: /data/.gtnh-version matches the selected version $(basename "$gtnh_download_path")." fi else log "SKIP_GTNH_UPDATE_CHECK=$SKIP_GTNH_UPDATE_CHECK ... Skipping GTNH Update/Install" fi } # Set server.properties defaults suitable for gtnh servers log "Applying GTNH optimized server defaults" export ALLOW_FLIGHT="${ALLOW_FLIGHT:=true}" export LEVEL_TYPE="${LEVEL_TYPE:=rwg}" export DIFFICULTY="${DIFFICULTY:=3}" export ENABLE_COMMAND_BLOCK="${ENABLE_COMMAND_BLOCK:=true}" export MOTD="${MOTD:="Greg Tech New Horizons $GTNH_PACK_VERSION"}" debug "Set MOTD=$MOTD, ENABLE_COMMAND_BLOCK=$ENABLE_COMMAND_BLOCK, DIFFICULTY=$DIFFICULTY, LEVEL_TYPE=$LEVEL_TYPE, ALLOW_FLIGHT=$ALLOW_FLIGHT" isDebugging && set -x ensureRemoveAllModsOff "MODPACK_PLATFORM=GTNH" java_version=$(mc-image-helper java-release) if (( java_version == 8 )); then export SERVER=/data/forge-1.7.10-10.13.4.1614-1.7.10-universal.jar elif (( java_version >= 17 )); then export SERVER=/data/lwjgl3ify-forgePatches.jar else logError "Greg Tech New Horizons only supports the following Java versions: 8, 17 or later" exit 1 fi log "TYPE=GTNH, setting Minecraft version to 1.7.10" export VERSION=1.7.10 # Start setup gtnh server files handleGTNH export USES_MODS=true exec "$(dirname "$0")/start-setupWorld" "$@" ================================================ FILE: scripts/start-deployKetting ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" isDebugging && set -x if ! SERVER=$(mc-image-helper github download-latest-asset \ --output-directory=/data \ --name-pattern="kettinglauncher-.+?(? /data/settings.yml # # NanoLimbo configuration # # Server's host address and port. Set ip empty to use public address bind: ip: '0.0.0.0' port: ${SERVER_PORT} # Max number of players can join to server # Set -1 to make it infinite maxPlayers: 100 # Server's data in servers list ping: description: '{"text": "&9NanoLimbo"}' version: 'NanoLimbo' # Return static protocol version number in ping result # By default, its -1 to return the client version if it supported # https://wiki.vg/Protocol_version_numbers protocol: -1 # Available dimensions: OVERWORLD, NETHER, THE_END dimension: THE_END # Whether to display the player in the player list # For 1.16.5 clients, the player list will be sent even if disabled, to avoid crash playerList: enable: false username: 'NanoLimbo' # Whether to display header and footer in player list # For 1.8+ clients headerAndFooter: enable: false header: '{"text": "&eWelcome!"}' footer: '{"text": "&9NanoLimbo"}' # Setup player's game mode # 0 - Survival # 1 - Creative (hide HP and food bar) # 2 - Adventure # 3 - Spectator (hide all UI bars) # Spectator works on 1.8+ clients gameMode: 3 # Remove secure-chat toast # For 1.20.5+ clients secureProfile: false # Server name which is shown under F3 # For 1.13+ clients brandName: enable: true content: 'NanoLimbo' # Message sends when player joins to the server joinMessage: enable: true text: '{"text": "&eWelcome to the Limbo!"}' # BossBar displays when player joins to the server # For 1.9+ clients bossBar: enable: true text: '{"text": "Welcome to the Limbo!"}' health: 1.0 # Available colors: PINK, BLUE, RED, GREEN, YELLOW, PURPLE, WHITE color: PINK # Available divisions: SOLID, DASHES_6, DASHES_10, DASHES_12, DASHES_20 division: SOLID # Display title and subtitle # For 1.8+ clients title: enable: true # Set title text value empty, if you need only subtitle title: '{"text": "&9&lWelcome!"}' # Set subtitle text value empty, if you need only title subtitle: '{"text": "&6NanoLimbo"}' # Fade in time in ticks (1 sec = 20 ticks) fadeIn: 10 # Stay time in ticks stay: 100 # Fade out time in ticks fadeOut: 10 # Player info forwarding support. # Available types: # - NONE # - LEGACY # - MODERN # - BUNGEE_GUARD # Don't use secret if you do not use MODERN type infoForwarding: type: NONE secret: '' tokens: - '' # Read timeout for connections in milliseconds readTimeout: 30000 # Define log level. For production, I'd recommend to use level 2 # Log levels: # 0 - Display only errors # 1 - Display errors, warnings # 2 - Display errors, warnings, info # 3 - Display errors, warnings, info, debug debugLevel: 2 # Warning! Do not touch params of this block if you are not completely sure what is this! netty: # Use a Linux native transport type, if it possible useEpoll: true # EventLoopGroup threads count threads: bossGroup: 1 workerGroup: 4 # Options to check incoming traffic and kick potentially malicious connections. # Take into account that player can send many small packets, for example, just moving mouse. traffic: # If true, then additional handler will be added to the channel pipeline enable: true # Max packet size in bytes # Unlimited if -1 maxPacketSize: 8192 # The interval to measure packets over # Lowering this value will limit peak packets from players which would target people with bad connections # Raising this value will allow higher peak packet rates, which will help with people who have poor connections # Ignored if -1.0 interval: 7.0 # The maximum packets per second for players # It is measured over the configured interval # Ignored if -1.0 maxPacketRate: 500.0 # The maximum packet bytes per second for players # It is measured over the configured interval as an average bytes/sec # Ignored if -1.0 maxPacketBytesRate: 2048.0 EOF fi mc-image-helper patch --patch-env-prefix "" /image/nanolimbo-settings-patch.json export SERVER export FAMILY=LIMBO exec "$(dirname "$0")/start-setupMounts" "$@" ================================================ FILE: scripts/start-deployNeoForge ================================================ #!/bin/bash : "${NEOFORGE_VERSION:=latest}" : "${NEOFORGE_FORCE_REINSTALL:=false}}" : "${NEOFORGE_INSTALLER:=}" # shellcheck source=start-utils . "${SCRIPTS:-$(dirname "$0")}/start-utils" isDebugging && set -x resultsFile=/data/.run-neoforge.env if [[ ${NEOFORGE_INSTALLER} ]]; then 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 logError "Failed to installForge given installer ${NEOFORGE_INSTALLER}" exit 1 fi elif ! mc-image-helper install-neoforge \ --output-directory=/data \ --results-file=${resultsFile} \ --minecraft-version="${VERSION}" \ --neoforge-version="${NEOFORGE_VERSION}" \ --force-reinstall="${NEOFORGE_FORCE_REINSTALL}"; then logError "Failed to install NeoForge" exit 1 fi applyResultsFile ${resultsFile} export FAMILY=FORGE exec "$(dirname "$0")/start-setupWorld" "$@" ================================================ FILE: scripts/start-deployPaper ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" set -o pipefail handleDebugMode : "${PAPER_PROJECT:=paper}" : "${PAPER_BUILD:=${PAPERBUILD:-}}" : "${PAPER_CHANNEL:=default}" : "${PAPER_DOWNLOAD_URL:=}" : "${PAPER_CUSTOM_JAR:=}" : "${PAPER_CONFIG_DEFAULTS_REPO:=${PAPER_CONFIG_REPO:=https://raw.githubusercontent.com/Shonz1/minecraft-default-configs/main}}" resultsFile=/data/.paper.env if [[ $PAPER_CUSTOM_JAR ]]; then export SERVER="$PAPER_CUSTOM_JAR" elif [[ $PAPER_DOWNLOAD_URL ]]; then if ! mc-image-helper install-paper \ --output-directory=/data \ --results-file="$resultsFile" \ --url="$PAPER_DOWNLOAD_URL"; then logError "Failed to download from custom PaperMC URL" exit 1 fi applyResultsFile ${resultsFile} else args=( --output-directory=/data --results-file="$resultsFile" --project="$PAPER_PROJECT" --version="$VERSION" --channel="$PAPER_CHANNEL" ) if [[ $PAPER_BUILD ]]; then args+=(--build="$PAPER_BUILD") fi if ! mc-image-helper install-paper "${args[@]}"; then logError "Failed to download $PAPER_PROJECT" exit 1 fi applyResultsFile ${resultsFile} fi defaultTopLevelConfigs="bukkit.yml spigot.yml" if versionLessThan 1.19; then defaultTopLevelConfigs+=" paper.yml" else # Download default configs to allow for consistent patching DOWNLOAD_DEFAULT_CONFIGS=$(buildDownloadList "$PAPER_CONFIG_DEFAULTS_REPO" "$VERSION" paper-global.yml paper-world-defaults.yml) export DOWNLOAD_DEFAULT_CONFIGS fi # Download top-level configs to allow for consistent patching DOWNLOAD_DEFAULTS=$(buildDownloadList "$PAPER_CONFIG_DEFAULTS_REPO" "$VERSION" $defaultTopLevelConfigs) export DOWNLOAD_DEFAULTS # Normalize on Spigot for downstream operations export FAMILY=SPIGOT exec "$(dirname "$0")/start-spiget" "$@" ================================================ FILE: scripts/start-deployPoseidon ================================================ #!/bin/bash # shellcheck source=start-utils . "${SCRIPTS:-$(dirname "$0")}/start-utils" set -o pipefail set -e isDebugging && set -x resolveVersion if [ "${VERSION}" != "b1.7.3" ]; then logError "Poseidon server type only supports VERSION=b1.7.3" exit 1 fi # : "${POSEIDON_RELEASE:=latest}" placeholder until I figure out how to add version selection with mc-image-helper : "${POSEIDON_TYPE:=poseidon}" if [[ ${POSEIDON_TYPE^^} = UBERBUKKIT ]]; then poseidonRepo="Moresteck/uberbukkit" else poseidonRepo="retromcorg/Project-Poseidon" fi if ! SERVER=$(mc-image-helper github download-latest-asset \ --output-directory=/data \ --name-pattern="^(?!original-).+\.jar" \ ${poseidonRepo} ); then logError "Failed to download ${POSEIDON_TYPE}" exit 1 fi export SERVER export FAMILY=SPIGOT exec "$(dirname "$0")/start-spiget" "$@" ================================================ FILE: scripts/start-deployPufferfish ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" set -euo pipefail isDebugging && set -x IFS=$'\n\t' if versionLessThan 1.17; then 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" exit 1 fi : "${PUFFERFISH_BUILD:=lastSuccessfulBuild}" majorVersion=$(get_major_version "$VERSION") PUFFERFISH_BUILD_JSON=$(curl -X GET -s "https://ci.pufferfish.host/job/Pufferfish-${majorVersion}/${PUFFERFISH_BUILD}/api/json") # Example: "url": "https://ci.pufferfish.host/job/Pufferfish-1.18/50/", PUFFERFISH_BUILD_URL=$(jq -n "$PUFFERFISH_BUILD_JSON" | jq -jc '.url // empty' ) # Example: "fileName": "pufferfish-paperclip-1.18.2-R0.1-SNAPSHOT-reobf.jar", PUFFERFISH_BUILD_FILENAME=$(jq -n "$PUFFERFISH_BUILD_JSON" | jq -jc '.artifacts[].fileName // empty' ) # Example: "relativePath": "pufferfish-server/build/libs/pufferfish-paperclip-1.21.7-R0.1-SNAPSHOT-mojmap.jar", PUFFERFISH_BUILD_PATH=$(jq -n "$PUFFERFISH_BUILD_JSON" | jq -jc '.artifacts[].relativePath // empty' ) PUFFERFISH_BUILD_DOWNLOAD_URL="${PUFFERFISH_BUILD_URL}artifact/${PUFFERFISH_BUILD_PATH}" # Setting server to the Jar filename for export. export SERVER=$PUFFERFISH_BUILD_FILENAME log "Removing old Pufferfish versions ..." shopt -s nullglob for f in pufferfish-*.jar; do [[ $f != "$SERVER" ]] && rm "$f" done if [[ ! -f "$SERVER" ]] || isTrue "${FORCE_REDOWNLOAD:-false}"; then log "Downloading Pufferfish from $PUFFERFISH_BUILD_DOWNLOAD_URL ..." if ! get -o "$SERVER" "$PUFFERFISH_BUILD_DOWNLOAD_URL"; then logError "Failed to download from $PUFFERFISH_BUILD_DOWNLOAD_URL (status=$?)" exit 3 fi fi if [[ "${PUFFERFISH_CONFIG_REPO}" ]]; then defaultTopLevelConfigs=(bukkit.yml spigot.yml pufferfish.yml) DOWNLOAD_DEFAULTS=$(buildDownloadList "$PUFFERFISH_CONFIG_REPO" "$VERSION" "${defaultTopLevelConfigs[@]}") export DOWNLOAD_DEFAULTS fi # Normalize on Spigot for later operations export FAMILY=SPIGOT exec "$(dirname "$0")/start-spiget" "$@" ================================================ FILE: scripts/start-deployPurpur ================================================ #!/bin/bash set -euo pipefail IFS=$'\n\t' : "${PURPUR_BUILD:=LATEST}" : "${PURPUR_DOWNLOAD_URL:=}" : "${PURPUR_CONFIG_REPO:=}" # shellcheck source=start-utils . "$(dirname "$0")/start-utils" isDebugging && set -x resultsFile=/data/.purpur.env if [[ $PURPUR_DOWNLOAD_URL ]]; then if ! mc-image-helper install-purpur \ --output-directory=/data \ --results-file="$resultsFile" \ --url="${PURPUR_DOWNLOAD_URL}"; then logError "Failed to download from custom Purpur URL" exit 1 fi else args=( --output-directory=/data --results-file="$resultsFile" --version="$VERSION" ) if [[ $PURPUR_BUILD ]]; then args+=(--build="$PURPUR_BUILD") fi if ! mc-image-helper install-purpur "${args[@]}"; then logError "Failed to download Purpur" exit 1 fi fi applyResultsFile ${resultsFile} if [[ "${PURPUR_CONFIG_REPO}" ]]; then defaultTopLevelConfigs=(bukkit.yml spigot.yml purpur.yml) DOWNLOAD_DEFAULTS=$(buildDownloadList "$PURPUR_CONFIG_REPO" "$VERSION" "${defaultTopLevelConfigs[@]}") export DOWNLOAD_DEFAULTS fi # Normalize on Spigot for later operations export FAMILY=SPIGOT exec "$(dirname "$0")/start-spiget" "$@" ================================================ FILE: scripts/start-deployQuilt ================================================ #!/bin/bash set -eu # shellcheck source=start-utils . "$(dirname "$0")/start-utils" : "${QUILT_LAUNCHER:=}" : "${QUILT_LAUNCHER_URL:=}" : "${QUILT_INSTALLER_URL:=}" : "${QUILT_INSTALLER_VERSION:=LATEST}" : "${QUILT_LOADER_VERSION:=LATEST}" isDebugging && set -x function mc-image-helper-quilt(){ mc-image-helper install-quilt \ --loader-version="$QUILT_LOADER_VERSION" \ --minecraft-version="$VERSION" \ --output-directory=/data \ --results-file="$resultsFile" "$@" } resultsFile=/data/.quilt.env if [[ $QUILT_LAUNCHER ]]; then logWarning "Use of QUILT_LAUNCHER is a deprecated feature." SERVER="$QUILT_LAUNCHER" export SERVER resolveVersion export FAMILY=FABRIC exec "$(dirname "$0")/start-setupWorld" "$@" elif [[ $QUILT_LAUNCHER_URL ]]; then logError "QUILT_LAUNCHER_URL is not longer supported. Pre-download and use QUILT_LAUNCHER." exit 2 elif [[ $QUILT_INSTALLER_URL ]]; then if ! mc-image-helper-quilt --installer-url="$QUILT_INSTALLER_URL"; then logError "Failed to installQuilt given custom installer URL $QUILT_INSTALLER_URL" exit 2 fi else if ! mc-image-helper-quilt --installer-version="$QUILT_INSTALLER_VERSION"; then logError "Failed to installQuilt given installer version $QUILT_INSTALLER_VERSION" exit 2 fi fi applyResultsFile ${resultsFile} export FAMILY=FABRIC exec "$(dirname "$0")/start-setupWorld" "$@" ================================================ FILE: scripts/start-deploySpongeVanilla ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" export TYPE=spongevanilla : ${SPONGEBRANCH:=STABLE} : ${SPONGEVERSION:=} # Parse branch log "Choosing branch for Sponge" case "$SPONGEBRANCH" in EXPERIMENTAL|experimental|BLEEDING|bleeding) SPONGEBRANCH=bleeding ;; *) SPONGEBRANCH=stable ;; esac # If not SPONGEVERSION selected, detect last version on selected branch if [ -z $SPONGEVERSION ]; then log "Choosing Version for Sponge" SPONGEVERSION=$(curl -fsSL https://dl-api.spongepowered.org/v1/org.spongepowered/$TYPE | jq -r --arg SPONGEBRANCH "$SPONGEBRANCH" '.buildTypes.$SPONGEBRANCH.latest.version') fi VERSION="$SPONGEVERSION" export VERSION export SERVER="spongevanilla-$SPONGEVERSION.jar" if [ ! -e "$SERVER" ] || [ -n "$FORCE_REDOWNLOAD" ]; then log "Downloading $SERVER ..." curl -sSL -o "$SERVER" "https://repo.spongepowered.org/maven/org/spongepowered/$TYPE/$SPONGEVERSION/$SERVER" fi export FAMILY=SPONGE exec "$(dirname "$0")/start-setupWorld" "$@" ================================================ FILE: scripts/start-deployVanilla ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" isDebugging && set -x set -o pipefail resolveVersion export SERVER="minecraft_server.${VERSION// /_}.jar" if [ ! -e "$SERVER" ] || [ -n "$FORCE_REDOWNLOAD" ]; then debug "Finding version manifest for $VERSION" versionManifestUrl=$(get 'https://launchermeta.mojang.com/mc/game/version_manifest.json' | jq --arg VERSION "$VERSION" --raw-output '[.versions[]|select(.id == $VERSION)][0].url') result=$? if [ $result != 0 ]; then logError "Failed to obtain version manifest URL ($result)" exit 1 fi if [ "$versionManifestUrl" = "null" ]; then logError "Couldn't find a matching manifest entry for $VERSION" exit 1 fi debug "Found version manifest at $versionManifestUrl" if ! serverDownloadUrl=$(get --json-path '$.downloads.server.url' "${versionManifestUrl}"); then logError "Failed to obtain version manifest from $versionManifestUrl ($result)" exit 1 elif [ "$serverDownloadUrl" = "null" ]; then logError "There is not a server download for version $VERSION" exit 1 fi log "Downloading $VERSION server..." debug "Downloading server from $serverDownloadUrl" get -o "$SERVER" "$serverDownloadUrl" result=$? if [ $result != 0 ]; then logError "Failed to download server from $serverDownloadUrl ($result)" exit 1 fi fi minecraftServerJarPath=/data/minecraft_server.jar if versionLessThan 1.6; then if ! [[ -L $minecraftServerJarPath && $minecraftServerJarPath -ef "/data/$SERVER" ]]; then rm -f $minecraftServerJarPath ln -s "/data/$SERVER" $minecraftServerJarPath fi SERVER=minecraft_server.jar elif [[ -L $minecraftServerJarPath ]]; then rm -f $minecraftServerJarPath fi isDebugging && ls -l export FAMILY=VANILLA exec "$(dirname "$0")/start-setupWorld" "$@" ================================================ FILE: scripts/start-finalExec ================================================ #!/bin/bash : "${DEBUG_EXEC:=false}" : "${SETUP_ONLY:=false}" : "${CUSTOM_JAR_EXEC:=}" # shellcheck source=start-utils . "$(dirname "$0")/start-utils" isDebugging && set -x baseDataDir=/data tmpServerIconPath=/tmp/icon.img serverIconPath=${baseDataDir}/server-icon.png mcHealthEnvPath=${baseDataDir}/.mc-health.env bootstrapPath=${baseDataDir}/bootstrap.txt if [ -n "$ICON" ]; then if [ ! -e server-icon.png ] || isTrue "${OVERRIDE_ICON}"; then log "Using server icon from $ICON..." if isURL "$ICON"; then # Not sure what it is yet...call it "img" if ! get -o "$tmpServerIconPath" "$ICON"; then logError "Failed to download icon from $ICON" exit 1 fi ICON="$tmpServerIconPath" iconSrc="url" elif [ -f "$ICON" ]; then iconSrc="file" else logError "$ICON does not appear to be a URL or existing file" exit 1 fi read -r -a specs < <(identify "$ICON" | awk 'NR == 1 { print $2, $3 }') if [ "${specs[0]} ${specs[1]}" = "PNG 64x64" ]; then if [ $iconSrc = url ]; then mv -f "$tmpServerIconPath" "$serverIconPath" else cp -f "$ICON" "$serverIconPath" fi elif [ "${specs[0]}" = GIF ]; then log "Converting GIF image to 64x64 PNG..." convert "$ICON"[0] -resize 64x64! "$serverIconPath" else log "Converting image to 64x64 PNG..." convert "$ICON" -resize 64x64! "$serverIconPath" fi fi fi useGeneratedLogs=${GENERATE_LOG4J2_CONFIG:-${ENABLE_ROLLING_LOGS:-false}} useFallbackJvmFlag=false SERVER_DIR="$baseDataDir" if [[ ${FTB_DIR:-} ]]; then SERVER_DIR="$FTB_DIR" fi patchLog4jConfig() { file=${1?} url=${2?} if ! get -o "${SERVER_DIR}/${file}" "$url"; then logError "Failed to download corrected log4j config, fallback to JVM flag" useFallbackJvmFlag=true return 1 fi JVM_OPTS="-Dlog4j.configurationFile=${file} ${JVM_OPTS}" useGeneratedLogs=false } # Temporarily disable debugging output oldState=$(shopt -po xtrace || true) shopt -u -o xtrace # Patch Log4j remote code execution vulnerability # See https://www.minecraft.net/en-us/article/important-message--security-vulnerability-java-edition if versionLessThan 1.7; then : # No patch required here. elif isFamily VANILLA && versionLessThan 1.12; then patchLog4jConfig log4j2_17-111.xml https://launcher.mojang.com/v1/objects/dd2b723346a8dcd48e7f4d245f6bf09e98db9696/log4j2_17-111.xml elif isFamily VANILLA && versionLessThan 1.17; then patchLog4jConfig log4j2_112-116.xml https://launcher.mojang.com/v1/objects/02937d122c86ce73319ef9975b58896fc1b491d1/log4j2_112-116.xml # See https://purpurmc.org/docs/Log4j/ elif isType PURPUR && versionLessThan 1.17; then patchLog4jConfig purpur_log4j2_1141-1165.xml https://purpurmc.org/docs/xml/purpur_log4j2_1141-1165.xml elif isType PURPUR && versionLessThan 1.18.1; then patchLog4jConfig purpur_log4j2_117.xml https://purpurmc.org/docs/xml/purpur_log4j2_117.xml elif versionLessThan 1.18.1; then useFallbackJvmFlag=true fi eval "$oldState" if ${useFallbackJvmFlag}; then JVM_OPTS="-Dlog4j2.formatMsgNoLookups=true ${JVM_OPTS}" fi if versionLessThan 1.7; then : # No patch required here. elif versionLessThan 1.18.1; then if isTrue ${SKIP_LOG4J_PATCHER:-false}; then log "Skipping Log4jPatcher, make sure you are not affected" else JVM_OPTS="-javaagent:/image/Log4jPatcher.jar ${JVM_OPTS}" fi fi # Set up log4j2 configuration with templating support LOGFILE="${SERVER_DIR}/log4j2.xml" if ${useGeneratedLogs}; then # Set up log configuration defaults : "${LOG_LEVEL:=info}" : "${ROLLING_LOG_MAX_FILES:=1000}" # Note: Can't use ${VAR:=default} syntax for values containing } as it breaks parsing if [ -z "${ROLLING_LOG_FILE_PATTERN}" ]; then ROLLING_LOG_FILE_PATTERN='logs/%d{yyyy-MM-dd}-%i.log.gz' fi # Pattern format defaults (compatible with vanilla Minecraft) # Note: Can't use ${VAR:=default} syntax because } in the value breaks parsing if [ -z "${LOG_CONSOLE_FORMAT}" ]; then LOG_CONSOLE_FORMAT='[%d{HH:mm:ss}] [%t/%level]: %msg%n' fi if [ -z "${LOG_TERMINAL_FORMAT}" ]; then LOG_TERMINAL_FORMAT='[%d{HH:mm:ss} %level]: %msg%n' fi if [ -z "${LOG_FILE_FORMAT}" ]; then LOG_FILE_FORMAT='[%d{HH:mm:ss}] [%t/%level]: %msg%n' fi export LOG_LEVEL ROLLING_LOG_FILE_PATTERN ROLLING_LOG_MAX_FILES export LOG_CONSOLE_FORMAT LOG_TERMINAL_FORMAT LOG_FILE_FORMAT # Always regenerate if file doesn't exist if [ ! -e "$LOGFILE" ] || isTrue "${REGENERATE_LOG4J2:-true}"; then log "Generating log4j2.xml from template in ${LOGFILE}" # Generate log4j2.xml using heredoc for reliable variable substitution cat > "$LOGFILE" < EOF else log "log4j2.xml already exists and is up to date, skipping generation" fi # Apply the log4j2 configuration JVM_OPTS="-Dlog4j.configurationFile=log4j2.xml ${JVM_OPTS}" else rm -f "${LOGFILE}" fi # Optional disable console if versionLessThan 1.14 && [[ ${CONSOLE,,} = false ]]; then EXTRA_ARGS+=" --noconsole" fi # Optional disable GUI for headless servers if [[ ${GUI,,} = false ]]; then EXTRA_ARGS+=" nogui" fi expandedDOpts= if [ -n "$JVM_DD_OPTS" ]; then for dopt in $JVM_DD_OPTS do expandedDOpts="${expandedDOpts} -D${dopt/:/=}" done fi if isTrue "${ENABLE_JMX}"; then : "${JMX_PORT:=7091}" JVM_OPTS="${JVM_OPTS} -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.port=${JMX_PORT} -Dcom.sun.management.jmxremote.rmi.port=${JMX_PORT} -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.host=${JMX_BINDING:-0.0.0.0} -Djava.rmi.server.hostname=${JMX_HOST:-localhost}" log "JMX is enabled. Make sure you have port forwarding for ${JMX_PORT}" fi : "${USE_AIKAR_FLAGS:=false}" : "${USE_MEOWICE_FLAGS:=false}" : "${USE_MEOWICE_GRAALVM_FLAGS:=false}" if isTrue "${USE_MEOWICE_FLAGS}"; then java_major_version=$(mc-image-helper java-release) if [[ $java_major_version -gt 16 ]]; then log "Java version $java_major_version using MeowIce's flags for Java 17+" else log "Your Java version is $java_major_version, MeowIce's flags are for Java 17+ falling back to Aikar's" USE_MEOWICE_FLAGS=FALSE USE_AIKAR_FLAGS=TRUE fi fi if isTrue "${USE_AIKAR_FLAGS}" || isTrue "${USE_MEOWICE_FLAGS}"; then # From https://mcflags.emc.gs/ if isTrue "${USE_MEOWICE_FLAGS}"; then log "Using MeowIce's flags" G1NewSizePercent=28 G1MaxNewSizePercent=50 G1HeapRegionSize=16M G1ReservePercent=15 InitiatingHeapOccupancyPercent=20 G1MixedGCCountTarget=3 G1RSetUpdatingPauseTimePercent=0 elif [[ $MAX_MEMORY ]] && (( $(normalizeMemSize "${MAX_MEMORY}") >= $(normalizeMemSize 12g) )); then log "Using Aikar's >12GB flags" G1NewSizePercent=40 G1MaxNewSizePercent=50 G1HeapRegionSize=16M G1ReservePercent=15 InitiatingHeapOccupancyPercent=20 G1MixedGCCountTarget=4 G1RSetUpdatingPauseTimePercent=5 else log "Using Aikar's flags" G1NewSizePercent=30 G1MaxNewSizePercent=40 G1HeapRegionSize=8M G1ReservePercent=20 InitiatingHeapOccupancyPercent=15 G1MixedGCCountTarget=4 G1RSetUpdatingPauseTimePercent=5 fi JVM_XX_OPTS="${JVM_XX_OPTS} -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1NewSizePercent=${G1NewSizePercent} -XX:G1MaxNewSizePercent=${G1MaxNewSizePercent} -XX:G1HeapRegionSize=${G1HeapRegionSize} -XX:G1ReservePercent=${G1ReservePercent} -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=${G1MixedGCCountTarget} -XX:InitiatingHeapOccupancyPercent=${InitiatingHeapOccupancyPercent} -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=${G1RSetUpdatingPauseTimePercent} -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 " if isTrue "${USE_AIKAR_FLAGS}"; then JVM_XX_OPTS="${JVM_XX_OPTS} -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true " fi fi if isTrue "${USE_MEOWICE_FLAGS}"; then JVM_XX_OPTS="${JVM_XX_OPTS} -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:G1SATBBufferEnqueueingThresholdPercent=30 -XX:G1ConcMarkStepDurationMillis=5 -XX:+UseNUMA -XX:-DontCompileHugeMethods -XX:MaxNodeLimit=240000 -XX:NodeLimitFudgeFactor=8000 -XX:ReservedCodeCacheSize=400M -XX:NonNMethodCodeHeapSize=12M -XX:ProfiledCodeHeapSize=194M -XX:NonProfiledCodeHeapSize=194M -XX:NmethodSweepActivity=1 -XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:AllocatePrefetchStyle=3 -XX:+AlwaysActAsServerClassMachine -XX:+UseTransparentHugePages -XX:LargePageSizeInBytes=2M -XX:+UseLargePages -XX:+EagerJVMCI -XX:+UseStringDeduplication -XX:+UseAES -XX:+UseAESIntrinsics -XX:+UseFMA -XX:+UseLoopPredicate -XX:+RangeCheckElimination -XX:+OptimizeStringConcat -XX:+UseCompressedOops -XX:+UseThreadPriorities -XX:+OmitStackTraceInFastThrow -XX:+RewriteBytecodes -XX:+RewriteFrequentPairs -XX:+UseFPUForSpilling -XX:+UseVectorCmov -XX:+UseXMMForArrayCopy -XX:+EliminateLocks -XX:+DoEscapeAnalysis -XX:+AlignVector -XX:+OptimizeFill -XX:+EnableVectorSupport -XX:+UseCharacterCompareIntrinsics -XX:+UseCopySignIntrinsic -XX:+UseVectorStubs " if [[ $(arch) == "x86_64" ]]; then JVM_XX_OPTS="${JVM_XX_OPTS} -XX:+UseFastStosb -XX:+UseNewLongLShift -XX:+UseXmmI2D -XX:+UseXmmI2F -XX:+UseXmmLoadAndClearUpper -XX:+UseXmmRegToRegMoveAll -XX:UseAVX=2 -XX:UseSSE=4 " else log "cpu not x86_64, disabling architecture specific flags" fi fi if isTrue "${USE_MEOWICE_GRAALVM_FLAGS}"; then if [[ $java_major_version -gt 23 ]]; then log "Java 24 or higher detected, using modified GraalVM flags" JVM_XX_OPTS="${JVM_XX_OPTS} -XX:+UseFastJNIAccessors -XX:+UseInlineCaches -XX:+SegmentedCodeCache -Djdk.nio.maxCachedBufferSize=262144 -Djdk.graal.UsePriorityInlining=true -Djdk.graal.Vectorization=true -Djdk.graal.OptDuplication=true -Djdk.graal.DetectInvertedLoopsAsCounted=true -Djdk.graal.LoopInversion=true -Djdk.graal.VectorizeHashes=true -Djdk.graal.EnterprisePartialUnroll=true -Djdk.graal.VectorizeSIMD=true -Djdk.graal.StripMineNonCountedLoops=true -Djdk.graal.SpeculativeGuardMovement=true -Djdk.graal.TuneInlinerExploration=1 -Djdk.graal.LoopRotation=true -Djdk.graal.CompilerConfiguration=enterprise --enable-native-access=ALL-UNNAMED " else log "Using MeowIce's flags for Graalvm" JVM_XX_OPTS="${JVM_XX_OPTS} -XX:+UseFastJNIAccessors -XX:+UseInlineCaches -XX:+SegmentedCodeCache -Djdk.nio.maxCachedBufferSize=262144 -Dgraal.UsePriorityInlining=true -Dgraal.Vectorization=true -Dgraal.OptDuplication=true -Dgraal.DetectInvertedLoopsAsCounted=true -Dgraal.LoopInversion=true -Dgraal.VectorizeHashes=true -Dgraal.EnterprisePartialUnroll=true -Dgraal.VectorizeSIMD=true -Dgraal.StripMineNonCountedLoops=true -Dgraal.SpeculativeGuardMovement=true -Dgraal.TuneInlinerExploration=1 -Dgraal.LoopRotation=true -Dgraal.OptWriteMotion=true -Dgraal.CompilerConfiguration=enterprise " fi fi if isTrue "${USE_FLARE_FLAGS}"; then JVM_XX_OPTS="${JVM_XX_OPTS} -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints " fi if isTrue "${USE_SIMD_FLAGS}"; then JVM_XX_OPTS="${JVM_XX_OPTS} --add-modules=jdk.incubator.vector " fi # Handle GTNH args if isType "GTNH"; then expandedDOpts="${expandedDOpts} -Dfml.readTimeout=180" java_major_version=$(mc-image-helper java-release) if (( java_major_version == 8 )); then debug "Setting GTNH java8 args." JVM_XX_OPTS="${JVM_XX_OPTS} -XX:+UseStringDeduplication -XX:+UseCompressedOops -XX:+UseCodeCacheFlushing " elif (( java_major_version >= 17 )); then debug "Setting GTNH java17+ args." expandedDOpts="${expandedDOpts} @java9args.txt" fi fi if [[ ${INIT_MEMORY} || ${MAX_MEMORY} ]]; then log "Setting initial memory to ${INIT_MEMORY:=${MEMORY}} and max to ${MAX_MEMORY:=${MEMORY}}" if isPercentage "$INIT_MEMORY"; then JVM_OPTS="-XX:InitialRAMPercentage=$(getPercentageValue "${INIT_MEMORY}") ${JVM_OPTS}" elif [[ ${INIT_MEMORY} ]]; then JVM_OPTS="-Xms${INIT_MEMORY} ${JVM_OPTS}" fi if isPercentage "$MAX_MEMORY"; then JVM_OPTS="-XX:MaxRAMPercentage=$(getPercentageValue "${MAX_MEMORY}") ${JVM_OPTS}" elif [[ ${MAX_MEMORY} ]]; then JVM_OPTS="-Xmx${MAX_MEMORY} ${JVM_OPTS}" fi fi function copyFilesForCurseForge() { if [ ! -e "${FTB_DIR}/server-icon.png" ] && [ -e "$serverIconPath" ]; then cp -f "$serverIconPath" "${FTB_DIR}/" fi cp -f ${baseDataDir}/eula.txt "${FTB_DIR}/" } if versionLessThan 'b1.8'; then echo " DISABLE_HEALTHCHECK=true " > "$mcHealthEnvPath" elif versionLessThan 1.3; then echo " MC_HEALTH_EXTRA_ARGS=( --use-old-server-list-ping ) " > "$mcHealthEnvPath" elif versionLessThan 1.7; then echo " MC_HEALTH_EXTRA_ARGS=( --use-server-list-ping ) " > "$mcHealthEnvPath" elif isTrue "$USES_PROXY_PROTOCOL"; then echo " MC_HEALTH_EXTRA_ARGS=( --use-proxy ) " > "$mcHealthEnvPath" else rm -f "$mcHealthEnvPath" fi mcServerRunnerArgs=( --stop-duration "${STOP_DURATION:-60}s" ) if isTrue "${CREATE_CONSOLE_IN_PIPE:-false}"; then mcServerRunnerArgs+=(--named-pipe "${CONSOLE_IN_NAMED_PIPE:-/tmp/minecraft-console-in}") fi if [[ ${STOP_SERVER_ANNOUNCE_DELAY} ]]; then mcServerRunnerArgs+=(--stop-server-announce-delay "${STOP_SERVER_ANNOUNCE_DELAY}s") fi if isTrue "${ENABLE_SSH}"; then mcServerRunnerArgs+=(--remote-console) fi if [[ ${TYPE} == "CURSEFORGE" && "${SERVER}" ]]; then copyFilesForCurseForge cd "${FTB_DIR}" || (logError "Can't go into ${FTB_DIR}"; exit 1) log "Starting CurseForge server in ${FTB_DIR}..." if isTrue "${DEBUG_EXEC}"; then set -x fi exec mc-server-runner ${bootstrapArgs} "${mcServerRunnerArgs[@]}" java $JVM_XX_OPTS $JVM_OPTS $expandedDOpts -jar "$(basename "${SERVER}")" "$@" $EXTRA_ARGS elif [[ ${TYPE} == "CURSEFORGE" ]]; then mcServerRunnerArgs+=(--shell bash) copyFilesForCurseForge if isPercentage "$INIT_MEMORY" || isPercentage "$MAX_MEMORY"; then # Convert to bytes NORM_INIT_MEM=$(normalizeMemSize "$INIT_MEMORY") NORM_MAX_MEM=$(normalizeMemSize "$MAX_MEMORY") # Convert to MB ((NORM_INIT_MEM*=1048576)) ((NORM_MAX_MEM*=1048576)) cat > "${FTB_DIR}/settings-local.sh" < "${FTB_DIR}/settings-local.sh" < user_jvm_args.txt if isTrue ${SETUP_ONLY}; then echo "SETUP_ONLY: bash ${SERVER}" exit fi if isTrue "${DEBUG_EXEC}"; then set -x fi exec mc-server-runner "${mcServerRunnerArgs[@]}" --shell bash "${SERVER}" $EXTRA_ARGS else # If we have a bootstrap.txt file... feed that in to the server stdin if [ -f $bootstrapPath ]; then bootstrapArgs="--bootstrap $bootstrapPath" fi log "Starting the Minecraft server..." # Specifically want the variables to expand to args, so... # shellcheck disable=SC2206 finalArgs=( $JVM_XX_OPTS $JVM_OPTS $expandedDOpts ) if [[ $CUSTOM_JAR_EXEC ]]; then # shellcheck disable=SC2206 finalArgs+=($CUSTOM_JAR_EXEC) else finalArgs+=(-jar "$SERVER") fi # shellcheck disable=SC2206 finalArgs+=( "$@" $EXTRA_ARGS ) if isTrue ${SETUP_ONLY}; then echo "SETUP_ONLY: java ${finalArgs[*]}" exit fi if isTrue "${DEBUG_EXEC}"; then set -x fi exec mc-server-runner ${bootstrapArgs} "${mcServerRunnerArgs[@]}" java "${finalArgs[@]}" fi ================================================ FILE: scripts/start-rconcmds ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" : "${RCON_CMDS_STARTUP:=}" : "${RCON_CMDS_ON_CONNECT:=}" : "${RCON_CMDS_ON_DISCONNECT:=}" : "${RCON_CMDS_FIRST_CONNECT:=}" : "${RCON_CMDS_LAST_DISCONNECT:=}" : "${RCON_CMDS_PERIOD:=10}" : "${SERVER_PORT:=25565}" export RCON_CMDS_STARTUP export RCON_CMDS_ON_CONNECT export RCON_CMDS_ON_DISCONNECT export RCON_CMDS_FIRST_CONNECT export RCON_CMDS_LAST_DISCONNECT export RCON_CMDS_PERIOD export SERVER_PORT log "Rcon cmds functionality enabled" isDebugging && set -x isNumericElseSetToDefault RCON_CMDS_PERIOD 10 checkIfNotZeroElseSetToDefault RCON_CMDS_PERIOD 10 "$(dirname "$0")/auto/rcon-cmds-daemon.sh" & ================================================ FILE: scripts/start-setupDatapack ================================================ #!/bin/bash set -e -o pipefail : "${VANILLATWEAKS_FILE:=}" : "${VANILLATWEAKS_SHARECODE:=}" : "${REMOVE_OLD_DATAPACKS:=false}" : "${DATAPACKS_FILE:=}" : "${REMOVE_OLD_DATAPACKS_DEPTH:=1} " : "${REMOVE_OLD_DATAPACKS_INCLUDE:=*.zip}" # shellcheck source=start-utils . "$(dirname "$0")/start-utils" isDebugging && set -x out_dir=/data/${LEVEL:-world}/datapacks # Remove old datapacks if isTrue "${REMOVE_OLD_DATAPACKS}" && [ -z "${DATAPACKS_FILE}" ]; then if [ -d "$out_dir" ]; then find "$out_dir" -mindepth 1 -maxdepth ${REMOVE_OLD_DATAPACKS_DEPTH:-16} -wholename "${REMOVE_OLD_DATAPACKS_INCLUDE:-*}" -not -wholename "${REMOVE_OLD_DATAPACKS_EXCLUDE:-}" -delete fi fi if [[ "$DATAPACKS" ]]; then mkdir -p "$out_dir" for i in ${DATAPACKS//,/ } do if isURL "$i"; then log "Downloading datapack $i ..." if ! get -o "${out_dir}" "$i"; then logError "Failed to download from $i into $out_dir" exit 2 fi elif [[ -f "$i" && "$i" =~ .*\.zip ]]; then log "Copying datapack located at $i ..." out_file=$(basename "$i") if ! cp "$i" "${out_dir}/$out_file"; then logError "Failed to copy from $i into $out_dir" exit 2 fi elif [[ -d "$i" ]]; then if [[ -f "$i/pack.mcmeta" ]]; then log "Copying datapack from $i" cp -r "$i" "${out_dir}" else log "Copying datapacks from $i ..." cp "$i"/*.zip "${out_dir}" fi else logError "Invalid URL or path given in DATAPACKS: $i" exit 2 fi done elif [[ "$DATAPACKS_FILE" ]]; then if [ ! -f "$DATAPACKS_FILE" ]; then logError "Given DATAPACKS_FILE file does not exist" exit 2 fi mkdir -p "$out_dir" args=( -o "${out_dir}" --log-progress-each --skip-existing --uris-file "${DATAPACKS_FILE}" ) if isTrue "${REMOVE_OLD_DATAPACKS}"; then args+=( --prune-others "${REMOVE_OLD_DATAPACKS_INCLUDE}" --prune-depth "${REMOVE_OLD_DATAPACKS_DEPTH}" ) fi if ! get "${args[@]}" ; then logError "Failed to retrieve one or more datapacks" exit 1 fi fi if [[ ${VANILLATWEAKS_FILE} || ${VANILLATWEAKS_SHARECODE} ]]; then mc-image-helper vanillatweaks \ --output-directory="/data" \ --world-subdir="${LEVEL:-world}" \ --share-codes="$VANILLATWEAKS_SHARECODE" \ --pack-files="$VANILLATWEAKS_FILE" fi exec "$(dirname "$0")/start-setupModpack" "$@" ================================================ FILE: scripts/start-setupEnvVariables ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" set -e handleDebugMode : "${REPLACE_ENV_IN_PLACE:=${REPLACE_ENV_VARIABLES:-false}}" : "${REPLACE_ENV_PATHS:=/data}" : "${REPLACE_ENV_SUFFIXES:=yml,yaml,txt,cfg,conf,properties,hjson,json,tml,toml}" : "${REPLACE_ENV_VARIABLE_PREFIX=${ENV_VARIABLE_PREFIX:-CFG_}}" : "${REPLACE_ENV_VARIABLES_EXCLUDES:=}" : "${REPLACE_ENV_VARIABLES_EXCLUDE_PATHS:=}" : "${PATCH_DEFINITIONS:=}" : "${DEBUG:=false}" : "${DOWNLOAD_DEFAULTS:=}" : "${DOWNLOAD_DEFAULT_CONFIGS:=}" : "${SKIP_DOWNLOAD_DEFAULTS:=false}" : "${APPLY_EXTRA_FILES:=${DOWNLOAD_EXTRA_CONFIGS:-}}" if isTrue "${REPLACE_ENV_IN_PLACE}"; then log "Replacing env variables in ${REPLACE_ENV_PATHS} that match the prefix '$REPLACE_ENV_VARIABLE_PREFIX' ..." mc-image-helper interpolate \ --replace-env-file-suffixes="${REPLACE_ENV_SUFFIXES}" \ --replace-env-excludes="${REPLACE_ENV_VARIABLES_EXCLUDES}" \ --replace-env-exclude-paths="${REPLACE_ENV_VARIABLES_EXCLUDE_PATHS}" \ --replace-env-prefix="${REPLACE_ENV_VARIABLE_PREFIX}" \ "${REPLACE_ENV_PATHS[@]}" fi if isFalse "$SKIP_DOWNLOAD_DEFAULTS"; then commonArgs=( --skip-existing --skip-up-to-date=false --quiet-when-skipped --ignore-missing-sources ) if [[ $DOWNLOAD_DEFAULT_CONFIGS ]]; then if ! mc-image-helper mcopy \ --to /data/config \ "${commonArgs[@]}" \ "$DOWNLOAD_DEFAULT_CONFIGS"; then logWarning "One or more default config files were not available from $DOWNLOAD_DEFAULT_CONFIGS" fi fi if [[ $DOWNLOAD_DEFAULTS ]]; then if ! mc-image-helper mcopy \ --to /data \ "${commonArgs[@]}" \ "$DOWNLOAD_DEFAULTS"; then logWarning "One or more default files were not available from $DOWNLOAD_DEFAULTS" fi fi fi if [[ $APPLY_EXTRA_FILES ]]; then mc-image-helper mcopy \ --to /data \ --skip-existing \ --skip-up-to-date=false \ --quiet-when-skipped \ "$APPLY_EXTRA_FILES" fi if [[ ${PATCH_DEFINITIONS} ]]; then log "Applying patch definitions from ${PATCH_DEFINITIONS}" mc-image-helper patch \ --patch-env-prefix="${REPLACE_ENV_VARIABLE_PREFIX}" \ "${PATCH_DEFINITIONS}" fi exec "$(dirname "$0")/start-setupRbac" "$@" ================================================ FILE: scripts/start-setupModpack ================================================ #!/bin/bash set -e -o pipefail : "${REMOVE_OLD_MODS:=false}" : "${MODS_OUT_DIR:=/data/mods}" : "${MODS_FILE:=}" : "${PLUGINS_OUT_DIR:=/data/plugins}" : "${PLUGINS_FILE:=}" : "${REMOVE_OLD_MODS_DEPTH:=1} " : "${REMOVE_OLD_MODS_INCLUDE:=*.jar,*-version.json}" : "${CF_API_KEY_FILE:=}" # Path to file containing CurseForge API key : "${MODRINTH_LOADER:=}" # shellcheck source=start-utils . "$(dirname "$0")/start-utils" isDebugging && set -x if [[ -n ${CF_API_KEY_FILE} ]]; then if [[ -r "${CF_API_KEY_FILE}" ]]; then CF_API_KEY="$(cat "${CF_API_KEY_FILE}")" export CF_API_KEY else logError "CF_API_KEY_FILE is not readable: ${CF_API_KEY_FILE}" exit 1 fi fi sum_file=/data/.generic_pack.sum # Remove old mods/plugins if isTrue "${REMOVE_OLD_MODS}" && [ -z "${MODS_FILE}" ]; then removeOldMods "$MODS_OUT_DIR" removeOldMods "$PLUGINS_OUT_DIR" rm -f "$sum_file" fi function handlePackwiz() { # If packwiz url passed, bootstrap packwiz and update mods before other modpack processing if [[ "${PACKWIZ_URL:-}" ]]; then if ! packwizInstaller=$(mc-image-helper maven-download \ --maven-repo=https://maven.packwiz.infra.link/repository/release/ \ --group=link.infra.packwiz --artifact=packwiz-installer --classifier=dist \ --skip-existing); then logError "Failed to get packwiz installer" exit 1 fi log "Running packwiz installer against URL: ${PACKWIZ_URL}" if ! java -cp "${packwizInstaller}" link.infra.packwiz.installer.Main -s server "${PACKWIZ_URL}"; then logError "Failed to run packwiz installer" exit 1 fi fi } function handleModpackZip() { # If supplied with a URL for a modpack (simple zip of jars), download it and unpack if [[ "$MODPACK" ]]; then if isURL "${MODPACK}"; then log "Downloading mod/plugin pack" if ! get -o /tmp/modpack.zip "${MODPACK}"; then logError "Failed to download from ${MODPACK}" exit 2 fi elif [[ "$MODPACK" =~ .*\.zip ]]; then if ! cp "$MODPACK" /tmp/modpack.zip; then logError "Failed to copy from $MODPACK" exit 2 fi else logError "Invalid URL or Path given for MODPACK: $MODPACK" exit 1 fi if [ "$FAMILY" = "SPIGOT" ]; then mkdir -p "$PLUGINS_OUT_DIR" if ! unzip -o -d "$PLUGINS_OUT_DIR" /tmp/modpack.zip; then logError "Failed to unzip the modpack from ${MODPACK}" fi else mkdir -p "$MODS_OUT_DIR" if ! unzip -o -d "$MODS_OUT_DIR" /tmp/modpack.zip; then logError "Failed to unzip the modpack from ${MODPACK}" fi fi rm -f /tmp/modpack.zip fi } function handleListings() { if usesMods && usesPlugins; then if [[ -v MODS ]]; then ensureRemoveAllModsOff "MODS is set" mkdir -p "$MODS_OUT_DIR" mc-image-helper mcopy \ --glob=*.jar \ --scope=var-list \ --to="$MODS_OUT_DIR" \ "$MODS" fi if [[ -v PLUGINS ]]; then ensureRemoveAllModsOff "PLUGINS is set" mkdir -p "$PLUGINS_OUT_DIR" mc-image-helper mcopy \ --glob=*.jar \ --scope=var-list \ --to="$PLUGINS_OUT_DIR" \ "$PLUGINS" fi if [[ "$MODS_FILE" ]]; then ensureRemoveAllModsOff "MODS_FILE is set" mkdir -p "$MODS_OUT_DIR" mc-image-helper mcopy \ --file-is-listing \ --scope=file-list \ --to="$MODS_OUT_DIR" \ "$MODS_FILE" fi if [[ "$PLUGINS_FILE" ]]; then ensureRemoveAllModsOff "PLUGINS_FILE is set" mkdir -p "$PLUGINS_OUT_DIR" mc-image-helper mcopy \ --file-is-listing \ --scope=file-list \ --to="$PLUGINS_OUT_DIR" \ "$PLUGINS_FILE" fi elif usesPlugins || usesMods; then outDir="$MODS_OUT_DIR" if usesPlugins; then outDir="$PLUGINS_OUT_DIR" fi if [[ -v MODS || -v PLUGINS ]]; then ensureRemoveAllModsOff "MODS or PLUGINS is set" mkdir -p "$outDir" mc-image-helper mcopy \ --glob=*.jar \ --scope=var-list \ --to="$outDir" \ "${MODS:-}" "${PLUGINS:-}" fi if [[ "$MODS_FILE" || "$PLUGINS_FILE" ]]; then ensureRemoveAllModsOff "MODS_FILE or PLUGINS_FILE is set" mkdir -p "$outDir" mc-image-helper mcopy \ --file-is-listing \ --scope=file-list \ --to="$outDir" \ "$MODS_FILE" "$PLUGINS_FILE" fi fi } function handleGenericPacks() { : "${GENERIC_PACKS:=${GENERIC_PACK}}" : "${GENERIC_PACKS_PREFIX:=}" : "${GENERIC_PACKS_SUFFIX:=}" : "${GENERIC_PACKS_DISABLE_MODS:=}" if [[ "${GENERIC_PACKS}" ]]; then IFS=',' read -ra packs <<< "${GENERIC_PACKS}" packFiles=() for packEntry in "${packs[@]}"; do pack="${GENERIC_PACKS_PREFIX}${packEntry}${GENERIC_PACKS_SUFFIX}" if isURL "${pack}"; then mkdir -p /data/packs log "Downloading generic pack from $pack" if ! outfile=$(get -o /data/packs --output-filename --skip-up-to-date "$pack"); then logError "Failed to download $pack" exit 2 fi packFiles+=("$outfile") else packFiles+=("$pack") fi done isDebugging && [ -f "$sum_file}" ] && cat "$sum_file" log "Checking if generic packs are up to date" if isTrue "${SKIP_GENERIC_PACK_UPDATE_CHECK:-false}" && [ -f "$sum_file" ]; then log "Skipping generic pack update check" elif isTrue "${FORCE_GENERIC_PACK_UPDATE}" || ! checkSum "${sum_file}"; then log "Generic pack(s) are out of date. Re-applying..." original_base_dir=/data/.tmp/generic_pack_base base_dir=$original_base_dir rm -rf "${base_dir}" mkdir -p "${base_dir}" for pack in "${packFiles[@]}"; do isDebugging && ls -l "${pack}" extract "${pack}" "${base_dir}" done # Disable mods for mod in ${GENERIC_PACKS_DISABLE_MODS}; do log Disabling $mod find "${base_dir}" -name "$mod" -exec mv {} {}.disabled -v \; done # Remove any eula file since container manages it rm -f "${base_dir}/eula.txt" # recalculate the actual base directory of content if ! base_dir=$(mc-image-helper find \ --max-depth=3 --type=directory --name=mods,plugins,config \ --only-shallowest --fail-no-matches --format '%h' \ "$base_dir"); then logError "Unable to find content base of generic packs ${GENERIC_PACKS}. Directories:" mc-image-helper find --name=* --max-depth=3 --type=directory --format '- %P' "$original_base_dir" exit 1 fi if [ -f /data/manifest.txt ]; then log "Manifest exists from older generic pack, cleaning up ..." while read -r f; do rm -rf "/data/${f}" done < /data/manifest.txt # prune empty dirs find /data -mindepth 1 -depth -type d -empty -delete rm -f /data/manifest.txt fi log "Writing generic pack manifest ... " find "${base_dir}" -type f -printf "%P\n" > /data/manifest.txt log "Applying generic pack ..." cp -R -f "${base_dir}"/* /data rm -rf $original_base_dir if isTrue "${SKIP_GENERIC_PACK_CHECKSUM:-false}"; then log "Skipping generic pack(s) checksum" else log "Saving generic pack(s) checksum" sha1sum "${packFiles[@]}" > "${sum_file}" if isDebugging; then cat "$sum_file" fi fi fi fi } function handleModrinthProjects() { # need to be backward compatible with old variable name, MODRINTH_ALLOWED_VERSION_TYPE : "${MODRINTH_PROJECTS_DEFAULT_VERSION_TYPE:=${MODRINTH_ALLOWED_VERSION_TYPE:-release}}" : "${MODRINTH_DOWNLOAD_DEPENDENCIES:=none}" if [[ -v MODRINTH_DOWNLOAD_OPTIONAL_DEPENDENCIES ]]; then logWarning "The variable MODRINTH_DOWNLOAD_OPTIONAL_DEPENDENCIES is removed." logWarning " Use MODRINTH_DOWNLOAD_DEPENDENCIES=optional instead" fi if [[ -v MODRINTH_PROJECTS ]]; then if isType CUSTOM; then if ! [[ $MODRINTH_LOADER ]]; then logError "MODRINTH_LOADER must be set when using TYPE=CUSTOM and MODRINTH_PROJECTS" exit 1 fi loader="${MODRINTH_LOADER,,}" elif isFamily HYBRID; then loader=${HYBRIDTYPE} elif isFamily VANILLA; then loader=datapack else loader="${TYPE,,}" fi mc-image-helper modrinth \ --output-directory=/data \ --world-directory="${LEVEL:-world}" \ --projects="${MODRINTH_PROJECTS}" \ --game-version="${VERSION}" \ --loader="$loader" \ --download-dependencies="$MODRINTH_DOWNLOAD_DEPENDENCIES" \ --allowed-version-type="$MODRINTH_PROJECTS_DEFAULT_VERSION_TYPE" fi } function handleCurseForgeFiles() { args=() if usesMods && ! usesPlugins; then args+=(--default-category mc-mods) elif usesPlugins && ! usesMods; then args+=(--default-category bukkit-plugins) fi case "${TYPE,,}" in forge|neoforge|fabric|quilt) args+=(--mod-loader "$TYPE") ;; *) if isFamily HYBRID; then # To disambiguate mc-mods we'll assume that hybrid servers # are blending Forge (rather than Fabric or NeoForge) args+=(--mod-loader "${HYBRIDTYPE}") fi ;; esac # shellcheck disable=SC2086 # since we want CURSEFORGE_FILES to expand mc-image-helper curseforge-files \ "${args[@]}" \ "${CURSEFORGE_FILES}" } handlePackwiz handleModpackZip handleListings if [[ $MANIFEST ]]; then logError "MANIFEST is no longer supported." logError " Use MODPACK_PLATFORM=AUTO_CURSEFORGE and CF_MODPACK_MANIFEST instead" exit 1 fi if [[ $MODS_FORGEAPI_KEY || $MODS_FORGEAPI_FILE || $MODS_FORGEAPI_PROJECTIDS ]]; then logError "The MODS_FORGEAPI_FILE / MODS_FORGEAPI_PROJECTIDS feature is no longer supported" logError " Use CURSEFORGE_FILES instead." exit 1 fi handleGenericPacks handleModrinthProjects if usesMods || usesPlugins; then handleCurseForgeFiles fi # If supplied with a URL for a config (simple zip of configurations), download it and unpack if [[ "$MODCONFIG" ]]; then case "X$MODCONFIG" in X[Hh][Tt][Tt][Pp]*[Zz][iI][pP]) log "Downloading mod/plugin configs via HTTP" log " from $MODCONFIG ..." curl -sSL -o /tmp/modconfig.zip "$MODCONFIG" if [ "$FAMILY" = "SPIGOT" ]; then mkdir -p /data/plugins unzip -o -d /data/plugins /tmp/modconfig.zip else mkdir -p /data/config unzip -o -d /data/config /tmp/modconfig.zip fi rm -f /tmp/modconfig.zip ;; *) log "Invalid URL given for modconfig: Must be HTTP or HTTPS and a ZIP file" ;; esac fi exec "$(dirname "$0")/start-setupMounts" "$@" ================================================ FILE: scripts/start-setupMounts ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" : "${SYNC_SKIP_NEWER_IN_DESTINATION:=${PLUGINS_SYNC_UPDATE:-true}}" : "${REPLACE_ENV_DURING_SYNC:=true}" : "${REPLACE_ENV_SUFFIXES:=yml,yaml,txt,cfg,conf,properties,hjson,json,tml,toml}" : "${REPLACE_ENV_VARIABLE_PREFIX=${ENV_VARIABLE_PREFIX:-CFG_}}" : "${REPLACE_ENV_VARIABLES_EXCLUDES:=}" : "${REPLACE_ENV_VARIABLES_EXCLUDE_PATHS:=}" : "${DEBUG:=false}" : "${MODS_OUT_DIR:=/data/mods}" : "${PLUGINS_OUT_DIR:=/data/plugins}" set -e isDebugging && set -x if isTrue "${SYNC_SKIP_NEWER_IN_DESTINATION}"; then updateArg="--skip-newer-in-destination" fi if isTrue "${REPLACE_ENV_DURING_SYNC}"; then subcommand=sync-and-interpolate else subcommand=sync fi function mc-image-helper-mounts(){ mc-image-helper \ ${subcommand} $updateArg \ --replace-env-file-suffixes="${REPLACE_ENV_SUFFIXES}" \ --replace-env-excludes="${REPLACE_ENV_VARIABLES_EXCLUDES}" \ --replace-env-exclude-paths="${REPLACE_ENV_VARIABLES_EXCLUDE_PATHS}" \ --replace-env-prefix="${REPLACE_ENV_VARIABLE_PREFIX}" "$@" } : "${COPY_PLUGINS_SRC:="/plugins"}" : "${COPY_PLUGINS_DEST:=${PLUGINS_OUT_DIR}}" if usesPlugins; then mkdir -p "${COPY_PLUGINS_DEST}" log "Copying any plugins from ${COPY_PLUGINS_SRC} to ${COPY_PLUGINS_DEST}" mc-image-helper-mounts "${COPY_PLUGINS_SRC}" "${COPY_PLUGINS_DEST}" fi : "${COPY_MODS_SRC:="/mods"}" : "${COPY_MODS_DEST:=${MODS_OUT_DIR}}" if usesMods; then log "Copying any mods from ${COPY_MODS_SRC} to ${COPY_MODS_DEST}" mc-image-helper-mounts "${COPY_MODS_SRC}" "${COPY_MODS_DEST}" fi : "${COPY_CONFIG_SRC:="/config"}" : "${COPY_CONFIG_DEST:="/data/config"}" log "Copying any configs from ${COPY_CONFIG_SRC} to ${COPY_CONFIG_DEST}" mc-image-helper-mounts "${COPY_CONFIG_SRC}" "${COPY_CONFIG_DEST}" exec "$(dirname "$0")/start-setupServerProperties" "$@" ================================================ FILE: scripts/start-setupRbac ================================================ #!/bin/bash set -euo pipefail IFS=$'\n\t' : "${EXISTING_OPS_FILE:=SYNC_FILE_MERGE_LIST}" : "${EXISTING_WHITELIST_FILE:=SYNC_FILE_MERGE_LIST}" # shellcheck source=start-utils . "$(dirname "$0")/start-utils" isDebugging && set -x if [[ -v APPEND_OPS ]] && isTrue "${APPEND_OPS}"; then EXISTING_OPS_FILE=MERGE elif [[ -v OVERRIDE_OPS ]] && isTrue "${OVERRIDE_OPS}"; then EXISTING_OPS_FILE=SYNCHRONIZE fi if [[ -v APPEND_WHITELIST ]] && isTrue "${APPEND_WHITELIST}"; then EXISTING_WHITELIST_FILE=MERGE elif [[ -v OVERRIDE_WHITELIST ]] && isTrue "${OVERRIDE_WHITELIST}"; then EXISTING_WHITELIST_FILE=SYNCHRONIZE fi sharedArgs=(--version="$VERSION") if [[ ${FTB_DIR:-} ]]; then sharedArgs+=(--output-directory="$FTB_DIR") else sharedArgs+=(--output-directory=/data) fi if [[ -v OPS_FILE ]]; then existing="$EXISTING_OPS_FILE" if [[ "$EXISTING_OPS_FILE" = SYNC_FILE_MERGE_LIST ]]; then existing=SYNCHRONIZE fi mc-image-helper manage-users \ "${sharedArgs[@]}" \ --type=JAVA_OPS \ --input-is-file \ --existing="${existing}" \ "$OPS_FILE" fi if [[ -v OPS ]]; then args=() existing="$EXISTING_OPS_FILE" # Working with an OPS list, so normalize the value to a "non-file" mode if [[ "$EXISTING_OPS_FILE" = SYNC_FILE_MERGE_LIST ]]; then existing=MERGE fi # legacy option if [[ -v APPEND_OPS ]] && isTrue "${APPEND_OPS}"; then existing=MERGE fi # legacy option if [[ -v OVERRIDE_OPS ]] && isFalse "${OVERRIDE_OPS}"; then existing=SKIP fi # shellcheck disable=SC2086 mc-image-helper manage-users \ "${sharedArgs[@]}" "${args[@]}" \ --type=JAVA_OPS \ --existing="${existing}" \ $OPS fi if [[ -v WHITELIST_FILE ]]; then existing="$EXISTING_WHITELIST_FILE" if [[ "$EXISTING_WHITELIST_FILE" = SYNC_FILE_MERGE_LIST ]]; then existing=SYNCHRONIZE fi mc-image-helper manage-users \ "${sharedArgs[@]}" \ --type=JAVA_WHITELIST \ --input-is-file \ --existing="${existing}" \ "$WHITELIST_FILE" fi if [[ -v WHITELIST ]]; then args=() existing="$EXISTING_WHITELIST_FILE" if [[ "$EXISTING_WHITELIST_FILE" = SYNC_FILE_MERGE_LIST ]]; then existing=MERGE fi # legacy option if [[ -v APPEND_WHITELIST ]] && isTrue "${APPEND_WHITELIST}"; then existing=MERGE fi # legacy option if [[ -v OVERRIDE_WHITELIST ]] && isFalse "${OVERRIDE_WHITELIST}"; then existing=SKIP fi # shellcheck disable=SC2086 mc-image-helper manage-users \ "${sharedArgs[@]}" "${args[@]}" \ --type=JAVA_WHITELIST \ --existing="${existing}" \ $WHITELIST fi exec "$(dirname "$0")/start-finalExec" "$@" ================================================ FILE: scripts/start-setupServerProperties ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" : "${SERVER_PROPERTIES:=/data/server.properties}" : "${OVERRIDE_SERVER_PROPERTIES:=true}" : "${SKIP_SERVER_PROPERTIES:=false}" : "${ENABLE_WHITELIST:=}" function customizeServerProps { local firstSetup=$1 # Whitelist processing if [ -n "$WHITELIST" ] || [ -n "$WHITELIST_FILE" ] || isTrue "${ENABLE_WHITELIST}"; then log "Enabling whitelist functionality" WHITELIST_PROP=true ENFORCE_WHITELIST=true export WHITELIST_PROP ENFORCE_WHITELIST elif isTrue "$firstSetup" || isFalse "${ENABLE_WHITELIST}"; then log "Disabling whitelist functionality" WHITELIST_PROP=false export WHITELIST_PROP fi # normalize MOTD if [[ ${TYPE^^} = LIMBO ]]; then if [[ $MOTD ]] && ! [[ $MOTD =~ ^{ ]]; then # shellcheck disable=SC2089 MOTD="{\"text\":\"${MOTD}\"}" fi fi if [[ -v MODE ]]; then log "Setting mode" case ${MODE,,} in su*|0) if versionLessThan 1.13; then MODE=0 else MODE=survival fi ;; c*|1) if versionLessThan 1.13; then MODE=1 else MODE=creative fi ;; a*|2) if versionLessThan 1.13; then MODE=2 else MODE=adventure fi ;; sp*|3) if versionLessThan 1.13; then MODE=3 else MODE=spectator fi ;; *) logError "Invalid game mode: $MODE" exit 1 ;; esac fi if [[ -v DIFFICULTY ]]; then case ${DIFFICULTY,,} in peaceful|0) if versionLessThan 1.13; then DIFFICULTY=0 else DIFFICULTY=peaceful fi ;; easy|1) if versionLessThan 1.13; then DIFFICULTY=1 else DIFFICULTY=easy fi ;; normal|2) if versionLessThan 1.13; then DIFFICULTY=2 else DIFFICULTY=normal fi ;; hard|3) if versionLessThan 1.13; then DIFFICULTY=3 else DIFFICULTY=hard fi ;; *) log "DIFFICULTY must be peaceful(0), easy(1), normal(2), or hard(3)." exit 1 ;; esac fi setPropertiesArgs=( --definitions "/image/property-definitions.json" ) if [[ -v CUSTOM_SERVER_PROPERTIES ]]; then setPropertiesArgs+=(--custom-properties "$CUSTOM_SERVER_PROPERTIES") fi if [[ -v SERVER_PROPERTIES_ESCAPE_UNICODE ]]; then if isTrue "$SERVER_PROPERTIES_ESCAPE_UNICODE"; then setPropertiesArgs+=(--escape-unicode) fi elif versionLessThan '1.20'; then setPropertiesArgs+=(--escape-unicode) fi handleDebugMode if ! mc-image-helper set-properties "${setPropertiesArgs[@]}" "$SERVER_PROPERTIES"; then logError "Failed to update server.properties" exit 1 fi } # Deploy server.properties file if [[ ${TYPE} == "CURSEFORGE" ]]; then export SERVER_PROPERTIES="${FTB_DIR}/server.properties" log "detected FTB, changing properties path to ${SERVER_PROPERTIES}" fi if ! isTrue "${SKIP_SERVER_PROPERTIES}"; then if [ ! -e "$SERVER_PROPERTIES" ] && [[ "${SERVER_PROPERTIES_REPO_URL:-}" ]]; then # Helper to support base URLs (like the config repos) # If the URL doesn't end in .properties, assume it is a base and append version/file if [[ "${SERVER_PROPERTIES_REPO_URL}" != *".properties" ]]; then SERVER_PROPERTIES_REPO_URL=$(buildDownloadList "$SERVER_PROPERTIES_REPO_URL" "$VERSION" server.properties) fi if ! mc-image-helper mcopy \ --to "$(dirname "$SERVER_PROPERTIES")" \ --skip-existing \ --ignore-missing-sources \ "$SERVER_PROPERTIES_REPO_URL"; then logWarning "Failed to download server.properties from ${SERVER_PROPERTIES_REPO_URL}" fi fi if [ ! -e "$SERVER_PROPERTIES" ]; then log "Creating server properties in ${SERVER_PROPERTIES}" # If not provided, generate a reasonable default message-of-the-day, # which shows up in the server listing in the client if ! [ -v MOTD ]; then # snapshot is the odd case where we have to look at version to identify that label if [[ ${DECLARED_TYPE} == "VANILLA" && ${VERSION} == "SNAPSHOT" ]]; then label=SNAPSHOT else label=${DECLARED_TYPE} fi # Convert label to title-case label=${label,,} label=${label^} MOTD="A ${label} Minecraft Server powered by Docker" fi customizeServerProps true elif isTrue "${OVERRIDE_SERVER_PROPERTIES}"; then customizeServerProps false else log "server.properties already created and managed manually" fi else log "Skipping setup of server.properties" fi if isTrue "${ENABLE_AUTOPAUSE}"; then if [ -f "$SERVER_PROPERTIES" ]; then current_max_tick=$( grep 'max-tick-time' "$SERVER_PROPERTIES" | sed -r 's/( )+//g' | awk -F= '{print $2}' ) if (( current_max_tick > 0 && current_max_tick < 86400000 )); then logWarning "The server.properties for the server doesn't have the Server Watchdog (effectively) disabled." logWarning " Autopause functionality resuming the process might trigger the Watchdog and restart the server completely." 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+)." fi fi fi if isTrue "${DUMP_SERVER_PROPERTIES:-false}"; then log "DEBUG Dumping server.properties" cat "${SERVER_PROPERTIES}" fi exec "$(dirname "$0")/start-setupEnvVariables" "$@" ================================================ FILE: scripts/start-setupWorld ================================================ #!/bin/bash # shellcheck source=start-utils . "$(dirname "$0")/start-utils" set -e isDebugging && set -x worldDownload=/data/tmp/world.zip tmpWorldData=/data/tmp/world-data # support absolute directories if [[ "${LEVEL:-world}" =~ ^\/.*$ ]]; then worldDest=${LEVEL} else if [ "$TYPE" = "CURSEFORGE" ]; then worldDest=$FTB_DIR/${LEVEL:-world} else worldDest=/data/${LEVEL:-world} fi fi if [[ "$WORLD" ]] && ( isTrue "${FORCE_WORLD_COPY}" || [ ! -d "$worldDest" ] ); then if isTrue "${FORCE_WORLD_COPY}"; then log "Removing existing world data in $worldDest ${worldDest}_nether ${worldDest}_the_end" rm -rf "$worldDest" \ "${worldDest}_nether" \ "${worldDest}_the_end" fi if isURL "$WORLD"; then log "Downloading world from $WORLD" mkdir -p /data/tmp if ! get -o ${worldDownload} "$WORLD"; then logError "Failed to download world from $WORLD" exit 1 fi WORLD=${worldDownload} fi if [ -f "$WORLD" ]; then log "Extracting world" # Stage contents so that the correct subdirectory can be picked off mkdir -p ${tmpWorldData} if ! extract "$WORLD" ${tmpWorldData}; then logError "Extracting world from $WORLD" exit 1 fi baseDirs=$(find ${tmpWorldData} -name "level.dat" -exec dirname "{}" \;) if ! [[ $baseDirs ]]; then logError "World content is not valid since level.dat could not be found" exit 2 fi count=$(echo "$baseDirs" | wc -l) if [[ $count -gt 1 ]]; then baseDirsNoSpigotSuffix=$(echo "$baseDirs" | sed -re 's:(_nether|_the_end)/?$::' | sort -u) if [ "$(echo "$baseDirsNoSpigotSuffix" | wc -l)" -eq 1 ]; then baseDir="$baseDirsNoSpigotSuffix" baseName=$(basename "$baseDir") log "Found Spigot naming conventions, taking $baseName as main dimension" else baseDir="$(echo "$baseDirs" | sed -n ${WORLD_INDEX:-1}p)" baseName=$(basename "$baseDir") logWarning "Multiple levels found, picking: $baseName" fi elif [[ $count -gt 0 ]]; then baseDir="$baseDirs" else logError "Invalid world content" exit 1 fi if [ -d "${baseDir}_nether/DIM-1" ]; then if [ -d "$baseDir/DIM-1" ]; then logWarning "Found Nether dimension in both $baseDir and ${baseDir}_nether, picking ${baseDir}_nether" rm -r "$baseDir/DIM-1" fi fi if [ -d "${baseDir}_the_end/DIM1" ]; then if [ -d "$baseDir/DIM1" ]; then logWarning "Found End dimension in both $baseDir and ${baseDir}_the_end, picking ${baseDir}_the_end" rm -r "$baseDir/DIM1" fi fi log "Copying world..." rsync --remove-source-files --recursive --delete "$baseDir/" "$worldDest" if [ "$FAMILY" = "SPIGOT" ]; then if [ -d "${baseDir}_nether" ]; then log "Copying Spigot Nether..." rsync --remove-source-files --recursive --delete "${baseDir}_nether/" "${worldDest}_nether" elif [ -d "$worldDest/DIM-1" ]; then log "Moving Nether to Spigot location..." mkdir -p "${worldDest}_nether" mv -f "$worldDest/DIM-1" "${worldDest}_nether/" fi if [ -d "${baseDir}_the_end" ]; then log "Copying Spigot End..." rsync --remove-source-files --recursive --delete "${baseDir}_the_end/" "${worldDest}_the_end" elif [ -d "$worldDest/DIM1" ]; then log "Moving End to Spigot location..." mkdir -p "${worldDest}_the_end" mv -f "$worldDest/DIM1" "${worldDest}_the_end/" fi else if [ -d "${baseDir}_nether/DIM-1" ]; then log "Copying Spigot Nether to vanilla location..." rsync --remove-source-files --recursive --delete "${baseDir}_nether/DIM-1" "${worldDest}/" fi if [ -d "${baseDir}_the_end/DIM1" ]; then log "Copying Spigot End to vanilla location..." rsync --remove-source-files --recursive --delete "${baseDir}_the_end/DIM1" "${worldDest}/" fi fi elif [ -d "$WORLD" ]; then log "Cloning world directory from $WORLD ..." rsync --recursive --delete "${WORLD%/}"/ "$worldDest" else logError "World file/directory $WORLD is missing" exit 1 fi if [ "$FAMILY" = "SPIGOT" ]; then # Reorganise if a Spigot server log "Moving End and Nether maps to Spigot location" [ -d "$worldDest/DIM1" ] && mv -f "$worldDest/DIM1" "${worldDest}_the_end" [ -d "$worldDest/DIM-1" ] && mv -f "$worldDest/DIM-1" "${worldDest}_nether" fi fi [[ -f ${worldDownload} ]] && rm ${worldDownload} [[ -d ${tmpWorldData} ]] && rm -rf ${tmpWorldData} exec "$(dirname "$0")/start-setupDatapack" "$@" ================================================ FILE: scripts/start-spiget ================================================ #!/bin/bash set -euo pipefail IFS=$'\n\t' # shellcheck source=start-utils . "$(dirname "$0")/start-utils" handleDebugMode : "${SPIGET_RESOURCES:=}" : "${SPIGET_DOWNLOAD_TOLERANCE:=5}" # in minutes : "${REMOVE_OLD_MODS:=false}" : "${REMOVE_OLD_MODS_EXCLUDE:=}" : "${REMOVE_OLD_MODS_INCLUDE:=*.jar,*-version.json}" : "${REMOVE_OLD_MODS_DEPTH:=1} " acceptArgs=(--accept application/zip --accept application/java-archive --accept application/octet-stream) containsJars() { file=${1?} pat='\.jar$' while read -r line; do if [[ $line =~ $pat ]]; then return 0 fi done < <(unzip -l "$file" | tail -n +4) return 1 } containsPlugin() { file=${1?} pat='plugin.yml$' while read -r line; do if [[ $line =~ $pat ]]; then return 0 fi done < <(unzip -l "$file" | tail -n +4) return 1 } getResourceFromSpiget() { resource=${1?} log "Downloading resource ${resource} ..." mkdir -p /data/plugins versionfile="/data/plugins/.${resource}-version.json" versionfileNew="${versionfile}.tmp" if [ -f "$versionfile" ]; then if [[ -n $(find "$versionfile" -mmin +${SPIGET_DOWNLOAD_TOLERANCE}) ]]; then urlVersion="https://api.spiget.org/v2/resources/${resource}/versions/latest" if ! get -o "${versionfileNew}" "${urlVersion}"; then logError "Failed to download resource version meta data '${resource}' from ${urlVersion}" exit 2 fi if isTrue "${REMOVE_OLD_MODS:-false}"; then installedVersion=0.0.0 else installedVersion=$(jq -r '.name' $versionfile) fi newVersion=$(jq -r '.name' $versionfileNew) if [ "$installedVersion" = "$newVersion" ]; then log "Resource '${resource}': installed version '${installedVersion}' already up to date" mv "${versionfileNew}" "${versionfile}" else if downloadResourceFromSpiget "${resource}"; then mv "${versionfileNew}" "${versionfile}" fi fi else log "resource '${resource}' not checked because version meta file newer than '${SPIGET_DOWNLOAD_TOLERANCE}' minutes" fi else if downloadResourceFromSpiget "${resource}"; then urlVersion="https://api.spiget.org/v2/resources/${resource}/versions/latest" if ! get -o "${versionfileNew}" "${urlVersion}"; then logError "Failed to download resource version meta data '${resource}' from ${urlVersion}" exit 2 fi mv "${versionfileNew}" "${versionfile}" fi fi } downloadResourceFromSpiget() { resource=${1?} tempDir="/data/plugins/tmp-${resource}" mkdir -p $tempDir resourceUrl="https://api.spiget.org/v2/resources/${resource}" if ! outfile=$(get_silent --output-filename -o $tempDir "${acceptArgs[@]}" "${resourceUrl}/download"); then logError "Failed to download resource '${resource}' from ${resourceUrl}/download" if externalUrl=$(get --json-path '$.file.externalUrl' "${resourceUrl}"); then logError " Visit $externalUrl to pre-download the resource" logError " instead of using SPIGET_RESOURCES" fi exit 1 fi if ! fileType=$(get --json-path '.file.type' "${resourceUrl}"); then logError "Failed to retrieve file type of resource $resource" exit 1 fi if [[ $fileType = .sk ]]; then mkdir -p /data/plugins/Skript/scripts mv "$outfile" /data/plugins/Skript/scripts else if containsPlugin "${outfile}"; then log "Moving resource ${resource} into plugins" mv "$outfile" /data/plugins elif containsJars "${outfile}"; then log "Extracting contents of resource ${resource} into plugins" extract "$outfile" /data/plugins else logError "File for resource ${resource} has an unexpected file type: ${fileType}" exit 2 fi fi rm -rf "$tempDir" } if [[ ${SPIGET_RESOURCES} ]]; then if isTrue "${REMOVE_OLD_MODS}"; then removeOldMods /data/plugins REMOVE_OLD_MODS=false fi log "Getting plugins via Spiget" IFS=',' read -r -a resources <<<"${SPIGET_RESOURCES}" for resource in "${resources[@]}"; do getResourceFromSpiget "${resource}" done fi exec "$(dirname "$0")/start-setupWorld" "$@" ================================================ FILE: scripts/start-utils ================================================ #!/bin/bash function get_from_gh() { if [[ "${GH_TOKEN:-}" ]]; then # User has provided a Personal Access Token to mitigate rate-limiting issues if [[ -z "${oAuthScopes}" ]]; then oAuthScopes=$(curl -s -H "Authorization: token $GH_TOKEN" https://api.github.com/users/codertocat -I | grep x-oauth-scopes) fi if [[ ! "$oAuthScopes" =~ ^x-oauth-scopes:[[:space:]]*$ ]]; then # Don't use what you don't have to... logError "GH_TOKEN has permissions it doesn't need. Recreate or update this personal access token and disable ALL scopes." exit 1 else curl -fsSL -H "Authorization: token $GH_TOKEN" "${@:2}" "$1" fi else curl -fsSL "${@:2}" "$1" fi } function applyResultsFile() { # grab SERVER and export it set -a # shellcheck disable=SC1090 source "$1" set +a } function join_by() { local d=$1 shift echo -n "$1" shift printf "%s" "${@/#/$d}" } function get_major_version() { version=$1 echo "$version" | cut -d. -f 1-2 } function isPercentage() { local value=$1 [[ $value =~ ^[0-9]+(\.[0-9]+)?\s*%$ ]] } function getPercentageValue() { local value=$1 if [[ "$value" =~ ^([0-9]+(\.[0-9]+)?)\s*%$ ]]; then echo "${BASH_REMATCH[1]}" return 0 else logError "Value '$value' is not a valid percentage." return 1 fi } function isURL() { local value=$1 [[ $value =~ ^(https?|ftp):// ]] } function isValidFileURL() { suffix=${1:?Missing required suffix arg} url=${2:?Missing required url arg} [[ "$url" =~ ^http.*://.*\.${suffix}(\?.*)?$ ]] } function resolveEffectiveUrl() { url="${1:?Missing required url argument}" if ! curl -Ls -o /dev/null -w "%{url_effective}" "$url"; then logError "Failed to resolve effective URL from $url" exit 2 fi } function getFilenameFromUrl() { url="${1:?Missing required url argument}" strippedOfQuery="${url%\?*}" basename "$strippedOfQuery" } function isTrue() { case "${1,,}" in true | yes | on | 1) return 0 ;; *) return 1 ;; esac } function isFalse() { case "${1,,}" in false | no | off | 0) return 0 ;; *) return 1 ;; esac } function isDebugging() { isTrue "${DEBUG:-false}" } function handleDebugMode() { if isDebugging; then set -x fi } function debug() { if isDebugging; then log "DEBUG: $*" fi } function logn() { echo -n "[init] $*" } # Use as # cmd | applyLogPrefix function applyLogPrefix() { sed 's/^/[init] /' } function log() { local oldState # The return status when listing options is zero if all optnames are enabled, non- zero otherwise. oldState=$(shopt -po xtrace || true) shopt -u -o xtrace ts= if isDebugging || isTrue "${LOG_TIMESTAMP:-false}"; then ts=" $(date --rfc-3339=seconds)" fi echo -e "[init]${ts} $*" eval "$oldState" } # Refer to https://unix.stackexchange.com/a/10065/102376 function isTerminal() { if test -t 1; then # see if it supports colors... ncolors=$(tput colors) test -n "$ncolors" && test "$ncolors" -ge 8 else return 1 fi } errorLogTag="[ERROR]" warningLogTag="[WARN]" if isTerminal; then normal="$(tput sgr0)" red="$(tput setaf 1)" yellow="$(tput setaf 3)" function getErrorColoredLogString() { echo "${red}$errorLogTag $* ${normal}" } function getWarningColoredLogString() { echo "${yellow}$warningLogTag $* ${normal}" } else function getErrorColoredLogString() { echo "$errorLogTag $*" } function getWarningColoredLogString() { echo "$warningLogTag $*" } fi function error() { echo -e "$(getErrorColoredLogString "$*")" } function logError() { if isDebugging; then set +x fi log "$(getErrorColoredLogString "$*")" if isDebugging; then set -x fi } function warning() { if isDebugging; then set +x fi echo -e "$(getWarningColoredLogString "$*")" if isDebugging; then set -x fi } function logWarning() { log "$(getWarningColoredLogString "$*")" } function isNumeric() { [[ $1 =~ ^[0-9]+$ ]] } function isNumericElseSetToDefault() { local var_name="$1" local default_value="$2" if ! isNumeric ${!var_name} ; then eval "$var_name=$default_value" export "$var_name" logWarning "$var_name is not numeric, set to $default_value (seconds)" fi } function checkIfNotZeroElseSetToDefault() { local var_name="$1" local default_value="$2" if [ "${!var_name}" -eq "0" ] ; then eval "$var_name=$default_value" export "$var_name" logWarning "$var_name must not be 0, set to $default_value (seconds)" fi } function logAutopause() { echo "[Autopause loop] $*" } function logAutopauseAction() { echo "[$(date -Iseconds)] [Autopause] $*" } function logAutostop() { echo "[Autostop loop] $*" } function logAutostopAction() { echo "[$(date -Iseconds)] [Autostop] $*" } function logRcon() { echo "[Rcon loop] $*" } function normalizeMemSize() { local scale=1 local mem=1 case ${1,,} in *k) scale=1024 ;; *m) scale=1048576 ;; *g) scale=1073741824 ;; *%) # Get system memory if [ -f "/sys/fs/cgroup/memory/memory.limit_in_bytes" ]; then mem=$( cat /sys/fs/cgroup/memory/memory.limit_in_bytes ) elif [ -f "/sys/fs/cgroup/memory.max" ]; then mem=$( cat /sys/fs/cgroup/memory.max ) fi # Scale is used to transform percentages into decimals (eg. 60 -> 0.6) scale=0.01 ;; esac val=${1:0:-1} echo $((val * scale * mem)) } function compare_version() { local left_version=$1 local comparison=$2 local right_version=$3 if [[ -z "$left_version" ]]; then echo "Left version is required" return 1 fi if [[ -z "$right_version" ]]; then echo "Right version is required" return 1 fi # Handle version channels ('a', 'b', or numeric) if [[ $left_version == a* ]]; then left_version=${left_version:1} left_version_channel=1 elif [[ $left_version == b* ]]; then left_version=${left_version:1} left_version_channel=2 else left_version_channel=3 fi if [[ $right_version == a* ]]; then right_version=${right_version:1} right_version_channel=1 elif [[ $right_version == b* ]]; then right_version=${right_version:1} right_version_channel=2 else right_version_channel=3 fi if [[ $comparison == "lt" && $left_version_channel < $right_version_channel ]]; then return 0 elif [[ $comparison == "lt" && $left_version_channel > $right_version_channel ]]; then return 1 elif [[ $comparison == "gt" && $left_version_channel > $right_version_channel ]]; then return 0 elif [[ $comparison == "gt" && $left_version_channel < $right_version_channel ]]; then return 1 elif [[ $comparison == "le" && $left_version_channel < $right_version_channel ]]; then return 0 elif [[ $comparison == "le" && $left_version_channel == $right_version_channel ]]; then return 0 elif [[ $comparison == "ge" && $left_version_channel > $right_version_channel ]]; then return 0 elif [[ $comparison == "ge" && $left_version_channel == $right_version_channel ]]; then return 0 elif [[ $comparison == "eq" && $left_version_channel == $right_version_channel ]]; then return 0 fi # Compare the versions using sort -V local result=1 case $comparison in "lt") if [[ $(echo -e "$left_version\n$right_version" | sort -V | head -n1) == "$left_version" && "$left_version" != "$right_version" ]]; then result=0 fi ;; "le") if [[ $(echo -e "$left_version\n$right_version" | sort -V | head -n1) == "$left_version" ]]; then result=0 fi ;; "eq") if [[ "$left_version" == "$right_version" ]]; then result=0 fi ;; "ge") if [[ $(echo -e "$left_version\n$right_version" | sort -V | tail -n1) == "$left_version" ]]; then result=0 fi ;; "gt") if [[ $(echo -e "$left_version\n$right_version" | sort -V | tail -n1) == "$left_version" && "$left_version" != "$right_version" ]]; then result=0 fi ;; *) echo "Unsupported comparison operator: $comparison" return 1 ;; esac return $result } function versionLessThan() { local oldState # The return status when listing options is zero if all optnames are enabled, non- zero otherwise. oldState=$(shopt -po xtrace || true) shopt -u -o xtrace eval "$oldState" # Verify strict mode because it might be enabled compare_version "${VERSION}" "lt" "${1?}" } function writeEula() { if ! echo "# Generated via Docker # $(date) eula=${EULA,,} " >/data/eula.txt; then logError "Unable to write eula to /data. Please make sure attached directory is writable by uid=${UID}" exit 2 fi } function removeOldMods { if [ -d "$1" ]; then log "Removing old mods including='${REMOVE_OLD_MODS_INCLUDE}' excluding='${REMOVE_OLD_MODS_EXCLUDE}' up to depth=${REMOVE_OLD_MODS_DEPTH}" args=( --delete --type file --min-depth=1 --max-depth "${REMOVE_OLD_MODS_DEPTH}" --name "${REMOVE_OLD_MODS_INCLUDE}" --exclude-name "${REMOVE_OLD_MODS_EXCLUDE}" ) if ! isDebugging; then args+=(--quiet) fi mc-image-helper find "${args[@]}" "$1" fi } function get() { mc-image-helper get "$@" } function get_silent() { local flags=(-s) if isTrue "${DEBUG_GET:-false}"; then flags+=("--debug") fi mc-image-helper "${flags[@]}" get "$@" } function isFamily() { for f in "${@}"; do if [[ ${FAMILY^^} == "${f^^}" ]]; then return 0 fi done return 1 } function isType() { for t in "${@}"; do # shellcheck disable=SC2153 if [[ ${TYPE^^} == "${t^^}" ]]; then return 0 fi done return 1 } function extract() { src=${1?} destDir=${2?} type=$(file -b --mime-type "${src}") case "${type}" in application/zip) unzip -o -q -d "${destDir}" "${src}" ;; application/x-tar | application/gzip | application/x-gzip | application/x-bzip2) tar -C "${destDir}" -xf "${src}" ;; application/zstd | application/x-zstd) tar -C "${destDir}" --use-compress-program=unzstd -xf "${src}" ;; *) logError "Unsupported archive type: $type" return 1 ;; esac } function getDistro() { grep -E "^ID=" /etc/os-release | cut -d= -f2 | sed -e 's/"//g' } function checkSum() { local sum_file=${1?} # Get distro distro=$(getDistro) case "${distro}" in debian | ubuntu | ol) sha1sum -c "${sum_file}" --status 2>/dev/null && return 0 ;; alpine) sha1sum -c "${sum_file}" -s 2>/dev/null && return 0 ;; *) return 1 ;; esac } function usesMods() { if isTrue "${USES_MODS:-}"; then return 0 else case "$FAMILY" in FORGE | FABRIC | HYBRID | SPONGE) return 0 ;; esac return 1 fi } function usesPlugins() { if isTrue "${USES_PLUGINS:-}"; then return 0 else case "$FAMILY" in SPIGOT | HYBRID) return 0 ;; esac return 1 fi } function resolveVersion() { givenVersion="$VERSION" # shellcheck disable=SC2153 if ! VERSION=$(mc-image-helper resolve-minecraft-version "$VERSION"); then exit 2 fi log "Resolved version given ${givenVersion} into ${VERSION}" } function resolveFamily() { case "$TYPE" in PAPER | SPIGOT | BUKKIT | CANYON | PUFFERFISH | PURPUR) FAMILY=SPIGOT ;; FORGE) FAMILY=FORGE ;; FABRIC | QUILT) FAMILY=FABRIC ;; esac export FAMILY } function ensureRemoveAllModsOff() { reason=${1?} if isTrue "${REMOVE_OLD_MODS:-false}"; then logWarning "Using REMOVE_OLD_MODS interferes with $reason -- it is now disabled" REMOVE_OLD_MODS=false fi } function buildDownloadList() { repoUrl=${1?} version=${2?} shift 2 result= for c in "${@}"; do if [[ $result ]]; then result+="," fi result+="${repoUrl}/${version}/$c" done echo "$result" } function firstArrayElement { local -n a="$1" if (( ${#a[@]} )); then echo "${a[0]}" fi } function shiftArray { local -n a="$1" if (( ${#a[@]} )); then a=("${a[@]:1}") fi } ================================================ FILE: tests/.gitignore ================================================ data/ cf_api_key.secret ================================================ FILE: tests/fulltests/multi-part-motd/docker-compose.yml ================================================ services: monitor: depends_on: mc: condition: service_started image: ${IMAGE_TO_TEST:-itzg/minecraft-server} entrypoint: mc-monitor command: status --host mc --retry-interval 1s --timeout 1s --retry-limit 60 restart: no mc: restart: no image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" VERSION: ${MINECRAFT_VERSION:-LATEST} TYPE: PAPER # regression tests https://github.com/itzg/docker-minecraft-server/issues/2545 MOTD: "Foo§rBar" ================================================ FILE: tests/fulltests/test.sh ================================================ #!/bin/bash # go to script root directory cd "$(dirname "$0")" || exit 1 # tests to completely spin up Minecraft and use the monitor to validate the service is running. fullMinecraftUpTest(){ file="$1" result=0 echo "Testing with images:" docker compose -f "$file" config --images # run the monitor to validate the Minecraft image is healthy upArgs=( --attach-dependencies --always-recreate-deps --abort-on-container-failure ) if ! docker compose -f "$file" up "${upArgs[@]}" monitor; then echo "$(dirname "$file") Result: failed" result=1 else echo "$(dirname "$file") Result: success" fi docker compose -f "$file" down -v --remove-orphans return $result } # go through each folder in fulltests and run fullbuilds files=$(ls */docker-compose.yml) for file in $files; do echo "Starting Tests on $(dirname "$file")" if ! fullMinecraftUpTest "$file"; then exit 2 fi done ================================================ FILE: tests/fulltests/vanilla-latest/docker-compose.yml ================================================ services: monitor: depends_on: mc: condition: service_started image: ${IMAGE_TO_TEST:-itzg/minecraft-server} entrypoint: mc-monitor command: status --host mc --retry-interval 1s --timeout 1s --retry-limit 300 restart: no mc: restart: no image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" VERSION: ${MINECRAFT_VERSION:-LATEST} ================================================ FILE: tests/setuponlytests/auto_curseforge/docker-compose.yml ================================================ services: mc: image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" SETUP_ONLY: "TRUE" MODPACK_PLATFORM: AUTO_CURSEFORGE CF_API_KEY: ${CF_API_KEY} CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/the-pixelmon-modpack/files/5954570 DEBUG: true volumes: - ./data:/data ================================================ FILE: tests/setuponlytests/auto_curseforge/require.sh ================================================ [[ $CF_API_KEY ]] || exit 1 ================================================ FILE: tests/setuponlytests/auto_curseforge/verify.sh ================================================ mc-image-helper assert fileExists "/data/mods/ExplorersCompass-1.16.5-1.1.2-forge.jar" mc-image-helper assert fileExists "/data/forge-1.16.5-36.2.34.jar" ================================================ FILE: tests/setuponlytests/auto_curseforge_file/docker-compose.yml ================================================ services: mc: image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" SETUP_ONLY: "TRUE" MODPACK_PLATFORM: AUTO_CURSEFORGE CF_API_KEY_FILE: /run/secrets/cf_api_key CF_PAGE_URL: https://www.curseforge.com/minecraft/modpacks/the-pixelmon-modpack/files/5954570 DEBUG: true volumes: - ./data:/data secrets: - cf_api_key secrets: cf_api_key: file: cf_api_key.secret ================================================ FILE: tests/setuponlytests/auto_curseforge_file/require.sh ================================================ [[ -n "$CF_API_KEY" ]] || exit 1 echo "$CF_API_KEY" > cf_api_key.secret || exit 1 ================================================ FILE: tests/setuponlytests/auto_curseforge_file/verify.sh ================================================ mc-image-helper assert fileExists "/data/mods/ExplorersCompass-1.16.5-1.1.2-forge.jar" mc-image-helper assert fileExists "/data/forge-1.16.5-36.2.34.jar" ================================================ FILE: tests/setuponlytests/cf_files/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" TYPE: FABRIC VERSION: 1.20.1 # Contains mix of Forge and Fabric mods CURSEFORGE_FILES: | https://www.curseforge.com/minecraft/mc-mods/clumps/files/4153343 jei 306612 @/extras/listing.txt CF_API_KEY: ${CF_API_KEY} volumes: - ./data:/data - ./fake.jar:/servers/fake.jar:ro - ./listing.txt:/extras/listing.txt:ro ================================================ FILE: tests/setuponlytests/cf_files/fake.jar ================================================ ================================================ FILE: tests/setuponlytests/cf_files/listing.txt ================================================ architectury-api@9.2.14 ================================================ FILE: tests/setuponlytests/cf_files/require.sh ================================================ [[ $CF_API_KEY ]] || exit 1 ================================================ FILE: tests/setuponlytests/cf_files/verify.sh ================================================ mc-image-helper assert fileExists "/data/mods/architectury-*-fabric.jar" mc-image-helper assert fileExists "/data/mods/Clumps-fabric-*.jar" mc-image-helper assert fileExists "/data/mods/fabric-api-*.jar" mc-image-helper assert fileExists "/data/mods/jei-*-fabric-*.jar" ================================================ FILE: tests/setuponlytests/conflicting_world_for_spigot_server/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" VERSION: ${MINECRAFT_VERSION:-LATEST} TYPE: "PAPER" WORLD: /worlds/world-for-testing.zip volumes: - ./worlds:/worlds:ro - ./data:/data ================================================ FILE: tests/setuponlytests/conflicting_world_for_spigot_server/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/conflicting_world_for_spigot_server/verify.sh ================================================ mc-image-helper assert fileExists \ world/level.dat \ world_nether/DIM-1/some_spigot_nether_file \ world_the_end/DIM1/some_spigot_end_file mc-image-helper assert fileNotExists \ world_nether/DIM-1/some_vanilla_nether_file \ world_the_end/DIM1/some_vanilla_end_file ================================================ FILE: tests/setuponlytests/conflicting_world_for_vanilla_server/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" WORLD: /worlds/world-for-testing.zip # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./worlds:/worlds:ro - ./data:/data # the following are only used to speed up test execution - ./verify.sh:/servers/fake.jar ================================================ FILE: tests/setuponlytests/conflicting_world_for_vanilla_server/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/conflicting_world_for_vanilla_server/verify.sh ================================================ mc-image-helper assert fileExists world/level.dat && \ mc-image-helper assert fileExists world/DIM-1/some_spigot_nether_file && \ mc-image-helper assert fileExists world/DIM1/some_spigot_end_file && \ ! mc-image-helper assert fileExists world/DIM-1/some_vanilla_nether_file && \ ! mc-image-helper assert fileExists world/DIM1/some_vanilla_end_file ================================================ FILE: tests/setuponlytests/curseforge/docker-compose.yml ================================================ services: mc: image: ${IMAGE_TO_TEST:-itzg/minecraft-server:java8-multiarch} environment: EULA: "true" SETUP_ONLY: "TRUE" TYPE: CURSEFORGE CF_SERVER_MOD: /modpacks/pack.zip INIT_MEMORY: 2G MAX_MEMORY: 6G volumes: - ./data:/data - ./modpacks:/modpacks:ro ================================================ FILE: tests/setuponlytests/curseforge/require.sh ================================================ [[ $VARIANT == java8* ]] || exit 1 ================================================ FILE: tests/setuponlytests/curseforge/verify.sh ================================================ mc-image-helper assert fileExists "/data/FeedTheBeast/forge-installer.jar" mc-image-helper assert fileExists "/data/FeedTheBeast/forge.jar" ================================================ FILE: tests/setuponlytests/defaults/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" volumes: - ./data:/data ================================================ FILE: tests/setuponlytests/defaults/verify.sh ================================================ mc-image-helper assert propertyEquals --file=server.properties --property=rcon.port --expect=25575 mc-image-helper assert propertyEquals --file=server.properties --property=enable-rcon --expect=true ================================================ FILE: tests/setuponlytests/generic-packs/docker-compose.yml ================================================ services: web: image: nginx volumes: - ./web:/usr/share/nginx/html healthcheck: test: ["CMD", "curl", "--fail", "http://localhost/configs.zip"] interval: 3s timeout: 5s retries: 3 mc: depends_on: web: condition: service_healthy image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" SETUP_ONLY: "true" GENERIC_PACKS: http://web/configs.zip,/packs/testing.zip LOG_TIMESTAMP: "true" # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 DEBUG: "true" volumes: - ./packs:/packs - ./data:/data - ./fake.jar:/servers/fake.jar ================================================ FILE: tests/setuponlytests/generic-packs/fake.jar ================================================ ================================================ FILE: tests/setuponlytests/generic-packs/verify.sh ================================================ mc-image-helper assert fileExists one.txt mods/two.txt mc-image-helper assert fileExists config/opt.yml ================================================ FILE: tests/setuponlytests/generic-packs-prefix/docker-compose.yml ================================================ services: mc: image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" SETUP_ONLY: "TRUE" GENERIC_PACKS: testing GENERIC_PACKS_PREFIX: /packs/ GENERIC_PACKS_SUFFIX: .zip DEBUG: "true" # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./packs:/packs - ./data:/data # the following are only used to speed up test execution - ./verify.sh:/servers/fake.jar ================================================ FILE: tests/setuponlytests/generic-packs-prefix/verify.sh ================================================ mc-image-helper assert fileExists one.txt mods/two.txt ================================================ FILE: tests/setuponlytests/icon-file-exact/docker-compose.yml ================================================ services: mc: image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" SETUP_ONLY: "true" ICON: /extra/4737386_minecraft_squircle_icon_64x64.png # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./data:/data - ./extra:/extra - ./fake.jar:/servers/fake.jar ================================================ FILE: tests/setuponlytests/icon-file-exact/fake.jar ================================================ ================================================ FILE: tests/setuponlytests/icon-file-exact/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/icon-file-exact/verify.sh ================================================ mc-image-helper assert fileExists server-icon.png ================================================ FILE: tests/setuponlytests/icon-file-scale/docker-compose.yml ================================================ services: mc: image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" SETUP_ONLY: "true" ICON: /extra/4737386_minecraft_squircle_icon.png # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./data:/data - ./extra:/extra - ./fake.jar:/servers/fake.jar ================================================ FILE: tests/setuponlytests/icon-file-scale/fake.jar ================================================ ================================================ FILE: tests/setuponlytests/icon-file-scale/verify.sh ================================================ mc-image-helper assert fileExists server-icon.png ================================================ FILE: tests/setuponlytests/icon-gif-multiframe/docker-compose.yml ================================================ services: web: image: nginx volumes: - ./web:/usr/share/nginx/html healthcheck: test: ["CMD", "curl", "--fail", "http://localhost/motion-tween-example.gif"] interval: 3s timeout: 5s retries: 3 mc: depends_on: web: condition: service_healthy image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" SETUP_ONLY: "true" ICON: http://web/motion-tween-example.gif # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./data:/data - ./fake.jar:/servers/fake.jar ================================================ FILE: tests/setuponlytests/icon-gif-multiframe/fake.jar ================================================ ================================================ FILE: tests/setuponlytests/icon-gif-multiframe/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/icon-gif-multiframe/verify.sh ================================================ mc-image-helper assert fileExists server-icon.png ================================================ FILE: tests/setuponlytests/icon-png-atscale/docker-compose.yml ================================================ services: web: image: nginx volumes: - ./web:/usr/share/nginx/html healthcheck: test: [ "CMD", "curl", "--fail", "http://localhost/4737386_minecraft_squircle_icon.png", ] interval: 3s timeout: 5s retries: 3 mc: depends_on: web: condition: service_healthy image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" SETUP_ONLY: "true" ICON: http://web/4737386_minecraft_squircle_icon.png # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./data:/data - ./fake.jar:/servers/fake.jar ================================================ FILE: tests/setuponlytests/icon-png-atscale/fake.jar ================================================ ================================================ FILE: tests/setuponlytests/icon-png-atscale/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/icon-png-atscale/verify.sh ================================================ mc-image-helper assert fileExists server-icon.png ================================================ FILE: tests/setuponlytests/icon-png-scale/docker-compose.yml ================================================ services: web: image: nginx volumes: - ./web:/usr/share/nginx/html healthcheck: test: [ "CMD", "curl", "--fail", "http://localhost/4737386_minecraft_squircle_icon.png", ] interval: 3s timeout: 5s retries: 3 mc: depends_on: web: condition: service_healthy image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" SETUP_ONLY: "true" ICON: http://web/4737386_minecraft_squircle_icon.png # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./data:/data - ./fake.jar:/servers/fake.jar ================================================ FILE: tests/setuponlytests/icon-png-scale/fake.jar ================================================ ================================================ FILE: tests/setuponlytests/icon-png-scale/verify.sh ================================================ mc-image-helper assert fileExists server-icon.png ================================================ FILE: tests/setuponlytests/modrinth/docker-compose.yml ================================================ services: mc: image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" SETUP_ONLY: "true" TYPE: FABRIC VERSION: 1.21.4 MODRINTH_PROJECTS: fabric-api,cloth-config volumes: - ./data:/data ================================================ FILE: tests/setuponlytests/modrinth/verify.sh ================================================ mc-image-helper assert fileExists "mods/cloth-config-*.jar" "mods/fabric-api-*.jar" ================================================ FILE: tests/setuponlytests/mounts-custom/custom/config/test.json ================================================ {} ================================================ FILE: tests/setuponlytests/mounts-custom/custom/mods/mod.jar ================================================ ================================================ FILE: tests/setuponlytests/mounts-custom/custom/plugins/plugin.jar ================================================ ================================================ FILE: tests/setuponlytests/mounts-custom/docker-compose.yml ================================================ services: mc: image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" SETUP_ONLY: "true" TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 COPY_PLUGINS_SRC: /custom/plugins COPY_PLUGINS_DEST: /data/custom-plugins COPY_MODS_SRC: /custom/mods COPY_MODS_DEST: /data/custom-mods COPY_CONFIG_SRC: /custom/config COPY_CONFIG_DEST: /data/custom-config volumes: - ./data:/data - ./custom:/custom - ./fake.jar:/servers/fake.jar ================================================ FILE: tests/setuponlytests/mounts-custom/fake.jar ================================================ ================================================ FILE: tests/setuponlytests/mounts-custom/verify.sh ================================================ mc-image-helper assert fileExists custom-plugins/plugin.jar mc-image-helper assert fileExists custom-mods/mod.jar mc-image-helper assert fileExists custom-config/test.json ================================================ FILE: tests/setuponlytests/nanolimbo/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" TYPE: NANOLIMBO volumes: - ./data:/data ================================================ FILE: tests/setuponlytests/nanolimbo/verify.sh ================================================ mc-image-helper assert fileExists "/data/NanoLimbo*.jar" mc-image-helper assert fileExists "/data/settings.yml" ================================================ FILE: tests/setuponlytests/ops_from_scratch/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" OPS: itzg # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./data:/data # the following are only used to speed up test execution - ./verify.sh:/servers/fake.jar ================================================ FILE: tests/setuponlytests/ops_from_scratch/verify.sh ================================================ mc-image-helper assert jsonPathEquals --file=ops.json --path='$[0].name' --expect=itzg mc-image-helper assert jsonPathEquals --file=ops.json --path='$[0].uuid' --expect=5cddfd26-fc86-4981-b52e-c42bb10bfdef mc-image-helper assert jsonPathEquals --file=ops.json --path='$[0].level' --expect=4 ================================================ FILE: tests/setuponlytests/packwiz/docker-compose.yml ================================================ services: web: image: nginx volumes: - ./web:/usr/share/nginx/html healthcheck: test: ["CMD", "curl", "--fail", "http://localhost/pack.toml"] interval: 3s timeout: 5s retries: 3 mc: depends_on: web: condition: service_healthy image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "true" PACKWIZ_URL: http://web/pack.toml TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.19 DEBUG_HELPER: "true" volumes: - ./data:/data - ./fake.jar:/servers/fake.jar ================================================ FILE: tests/setuponlytests/packwiz/fake.jar ================================================ ================================================ FILE: tests/setuponlytests/packwiz/require.sh ================================================ # maven.packwiz.infra.link is not resolvable exit 1 ================================================ FILE: tests/setuponlytests/packwiz/verify.sh ================================================ mc-image-helper assert fileExists mods/architectury-5.7.28-fabric.jar ================================================ FILE: tests/setuponlytests/packwiz/web/index.toml ================================================ hash-format = "sha256" [[files]] file = "mods/architectury-api.pw.toml" hash = "c20179449fff711afb96ba0eadd3328fbf1aae639082d25d77c9080837685b79" metafile = true ================================================ FILE: tests/setuponlytests/packwiz/web/mods/architectury-api.pw.toml ================================================ name = "Architectury API" filename = "architectury-5.7.28-fabric.jar" side = "both" [download] url = "https://cdn.modrinth.com/data/lhGA9TYQ/versions/5.7.28+fabric/architectury-5.7.28-fabric.jar" hash-format = "sha1" hash = "aa38ae9cc2e978e4ec87ff891f7b02ea0c0ee1b8" [update] [update.modrinth] mod-id = "lhGA9TYQ" version = "Hf0Bau1j" ================================================ FILE: tests/setuponlytests/packwiz/web/pack.toml ================================================ name = "Vanillia Server" author = "itzg" version = "2.0.0" pack-format = "packwiz:1.1.0" [index] file = "index.toml" hash-format = "sha256" hash = "1a27b406c3fb6d35167fe659384ab528a6b3f8a66e6c05d593058e646aec591f" [versions] fabric = "0.14.8" minecraft = "1.19" ================================================ FILE: tests/setuponlytests/pufferfish/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" TYPE: PUFFERFISH VERSION: ${MINECRAFT_VERSION:-LATEST} volumes: - ./data:/data ================================================ FILE: tests/setuponlytests/pufferfish/require.sh ================================================ [[ $MINECRAFT_VERSION == LATEST ]] || exit 1 ================================================ FILE: tests/setuponlytests/pufferfish/verify.sh ================================================ mc-image-helper assert fileExists "/data/pufferfish-*.jar" ================================================ FILE: tests/setuponlytests/quilt/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" TYPE: QUILT VERSION: ${MINECRAFT_VERSION:-LATEST} volumes: - ./data:/data ================================================ FILE: tests/setuponlytests/quilt/require.sh ================================================ [[ $MINECRAFT_VERSION == LATEST ]] || exit 1 ================================================ FILE: tests/setuponlytests/quilt/verify.sh ================================================ mc-image-helper assert fileExists "/data/quilt-server-*-launch.jar" ================================================ FILE: tests/setuponlytests/spiget/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" TYPE: PAPER # jar doesn't need to exist for setuponly tests PAPER_CUSTOM_JAR: /servers/fake.jar SPIGET_RESOURCES: "34315,3836,6245,2124" volumes: - ./data:/data ================================================ FILE: tests/setuponlytests/spiget/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/spiget/verify.sh ================================================ mc-image-helper assert fileExists plugins/3836.jar mc-image-helper assert fileExists plugins/34315.jar mc-image-helper assert fileExists plugins/6245.jar mc-image-helper assert fileExists plugins/SkinsRestorer.jar ================================================ FILE: tests/setuponlytests/spigot_world_for_spigot_server/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" TYPE: "PAPER" WORLD: /worlds/world-for-testing.zip # the following are only used to speed up test execution VERSION: 1.18.1 PAPER_CUSTOM_JAR: /servers/fake.jar volumes: - ./worlds:/worlds:ro - ./data:/data ================================================ FILE: tests/setuponlytests/spigot_world_for_spigot_server/verify.sh ================================================ mc-image-helper assert fileExists world/level.dat && \ mc-image-helper assert fileExists world/some_overworld_file && \ mc-image-helper assert fileExists world_nether/DIM-1/some_nether_file && \ mc-image-helper assert fileExists world_the_end/DIM1/some_end_file ================================================ FILE: tests/setuponlytests/spigot_world_for_vanilla_server/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" WORLD: /worlds/world-for-testing.zip # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./worlds:/worlds:ro - ./data:/data # the following are only used to speed up test execution - ./verify.sh:/servers/fake.jar ================================================ FILE: tests/setuponlytests/spigot_world_for_vanilla_server/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/spigot_world_for_vanilla_server/verify.sh ================================================ mc-image-helper assert fileExists world/level.dat && \ mc-image-helper assert fileExists world/some_overworld_file && \ mc-image-helper assert fileExists world/DIM-1/some_nether_file && \ mc-image-helper assert fileExists world/DIM1/some_end_file ================================================ FILE: tests/setuponlytests/spongevanilla_version_compare/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" TYPE: "SPONGEVANILLA" SPONGEVERSION: "1.12.2-7.3.1-RC391" DIFFICULTY: "0" volumes: - ./data:/data ================================================ FILE: tests/setuponlytests/spongevanilla_version_compare/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/spongevanilla_version_compare/verify.sh ================================================ mc-image-helper assert propertyEquals --file=server.properties --property=difficulty --expect=0 ================================================ FILE: tests/setuponlytests/test.sh ================================================ #!/bin/bash set -euo pipefail IFS=$'\n\t' : "${IMAGE_TO_TEST:=itzg/minecraft-server}" # go to script root directory cd "$(dirname "$0")" || exit 1 outputContainerLog() { logs=${1?} echo "${folder} test scenario FAILED" echo ":::::::::::: LOGS :::::::::::::::: $logs :::::::::::::::::::::::::::::::::: " } delta() { startTime=${1?} endTime=$(date +%s) echo "$(( endTime - startTime )) seconds" } # tests that only run the setup files for things like downloads and configuration. setupOnlyMinecraftTest(){ folder=$1 cd "$folder" result=0 if [ -f require.sh ]; then # require.sh scripts can check for environment variables, etc that are required for the test. # The script should exit with a non-zero status to indicate the test requirements are missing # and the test should be skipped if ! bash require.sh; then echo "${folder} SKIP" cd .. return 0 fi fi # false positive since it's used in delta calculations below # shellcheck disable=SC2034 start=$(date +%s) status=PASSED verify= if ! logs=$(docker compose run --rm -e SETUP_ONLY=true -e DEBUG="${DEBUG:-false}" mc 2>&1); then status=FAILED outputContainerLog "$logs" result=1 elif [ -f verify.sh ]; then verify=" verify" if ! docker run --rm --entrypoint bash -v "${PWD}/data":/data -v "${PWD}/verify.sh":/verify "${IMAGE_TO_TEST}" -e /verify; then status=FAILED outputContainerLog "$logs" result=1 fi fi echo "${folder} ${status}${verify} in $(delta start)" docker compose down -v --remove-orphans >& /dev/null cd .. return $result } foldersList=("$@") image="" # Go through each folder in setuponly and test setups if (( $# == 0 )); then readarray -t folders < <(find . -maxdepth 2 -mindepth 2 -name docker-compose.yml -exec dirname "{}" \;) foldersList=("${folders[@]}") image=" using $IMAGE_TO_TEST" fi for folder in "${foldersList[@]}"; do echo "Starting Tests in ${folder}${image}" setupOnlyMinecraftTest "$folder" done ================================================ FILE: tests/setuponlytests/vanilla_world_for_spigot_server/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" TYPE: "PAPER" WORLD: /worlds/world-for-testing.zip # the following are only used to speed up test execution VERSION: 1.18.1 PAPER_CUSTOM_JAR: /servers/fake.jar volumes: - ./worlds:/worlds:ro - ./data:/data ================================================ FILE: tests/setuponlytests/vanilla_world_for_spigot_server/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/vanilla_world_for_spigot_server/verify.sh ================================================ mc-image-helper assert fileExists world/level.dat && \ mc-image-helper assert fileExists world_nether/DIM-1/some_nether_file && \ mc-image-helper assert fileExists world_the_end/DIM1/some_end_file ================================================ FILE: tests/setuponlytests/vanilla_world_for_vanilla_server/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" WORLD: /worlds/world-for-testing.zip # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./worlds:/worlds:ro - ./data:/data # the following are only used to speed up test execution - ./verify.sh:/servers/fake.jar ================================================ FILE: tests/setuponlytests/vanilla_world_for_vanilla_server/verify.sh ================================================ mc-image-helper assert fileExists world/level.dat && \ mc-image-helper assert fileExists world/DIM-1/some_nether_file && \ mc-image-helper assert fileExists world/DIM1/some_end_file ================================================ FILE: tests/setuponlytests/vanillatweaks_file/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: VANILLATWEAKS_FILE: /config/vt-datapacks.json,/config/vt-craftingtweaks.json EULA: "TRUE" SETUP_ONLY: "TRUE" # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./data:/data - ./vt-datapacks.json:/config/vt-datapacks.json:ro - ./vt-craftingtweaks.json:/config/vt-craftingtweaks.json:ro # the following are only used to speed up test execution - ./verify.sh:/servers/fake.jar ================================================ FILE: tests/setuponlytests/vanillatweaks_file/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/vanillatweaks_file/verify.sh ================================================ mc-image-helper assert fileExists "/data/world/datapacks/afk*" mc-image-helper assert fileExists "/data/world/datapacks/graves*" mc-image-helper assert fileExists "/data/world/datapacks/VanillaTweaks_*" mc-image-helper assert fileExists "/data/resourcepacks/VanillaTweaks_*" ================================================ FILE: tests/setuponlytests/vanillatweaks_file/vt-craftingtweaks.json ================================================ { "type": "craftingtweaks", "version": "1.18", "packs": { "quality of life": [ "dropper to dispenser", "double slabs", "back to blocks" ] }, "result": "ok" } ================================================ FILE: tests/setuponlytests/vanillatweaks_file/vt-datapacks.json ================================================ { "type": "datapacks", "version": "1.18", "packs": { "survival": [ "graves", "multiplayer sleep", "afk display", "armor statues", "unlock all recipes", "fast leaf decay", "coordinates hud" ], "items": ["armored elytra"] }, "result": "ok" } ================================================ FILE: tests/setuponlytests/vanillatweaks_file/vt-resourcepacks.json ================================================ { "type": "resourcepacks", "version": "1.18", "packs": { "aesthetic": ["CherryPicking", "BlackNetherBricks", "AlternateBlockDestruction"] }, "result": "ok" } ================================================ FILE: tests/setuponlytests/vanillatweaks_sharecode/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: VANILLATWEAKS_SHARECODE: MGr52E,tF1zL2,LnEDwT EULA: "TRUE" SETUP_ONLY: "TRUE" # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./data:/data # the following are only used to speed up test execution - ./verify.sh:/servers/fake.jar ================================================ FILE: tests/setuponlytests/vanillatweaks_sharecode/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/vanillatweaks_sharecode/verify.sh ================================================ mc-image-helper assert fileExists "/data/world/datapacks/afk*" mc-image-helper assert fileExists "/data/world/datapacks/graves*" mc-image-helper assert fileExists "/data/world/datapacks/VanillaTweaks_488158f.zip" mc-image-helper assert fileExists "/data/resourcepacks/VanillaTweaks_d1d810f.zip" ================================================ FILE: tests/setuponlytests/whitelist_from_scratch/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" WHITELIST: itzg ENFORCE_WHITELIST: "true" OVERRIDE_SERVER_PROPERTIES: "true" # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./data:/data # the following are only used to speed up test execution - ./verify.sh:/servers/fake.jar ================================================ FILE: tests/setuponlytests/whitelist_from_scratch/verify.sh ================================================ mc-image-helper assert jsonPathEquals --file=whitelist.json --path='$[0].name' --expect=itzg mc-image-helper assert jsonPathEquals --file=whitelist.json --path='$[0].uuid' --expect=5cddfd26-fc86-4981-b52e-c42bb10bfdef mc-image-helper assert propertyEquals --file=server.properties --property=white-list --expect=true mc-image-helper assert propertyEquals --file=server.properties --property=enforce-whitelist --expect=true ================================================ FILE: tests/setuponlytests/world_from_tar/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" WORLD: /worlds/world-for-testing.tar # the following are only used to speed up test execution TYPE: CUSTOM CUSTOM_SERVER: /servers/fake.jar VERSION: 1.18.1 volumes: - ./worlds:/worlds:ro - ./data:/data # the following are only used to speed up test execution - ./verify.sh:/servers/fake.jar ================================================ FILE: tests/setuponlytests/world_from_tar/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/world_from_tar/verify.sh ================================================ mc-image-helper assert fileExists world/level.dat ================================================ FILE: tests/setuponlytests/world_from_tarbz2/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" VERSION: ${MINECRAFT_VERSION:-LATEST} WORLD: /worlds/world-for-testing.tar.bz2 volumes: - ./worlds:/worlds:ro - ./data:/data ================================================ FILE: tests/setuponlytests/world_from_tarbz2/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/world_from_tarbz2/verify.sh ================================================ mc-image-helper assert fileExists world/level.dat ================================================ FILE: tests/setuponlytests/world_from_targz/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" VERSION: ${MINECRAFT_VERSION:-LATEST} WORLD: /worlds/world-for-testing.tar.gz volumes: - ./worlds:/worlds:ro - ./data:/data ================================================ FILE: tests/setuponlytests/world_from_targz/verify.sh ================================================ mc-image-helper assert fileExists world/level.dat ================================================ FILE: tests/setuponlytests/world_from_tarzst/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" VERSION: ${MINECRAFT_VERSION:-LATEST} WORLD: /worlds/world-for-testing.tar.zst volumes: - ./worlds:/worlds:ro - ./data:/data ================================================ FILE: tests/setuponlytests/world_from_tarzst/require.sh ================================================ [[ $EXTENDED_TESTS ]] || exit 1 ================================================ FILE: tests/setuponlytests/world_from_tarzst/verify.sh ================================================ mc-image-helper assert fileExists world/level.dat ================================================ FILE: tests/setuponlytests/world_from_zip/docker-compose.yml ================================================ services: mc: restart: "no" image: ${IMAGE_TO_TEST:-itzg/minecraft-server} environment: EULA: "TRUE" SETUP_ONLY: "TRUE" VERSION: ${MINECRAFT_VERSION:-LATEST} WORLD: /worlds/world-for-testing.zip volumes: - ./worlds:/worlds:ro - ./data:/data ================================================ FILE: tests/setuponlytests/world_from_zip/verify.sh ================================================ mc-image-helper assert fileExists world/level.dat ================================================ FILE: tests/test.sh ================================================ #!/bin/bash set -euo pipefail IFS=$'\n\t' # go to script root directory cd "$(dirname "$0")" || exit 1 # go through top level folders and trigger the tests in the subfolders readarray -t folders < <(find . -maxdepth 2 -mindepth 2 -name test.sh -printf '%h\n') for folder in "${folders[@]}"; do cd "$folder" echo "Starting ${folder} Tests" bash ./test.sh cd .. done ================================================ FILE: zensical.toml ================================================ [project] site_name = "Minecraft Server on Docker (Java Edition)" site_url = "https://docker-minecraft-server.readthedocs.io/en/latest/" site_description = "Documentation for Minecraft Server on Docker" repo_name = "itzg/docker-minecraft-server" repo_url = "https://github.com/itzg/docker-minecraft-server" site_author = "itzg" copyright = "Copyright © itzg 2026." docs_dir = "docs" site_dir = "site" [project.theme] features = [ "navigation.tracking", "navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes" ] language = "en" [project.theme.icon] repo = "fontawesome/brands/github" [[project.theme.palette]] media = "(prefers-color-scheme)" toggle.icon = "lucide/sun-moon" toggle.name = "Switch to light mode" [[project.theme.palette]] media = "(prefers-color-scheme: light)" scheme = "default" toggle.icon = "lucide/sun" toggle.name = "Switch to dark mode" [[project.theme.palette]] media = "(prefers-color-scheme: dark)" scheme = "slate" toggle.icon = "lucide/moon" toggle.name = "Switch to light mode"