Compare commits

...

33 Commits

Author SHA1 Message Date
dependabot[bot] 5219c8ca32 build(deps): bump the patches group in /docs with 2 updates
Bumps the patches group in /docs with 2 updates: [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) and [zensical](https://github.com/zensical/zensical).


Updates `pymdown-extensions` from 11.0 to 11.0.1
- [Release notes](https://github.com/facelessuser/pymdown-extensions/releases)
- [Commits](https://github.com/facelessuser/pymdown-extensions/compare/11.0...11.0.1)

Updates `zensical` from 0.0.46 to 0.0.47
- [Release notes](https://github.com/zensical/zensical/releases)
- [Commits](https://github.com/zensical/zensical/compare/v0.0.46...v0.0.47)

---
updated-dependencies:
- dependency-name: pymdown-extensions
  dependency-version: 11.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patches
- dependency-name: zensical
  dependency-version: 0.0.47
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patches
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-08 08:33:20 +00:00
renovate[bot] 702d05e60c Update dependency itzg/mc-monitor to v0.16.10 (#4148)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-07 18:52:58 -05:00
David Gaag 32b026d0df Fix GTNH versions.json parsing (#4146) 2026-07-06 06:59:21 -05:00
Leon Kampwerth d9ee3ee68c Refactor deploy gtnh (#4141) 2026-07-05 07:43:43 -05:00
Geoff Bourne ab505fa43c Pass GH_TOKEN to containers started during tests (#4136) 2026-07-04 23:17:54 -05:00
dependabot[bot] 78ecbb5749 build(deps): bump click from 8.4.1 to 8.4.2 in /docs in the patches group (#4143) 2026-07-01 07:08:31 -05:00
renovate[bot] e992e71988 Update dependency itzg/mc-monitor to v0.16.8 (#4142) 2026-06-30 19:56:26 -05:00
Samuel Stanley 3cc2e64163 Add example for loading mods from a local directory (#4138) 2026-06-28 07:50:59 -05:00
Chip Wolf ‮ f1dd0f3d71 packwiz: re-load LOAD_ENV_FROM_FILE after the installer runs (#4137) 2026-06-26 10:18:53 -05:00
Harrison Black 292b2f757e paper: Fix boolean flag always return true (#4134) 2026-06-25 12:36:59 -05:00
Harrison Black 82043d556e Add purpur support for ServerLibraryCleaner (#4133) 2026-06-25 12:35:29 -05:00
Geoff Bourne d9bf26cc17 GTNH: allow for spaces in GTNH_PACK_VERSION (#4132) 2026-06-25 10:21:50 -05:00
dependabot[bot] 8b076e4868 build(deps): bump docker/build-push-action from 6 to 7 (#4124) 2026-06-24 12:20:04 -05:00
dependabot[bot] 4c534490f2 build(deps): bump pymdown-extensions from 10.21.3 to 11.0 in /docs (#4128) 2026-06-24 07:13:00 -05:00
dependabot[bot] 314beec407 build(deps): bump the patches group in /docs with 4 updates (#4127) 2026-06-24 06:54:41 -05:00
Geoff Bourne 2ecf6776e2 Handle non-specific file types in start-utils extraction logic (#4121) 2026-06-23 07:34:36 -05:00
Geoff Bourne dadecdac28 Improved and refactored sponsor labeler workflow (#4123) 2026-06-22 15:19:38 -05:00
Geoff Bourne 737a9879f7 Add github workflow to label issues/prs authored by sponsor (#4120) 2026-06-21 14:59:06 -05:00
renovate[bot] 8b5f7ed771 Update dependency itzg/mc-image-helper to v1.61.1 (#4118) 2026-06-20 17:53:37 -05:00
Geoff Bourne d20cd2dfe1 Add jattach utility (#4114) 2026-06-17 21:26:48 -05:00
renovate[bot] b67d88f713 Update dependency itzg/restify to v1.7.16 (#4112) 2026-06-16 07:54:05 -05:00
James Wagner 0dce4ed863 Added: STOP_SERVER_DELAY_COMMAND (#4109) 2026-06-16 07:22:32 -05:00
renovate[bot] 85170775fd Update dependency itzg/mc-image-helper to v1.61.0 (#4111) 2026-06-16 06:59:00 -05:00
renovate[bot] 11a78f5070 Update dependency itzg/mc-server-runner to v1.15.0 (#4110) 2026-06-15 19:15:21 -05:00
renovate[bot] 56179b56f5 Update dependency itzg/easy-add to v0.8.14 (#4105) 2026-06-14 22:31:00 -05:00
renovate[bot] cfa58bb78c Update dependency itzg/mc-monitor to v0.16.7 (#4106) 2026-06-14 22:15:36 -05:00
renovate[bot] 6094348d8f Update dependency itzg/restify to v1.7.15 (#4108) 2026-06-14 21:57:05 -05:00
renovate[bot] ab018d75a0 Update dependency itzg/mc-server-runner to v1.14.8 (#4107) 2026-06-14 21:07:08 -05:00
renovate[bot] 1a8844cb62 Update dependency itzg/rcon-cli to v1.7.6 (#4103) 2026-06-13 07:12:28 -05:00
Leon Kampwerth 2433473015 Improve get gtnh download path (#4101) 2026-06-12 07:14:52 -05:00
renovate[bot] 4495d9f8a7 Update dependency itzg/mc-image-helper to v1.60.2 (#4102) 2026-06-12 06:57:25 -05:00
Echo Nar 56c4c95606 examples: podman quadlet example (#4099) 2026-06-10 07:17:02 -05:00
dependabot[bot] 47efe3e4bb build(deps): bump the patches group in /docs with 2 updates (#4100) 2026-06-10 07:14:40 -05:00
28 changed files with 436 additions and 201 deletions
+4 -4
View File
@@ -104,7 +104,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7
with:
# for build-files step
fetch-depth: 0
@@ -141,13 +141,13 @@ jobs:
org.opencontainers.image.authors=Geoff Bourne <itzgeoff@gmail.com>
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v4.1.0
uses: docker/setup-buildx-action@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v4.1.0
- name: Build for test
uses: docker/build-push-action@v7.2.0
uses: docker/build-push-action@v7
with:
platforms: linux/amd64
tags: ${{ env.IMAGE_TO_TEST }}
@@ -187,7 +187,7 @@ jobs:
password: ${{ github.token }}
- name: Build and push
uses: docker/build-push-action@v7.2.0
uses: docker/build-push-action@v7
if: github.actor == github.repository_owner
with:
platforms: ${{ matrix.platforms }}
+16
View File
@@ -0,0 +1,16 @@
name: Sponsor Labeler
on:
issues:
types:
- opened
pull_request_target:
types:
- opened
jobs:
label-sponsor:
uses: itzg/github-workflows/.github/workflows/sponsor-labeler.yml@main
with:
author: ${{ github.event.issue.user.login || github.event.pull_request.user.login }}
number: ${{ github.event.issue.number || github.event.pull_request.number }}
+35
View File
@@ -0,0 +1,35 @@
name: Verify Docs
on:
pull_request:
paths:
- 'docs/**'
- 'zensical.toml'
- '.github/workflows/verify-docs.yml'
workflow_dispatch:
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build docs image
uses: docker/build-push-action@v7
with:
context: .
file: docs/Dockerfile
load: true
tags: docs-verifier:latest
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Verify rendering
run: |
docker run --rm \
-v ${{ github.workspace }}:/docs \
docs-verifier:latest build --strict
+4 -4
View File
@@ -53,16 +53,16 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7
with:
# for build-files step
fetch-depth: 0
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v4.1.0
uses: docker/setup-buildx-action@v4
- name: Confirm multi-arch build
uses: docker/build-push-action@v7.2.0
uses: docker/build-push-action@v7
with:
platforms: ${{ matrix.platforms }}
# ensure latest base image is used
@@ -73,7 +73,7 @@ jobs:
cache-from: type=gha,scope=${{ matrix.variant }}
- name: Build for test
uses: docker/build-push-action@v7.2.0
uses: docker/build-push-action@v7
with:
# Only build single platform since loading multi-arch image into daemon fails with
# "docker exporter does not currently support exporting manifest lists"
+6 -6
View File
@@ -26,36 +26,36 @@ ARG APPS_REV=1
ARG GITHUB_BASEURL=https://github.com
# renovate: datasource=github-releases packageName=itzg/easy-add
ARG EASY_ADD_VERSION=0.8.13
ARG EASY_ADD_VERSION=0.8.14
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.14
ARG RESTIFY_VERSION=1.7.16
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.5
ARG RCON_CLI_VERSION=1.7.6
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.6
ARG MC_MONITOR_VERSION=0.16.10
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.7
ARG MC_SERVER_RUNNER_VERSION=1.15.0
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.60.1
ARG MC_HELPER_VERSION=1.61.1
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
+1
View File
@@ -33,6 +33,7 @@ apk add --no-cache -U \
libwebp \
libcap \
numactl \
jattach \
${EXTRA_ALPINE_PACKAGES}
# Download and install patched knockd
+1
View File
@@ -30,6 +30,7 @@ apt-get install -y \
libpcap0.8 \
libnuma1 \
libcap2-bin \
jattach \
${EXTRA_DEB_PACKAGES}
# Clean up APT when done
+6 -1
View File
@@ -26,7 +26,10 @@ docker run -d --pull=always \
By default, supported server types remove stale server libraries during installation by setting `CLEAN_SERVER_LIBRARIES` to `true`. Set `CLEAN_SERVER_LIBRARIES` to `false` to disable this cleanup if it causes unexpected behavior.
Currently, library cleanup is supported by `TYPE=PAPER`. Other server jar types are not yet supported.
Currently, library cleanup is supported by
- `TYPE=PAPER`
- `TYPE=PURPUR`
## Running as alternate user/group ID
@@ -50,6 +53,8 @@ If this behavior interferes with the log content, then disable TTY or remove the
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.
To set a custom command to run at the start of this delay period, set `STOP_SERVER_DELAY_COMMAND` to the full command. This will run in place of the announcement.
!!! 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.
+17 -1
View File
@@ -35,4 +35,20 @@ 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 <https://github.com/itzg/docker-minecraft-server/commit/REVISION>
- `org.opencontainers.image.version` : image tag and variant [as described in this page](../versions/java.md)
- `org.opencontainers.image.version` : image tag and variant [as described in this page](../versions/java.md)
## jattach
This image bundles the [jattach](https://github.com/jattach/jattach) utility for attaching to running Java processes. It is described as
> The utility to send commands to a JVM process via Dynamic Attach mechanism.
>
> All-in-one jmap + jstack + jcmd + jinfo functionality in a single tiny program.
When exec'ed interactively into the container, jattach can be invoked against the Minecraft server's java process by using commands similar to the following
!!! example
```shell
jattach $(pgrep java) threaddump
```
+13
View File
@@ -42,6 +42,19 @@ There are optional volume paths that can be attached to supply content to be cop
`/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`.
!!! example "Loading mods from a local directory"
This is the most basic example, where `./mods` is mounted as `/mods`. If the directory with the server's mods is located somewhere else, `./mods` can be modified accordingly.
```yaml
environment:
EULA: "TRUE"
TYPE: "NEOFORGE"
volumes:
- "./data:/data"
- "./mods:/mods"
```
`/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`.
+2
View File
@@ -13,6 +13,8 @@ docker run -d --pull=always \
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).
If the pack ships a `.env` file that you reference with `LOAD_ENV_FROM_FILE`, that file is re-loaded immediately after the packwiz installer runs, so the freshly downloaded values are applied to the remaining startup stages. Note that `TYPE` and `VERSION` are resolved earlier (before the deploy is dispatched), so a pack-provided `.env` cannot change those from within packwiz; use `LOAD_ENV_FROM_GENERIC_PACK` or `LOAD_ENV_FROM_ARCHIVE` for those.
!!! 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.
+6 -6
View File
@@ -1,8 +1,8 @@
click==8.4.1
click==8.4.2
colorama==0.4.6
deepmerge==2.0
deepmerge==2.1.0
ghp-import==2.1.0
griffe==2.0.2
griffe==2.1.0
Jinja2==3.1.6
Markdown==3.10.2
MarkupSafe==3.0.3
@@ -11,15 +11,15 @@ mkdocs==1.6.1
mkdocs-autorefs==1.4.4
mkdocs-get-deps==0.2.2
mkdocstrings==1.0.4
mkdocstrings-python==2.0.3
mkdocstrings-python==2.0.5
packaging==26.2
pathspec==1.1.1
platformdirs==4.10.0
Pygments==2.20.0
pymdown-extensions==10.21.3
pymdown-extensions==11.0.1
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.43
zensical==0.0.47
+2 -2
View File
@@ -6,7 +6,7 @@ The container can host an SSH console. It is enabled by setting `ENABLE_SSH` to
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.
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.
@@ -15,7 +15,7 @@ The SSH server runs on port `2222` inside the container.
!!! 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).
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`).
+2 -2
View File
@@ -26,9 +26,9 @@ When a connection is established, the last 50 (by default, configurable with `WE
!!! 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).
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 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).
@@ -35,7 +35,7 @@ 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.
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. The folder name starts with `gtnh-upgrade-` followed by a timestamp. Set the environment variable `GTNH_DELETE_BACKUPS` to true to delete all backup folders at startup.
## server.properties defaults
+7 -1
View File
@@ -246,6 +246,12 @@ alternatively, you can mount: <code>/etc/localtime:/etc/localtime:ro
<td><code></code></td>
<td>⬜️</td>
</tr>
<tr>
<td><code>STOP_SERVER_DELAY_COMMAND</code></td>
<td>To set a custom command to run at the start of this delay period, set <code>STOP_SERVER_DELAY_COMMAND</code> to the full command. This will run in place of the announcement.</td>
<td><code></code></td>
<td>⬜️</td>
</tr>
<tr>
<td><code>PROXY</code></td>
<td>You may configure the use of an HTTP/HTTPS proxy by passing the proxy's URL</td>
@@ -670,4 +676,4 @@ This image maps known server properties as described in [this section](configura
<td></td>
<td><code></code></td>
<td>⬜️</td>
</tr> -->
</tr> -->
+45
View File
@@ -0,0 +1,45 @@
# Podman Quadlet Example
This example demonstrates how to deploy multiple autoscaling minecraft servers behind mc-router with [podman quadlets](https://docs.podman.io/en/stable/markdown/podman-systemd.unit.5.html).
The commands listed below assume rootless podman but can easily be modified for rootful.
## mc-router.host label
In `mc@.container`, replace `example.com` with your domain.
## Server instance configuration
Each server instance requries an environment file with a matching name in the mc folder; e.g. instance `example` uses `mc/example.env`.[^1]
## Container auto-removal fix
Once the quadlets files are installed and daemon-reloaded, the generated service file needs to be edited due to generated quadlets always adding `--rm`.[^2]
To fix this, run `systemctl --user edit mc@.service` and replace the `ExecStart` entry with a copy that substitues `--restart=unless-stopped` where `--rm` is.
The drop-in should look something like this:
```ini
[Service]
ExecStart=
ExecStart=/usr/bin/podman run --name %p-%i --replace --restart=unless-stopped ...
```
## Start containers/services
```sh
systemctl --user enable --now podman.socket
systemctl --user start mc@example.service mc-router.service
# instances are enabled by symlinking from the template
ln -s ${XDG_CONFIG_HOME}/containers/systemd/mc@.service ${XDG_CONFIG_HOME}/containers/systemd/mc@example.service
```
## Rootless notes
If running rootless, be sure to enable lingering with `sudo loginctl enable-linger $USER`.
Also note that source IPs are currently lost due to how rootless podman handles custom networks.
This should be fixed in a future podman release.[^3]
[^1]: The base config is a [template file](https://docs.podman.io/en/stable/markdown/podman-systemd.unit.5.html#template-files) with instance names after the @ sign .
[^2]: <https://github.com/podman-container-tools/podman/discussions/28837>
[^3]: <https://github.com/podman-container-tools/podman/pull/28478>
@@ -0,0 +1,24 @@
[Unit]
Description=Minecraft proxy with autoscaling support
[Container]
Image=docker.io/itzg/mc-router
ContainerName=%N
AutoUpdate=registry
UserNS=host
Volume=%t/podman/podman.sock:/var/run/docker.sock:ro
Network=minecraft.network
PublishPort=25565:25565
SecurityLabelDisable=true
Environment=\
"IN_DOCKER=true" \
"AUTO_SCALE_DOWN=true" \
"AUTO_SCALE_UP=true" \
"AUTO_SCALE_DOWN_AFTER=10m" \
"AUTO_SCALE_ASLEEP_MOTD='Server is asleep. Join again to wake it up!'"
[Service]
Restart=always
[Install]
WantedBy=default.target
+9
View File
@@ -0,0 +1,9 @@
# General options
INIT_MEMORY=1G
MAX_MEMORY=4G
# Server options
EULA=TRUE
VIEW_DISTANCE=16
DIFFICULTY=normal
MOTD=Example Server!\nRunning version %VERSION%
+26
View File
@@ -0,0 +1,26 @@
[Unit]
Description=Minecraft server instance
Before=mc-router.service
[Container]
Image=docker.io/itzg/minecraft-server
ContainerName=%p-%i
AutoUpdate=registry
UserNS=auto
Volume=%p-%i:/data:Z,U
Network=minecraft.network
PodmanArgs=--tty --interactive
HealthCmd="/usr/local/bin/mc-health"
HealthInterval=10s
HealthRetries=20
HealthStartPeriod=1m
HealthTimeout=10s
Label=mc-router.host=%p-%i.example.com
EnvironmentFile=./mc/%i.env
[Service]
Restart=on-abnormal
RemainAfterExit=yes
[Install]
WantedBy=default.target
@@ -0,0 +1,2 @@
[Network]
Driver=bridge
+167 -166
View File
@@ -6,87 +6,71 @@
# Define setup functions
function getGTNHdownloadPath(){
gtnh_download_path=""
local release_object=""
local current_java_version=""
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"
# Select release JSON object
if [ "$GTNH_PACK_VERSION" == "latest-dev" ]; then
if ! release_object="$(
curl -fsSL "https://downloads.gtnewhorizons.com/versions.json" \
| jq -r '(.versions // .)|to_entries|sort_by(.value.releaseDate)|map(select(.value.title=="Beta release"))|.[-1]'
)"; then logError "Failed to retrieve release from https://downloads.gtnewhorizons.com/versions.json"
exit 1
fi
log "Selected $(jq '.key' <<< "$release_object") as latest dev version for download."
elif [ "$GTNH_PACK_VERSION" == "latest" ]; then
if ! release_object="$(
curl -fsSL "https://downloads.gtnewhorizons.com/versions.json" \
| jq -r '(.versions // .)|to_entries|sort_by(.value.releaseDate)|map(select(.value.title=="Stable release"))|.[-1]'
)"; then logError "Failed to retrieve release from https://downloads.gtnewhorizons.com/versions.json"
exit 1
fi
log "Selected $(jq '.key' <<< "$release_object") as latest version for download."
else
if ! release_object="$(
curl -fsSL "https://downloads.gtnewhorizons.com/versions.json" \
| jq -r --arg USRIN "$GTNH_PACK_VERSION" '(.versions // .)|to_entries|sort_by(.value.releaseDate)|map(select(.key==$USRIN))|.[]'
)"; then logError "Failed to retrieve release from https://downloads.gtnewhorizons.com/versions.json"
exit 1
fi
log "Selected $(jq -r '.key' <<< "$release_object") as matching version for download."
fi
if [[ -z $release_object ]]; then
logError "No release found for GTNH_PACK_VERSION=$GTNH_PACK_VERSION. Please check if the version is valid."
exit 1
fi
mapfile -t packs <<< "$packs_data"
# Select compatible server files for java version
if (( current_java_version == 8 )); then
gtnh_download_path=$(jq -r '.value.server.java8Url' <<< "$release_object")
log "Use GTNH Server Java 8 release."
elif (( current_java_version >= 17 )); then
if (( current_java_version > $(jq '.value.maxJavaVersion' <<< "$release_object") )); then
logError "Container Java version $current_java_version is not supported by GTNH. Try an older release."
exit 1
fi
gtnh_download_path=$(jq -r '.value.server.java17_2XUrl' <<< "$release_object")
log "Use GTNH Server Java 17+ release."
else
logError "Container Java version $current_java_version is not supported by GTNH."
exit 1
fi
if [[ -z $gtnh_download_path ]]; then
logError "Server files not found! Please check if the selected GTNH_PACK_VERSION=$GTNH_PACK_VERSION is valid."
exit 1
fi
debug "Download source URL: $gtnh_download_path"
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(){
@@ -96,80 +80,125 @@ function deleteGTNHbackup(){
fi
}
function downloadGTNH(){
local gtnh_download=""
base_dir=/data/.tmp/gtnh_base
debug "Creating temporary /data/.tmp/packs folder for GTNH download"
mkdir -p /data/.tmp/packs
trap 'rm -rf /data/.tmp' EXIT
log "Downloading $gtnh_download_path."
if ! gtnh_download=$(mc-image-helper get -o /data/.tmp/packs --output-filename --skip-up-to-date "$gtnh_download_path"); then
logError "Failed to download $gtnh_download_path"
exit 1
fi
debug "Downloaded GTNH server files to $gtnh_download"
# Unpacking Server files into temporary directory
log "Unpacking Server Files into $base_dir"
mkdir -p "${base_dir}"
extract "${gtnh_download}" "${base_dir}"
# Remove any eula file since container manages it
debug "Removing eula.txt from $base_dir"
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
debug "Base directory of unpacked Server Files: $base_dir"
}
function updateGTNHresource(){
local resource="$1"
local new_resource="$2"
log "Trying to update $resource"
if [[ -z "$resource" ]]; then
logWarning "No resource specified for update"
return 0
fi
if [[ -e "$resource" ]]; then
log "Deleting existing resource: $resource"
if ! rm -rf "$resource"; then
logError "Failed to delete resource: $resource"
exit 1
fi
fi
if [[ -d "$new_resource" ]]; then
log "Copying $new_resource to /data"
if ! cp -r "$new_resource" "/data/"; then
logError "Failed to copy $new_resource to /data"
exit 1
fi
return 0
fi
if [[ -f "$new_resource" ]]; then
log "Copying $new_resource to /data"
if ! cp "$new_resource" "/data/"; then
logError "Failed to copy $new_resource to /data"
exit 1
fi
return 0
fi
logWarning "Resource update failed: $new_resource does not exist!"
return 0
}
function updateGTNH(){
# Get the current date and time
current_datetime=$(date +%Y-%m-%dT%H:%M)
local current_datetime=""
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
# base_dir is already defined in handleGTNH function
local folders_to_update=("libraries" "mods" "resources" "scripts")
local 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")
local config_folder="config"
local backup_folder="/data/gtnh-upgrade-$current_datetime"
local journey_map_folder="JourneyMapServer"
# 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
# Update resources from unpacked server files to /data
for resource in "${folders_to_update[@]}" "$config_folder" "${files_to_update[@]}"; do
local resource_path="/data/$resource"
local new_resource_path="$base_dir/$resource"
# Ensure the config folder exists
if [[ ! -d "$config_folder" ]]; then
log "$config_folder does not exist. Creating it now."
mkdir -p "$config_folder"
fi
debug "Updating resource: $resource_path with new resource: $new_resource_path"
updateGTNHresource "$resource_path" "$new_resource_path"
done
# 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/"
if [[ ! -d "/data/$config_folder" ]]; then
log "/data/$config_folder folder does not exist. Creating it now."
mkdir -p "/data/$config_folder"
fi
log "Restoring $journey_map_folder to /data/$config_folder"
cp -r "$backup_folder/$journey_map_folder" "/data/$config_folder/"
else
logWarning "$journey_map_folder not found in backup!"
fi
# Copy the changelog file to /data
local gtnh_changelog_file=""
gtnh_changelog_file=$(mc-image-helper find --max-depth=1 --type=file --name=changelog*.md "$base_dir")
if [[ -n "$gtnh_changelog_file" ]]; then
debug "Found changelog file: $gtnh_changelog_file"
log "Copying changelog file to /data"
cp -f "$gtnh_changelog_file" /data/
fi
@@ -186,49 +215,19 @@ function handleGTNH() {
deleteGTNHbackup
fi
if [[ -n $GTNH_PACK_VERSION ]] && isFalse "$SKIP_GTNH_UPDATE_CHECK" ; then
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
# Download and unpack GTNH server files
downloadGTNH
log "GTNH server files downloaded and unpacked to $base_dir."
# Split installation from update path. Check for version file.
if [[ -f /data/.gtnh-version ]]; then
@@ -238,10 +237,12 @@ function handleGTNH() {
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"
# Cleaning up temporary files
rm -rf /data/.tmp
else
log "No update required: /data/.gtnh-version matches the selected version $(basename "$gtnh_download_path")."
+2 -2
View File
@@ -24,7 +24,7 @@ elif [[ $PAPER_DOWNLOAD_URL ]]; then
--url="$PAPER_DOWNLOAD_URL"
)
if [[ $CLEAN_SERVER_LIBRARIES ]]; then
if isTrue $CLEAN_SERVER_LIBRARIES; then
args+=(--clean-libraries)
fi
@@ -45,7 +45,7 @@ else
args+=(--build="$PAPER_BUILD")
fi
if [[ $CLEAN_SERVER_LIBRARIES ]]; then
if isTrue $CLEAN_SERVER_LIBRARIES; then
args+=(--clean-libraries)
fi
+13 -4
View File
@@ -5,6 +5,7 @@ IFS=$'\n\t'
: "${PURPUR_BUILD:=LATEST}"
: "${PURPUR_DOWNLOAD_URL:=}"
: "${PURPUR_CONFIG_REPO:=}"
: "${CLEAN_SERVER_LIBRARIES:=true}"
# shellcheck source=start-utils
. "$(dirname "$0")/start-utils"
@@ -13,10 +14,15 @@ 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
args=(
--output-directory=/data
--results-file="$resultsFile"
--url="${PURPUR_DOWNLOAD_URL}"
)
if isTrue "$CLEAN_SERVER_LIBRARIES"; then
args+=(--clean-libraries)
fi
if ! mc-image-helper install-purpur "${args[@]}"; then
logError "Failed to download from custom Purpur URL"
exit 1
fi
@@ -29,6 +35,9 @@ else
if [[ $PURPUR_BUILD ]]; then
args+=(--build="$PURPUR_BUILD")
fi
if isTrue "$CLEAN_SERVER_LIBRARIES"; then
args+=(--clean-libraries)
fi
if ! mc-image-helper install-purpur "${args[@]}"; then
logError "Failed to download Purpur"
exit 1
+3
View File
@@ -481,6 +481,9 @@ fi
if [[ ${STOP_SERVER_ANNOUNCE_DELAY} ]]; then
mcServerRunnerArgs+=(--stop-server-announce-delay "${STOP_SERVER_ANNOUNCE_DELAY}s")
fi
if [[ ${STOP_SERVER_DELAY_COMMAND} ]]; then
mcServerRunnerArgs+=(--stop-server-delay-command "${STOP_SERVER_DELAY_COMMAND}s")
fi
if isTrue "${ENABLE_SSH}"; then
mcServerRunnerArgs+=(--remote-console)
fi
+10
View File
@@ -50,6 +50,16 @@ function handlePackwiz() {
logError "Failed to run packwiz installer"
exit 1
fi
# packwiz may have just downloaded/updated the env file referenced by
# LOAD_ENV_FROM_FILE. The initial load in start-configuration happened
# before this point, so re-load it now to pick up the pack's values for
# the remaining setup stages (server.properties, JVM args, etc.).
if [[ ${LOAD_ENV_FROM_FILE:-} ]]; then
if ! loadEnvFromFile "${LOAD_ENV_FROM_FILE}"; then
exit 1
fi
fi
fi
}
+11
View File
@@ -560,6 +560,17 @@ function extract() {
# remaining args are paths within the archive to extract; if none, extract everything
type=$(file -b --mime-type "${src}")
if [[ "$type" == application/octet-stream ]]; then
logWarning "Detected non-specific file type $type for $src"
case "$src" in
*.zip)
log "Assuming zip from extension"
type=application/zip
;;
# otherwise fall through to
esac
fi
case "${type}" in
application/zip)
unzip -o -q -d "${destDir}" "${src}" "$@"
+1 -1
View File
@@ -46,7 +46,7 @@ setupOnlyMinecraftTest(){
start=$(date +%s)
status=PASSED
verify=
if ! logs=$(docker compose run --rm -e SETUP_ONLY=true -e DEBUG="${DEBUG:-false}" mc 2>&1); then
if ! logs=$(docker compose run --rm -e SETUP_ONLY=true -e DEBUG="${DEBUG:-false}" -e GH_TOKEN="${GH_TOKEN:-}" mc 2>&1); then
status=FAILED
outputContainerLog "$logs"
result=1