OCI/Docker image for Minecraft Paper server based on distroless with a cache-optimized image layer layout
UNLICENSE License
Build the Minecraft Paper server software using an optimized container layout format. The JIB-Tool builds OCI or Docker images by separating library dependencies into a different layer than the actual source code (here: Spigot/Paper changes). This improves layer cache utilization, because libraries are less likely to be updated than the source code. In the end, this provides the following benefits:
Note: The last point is currently not implemented, because this project currently uses Paperclip instead of
something custom. Ideal would be the usage of a JIB Gradle task to automatically extract all necessary dependencies
and include a modified version of paperweight
to create a diff, but skipping the jar and shadowing step. It seems
there is no such solution as of now.
paperclip
)
felixklauke/paperspigot
) or 800 MiB (itzg/minecraft-server
)
dos2unix
or nano
(ref)The idea of this project relies on the fact that layers in images are cached and can be re-used. If a layer changes, itself and all following layers needs to be rebuilt. Therefore, it's recommended to place content that frequently changes at the end. See the layout below.
Additionally, to the improved layer layout the GPL compliance needs to addressed. The server implementation violates this license. Distributing it, also seems not be allowed. However, binary patching at the users seems to be allowed like in similar projects BuildTools or paperclip.
The implementation can be found in the GitHub actions file here. It can be summarized in the following steps:
layers.idx
fromCreating the following image layer layout:
Layer | Implementation | Ideal? |
---|---|---|
- | Base Image | Base image |
1 | Paperclip libraries | Paperclip libraries |
2 | Paperclip classes | Paperclip classes |
3 | Vanilla server | Vanilla server |
4 | Server libraries (incl. Paper-API) | Libraries |
5 | Server patch | Snapshot libraries |
6 | - | Module dependencies (Paper-API, Mojang-API) |
7 | - | Static files |
8 | - | Sever patch |
Meanwhile, the recommended setup by JIB includes the right side, because snapshot libraries are updated more often.
The chosen approach has a few limitations and Anti-Patterns, namely:
Distributing the Mojang vanilla server in an extra layer, means we are always including the full, original server. This is inefficient, because changes in Paper's server implementation remove, modify parts of the vanilla server making these unreachable.
Of course the patching process, necessary for GPL compliance, also adds a delay to the server startup. However, this overhead should be drastically reduced, because all necessary files are locally available and only the server implementation needs to be patched.
However, Paperclip supports patch-only start, which you could use to warm up the container. You can specify the
parameter using -Dpaperclip.patchonly=true
.
ref
MALLOC_ARENA_MAX
according to active processor count to help memory consumptionBesides, utilizing a more optimized layer layout:
The project at itzg/minecraft-server downloads the server software at container startup. This increases the container startup time. Containers should be ready to start directly. (Note: The patching process implemented in this project also has a delay) Furthermore, it downloads a file from a foreign source (by default: https://getbukkit.org/). In a very strict setup, this affects the security policies. First, it requires allowing an otherwise unnecessary outgoing connection (e.g. allow list policies) and trusting a foreign source. There no verification process involved, that would detect changed server jar on the upstream server like checksum.
FelixKlauke/paperspigot-docker builds the Paper server software and provides the server jar in the image. This means that there is no download or patching process involved when the container is created and started. However, this means that we are distributing a modified server software, which is against the GPL. Latter required us to create solutions like BuildTools or paperclip to build server on our own or binary patch it.
Furthermore, the EULA is automatically accepted, which hides it from the actual users. The process should be opt-in explicitly to make it transparent to users.
Running it, requires accepting the EULA. This can be specified using environment variables:
docker run --env JAVA_TOOL_OPTIONS="-Dcom.mojang.eula.agree=true" ghcr.io/games647/paperclip:1.18.1 ...
podman run --env JAVA_TOOL_OPTIONS="-Dcom.mojang.eula.agree=true" ghcr.io/games647/paperclip:1.18.1 ...
podman run --memory
or docker run --memory
.$ java-buildpack-memory-calculator --total-memory 2G --loaded-class-count 500 --thread-count 20
-XX:MaxDirectMemorySize=10M -XX:MaxMetaspaceSize=16503K -XX:ReservedCodeCacheSize=240M -Xss1M -Xmx1804168K
JDK_JAVA_OPTIONS
environment variable. If you want to modify or remove flags, use--env JDK_JAVA_OPTIONS=...
to override it. However, if you want to add additional flags use the environment variable--env JAVA_TOOL_OPTIONS="-Dcom.mojang.eula.agree=true -Xmx1G"
.--env JDK_JAVA_OPTIONS="-Dcom.mojang.eula.agree=true -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=localhost:5005"
Besides, the obvious JIB-Tool:
nektos/act allows running GitHub actions locally. Cloning the Paper
repository requires configuring a GitHub access token. Generate one here and
set it using export GITHUB_TOKEN=ghp_[...]
. Note tha
Docker:
act --reuse --bind --secret GITHUB_TOKEN="$GITHUB_TOKEN"
Podman:
Podman requires additional setup. Assuming podman is configured it rootless mode and systemd is present:
# Enable the podman socket using systemd
systemctl enable --now --user podman.socket
# Emulate a Docker socket
export DOCKER_HOST=unix://"$XDG_RUNTIME_DIR"/podman/podman.sock
act --reuse --bind --container-daemon-socket "$XDG_RUNTIME_DIR"/podman/podman.sock --secret GITHUB_TOKEN="$GITHUB_TOKEN"
--reuse
: Keep container state. Prevents additional rebuilds--secret
: Required to download the Paper repository in the checkout step--bind
: Bind the container to the working directorywagoodman/dive inspect the contents of the image for each layer. Inspect the separation process. Example:
dive podman://localhost/IMAGE