Minecraft server deployment config and admin panel
  • Vue 21.5%
  • TypeScript 20.9%
  • JavaScript 18.1%
  • CSS 17.9%
  • Python 9.3%
  • Other 12.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Flo 62c2f78ef1
All checks were successful
Release client pack / release (push) Successful in 3s
Add server Distant Horizons and Komodo restart countdown
2026-11-07 22:25:19 +01:00
.forgejo/workflows Add Aeronautics and Distant Horizons with locked Forgejo pack releases 2026-11-06 22:29:38 +01:00
build Initial commit 2026-11-03 23:03:50 +01:00
config Fix compose.yaml 2026-11-03 23:15:59 +01:00
data Fix compose.yaml 2026-11-03 23:15:59 +01:00
manifests Add server Distant Horizons and Komodo restart countdown 2026-11-07 22:25:19 +01:00
panel Fix compose.yaml 2026-11-03 23:15:59 +01:00
scripts Add server Distant Horizons and Komodo restart countdown 2026-11-07 22:25:19 +01:00
.dockerignore Initial commit 2026-11-03 23:03:50 +01:00
.env.example Fix compose.yaml 2026-11-03 23:15:59 +01:00
.gitignore Add Aeronautics and Distant Horizons with locked Forgejo pack releases 2026-11-06 22:29:38 +01:00
compose.yaml Fix compose.yaml 2026-11-03 23:15:59 +01:00
README.md Add server Distant Horizons and Komodo restart countdown 2026-11-07 22:25:19 +01:00

node-k-craft

Docker Compose configuration for a private Minecraft Java server with Create 6.0.10, Minecraft 1.21.1 and NeoForge 21.1.248, a separate NeoForge authentication lobby and optional Paper server, and Authentik at auth.node-k.at. Nothing is deployed by this repository.

Paper cannot load Create. The authentication lobby and Create world both run NeoForge with the same mod set, because the Create client requires that handshake before joining. Paper plugins run on an optional separate Paper server. Plugin functionality needed in that world must use a compatible mod. Players need a Minecraft Java account, a matching modded client, and an Authentik account authorized for this application.

Minecraft client -> HAProxy :25565 -> Velocity (Microsoft authentication)
                                     |-> NeoForge lobby -> browser Authentik login
                                     |-> NeoForge/Create after successful login
Browser -> HTTPS reverse proxy -> Velocity OIDC callback :8085
                                  |-> MariaDB (account links)

Authentication behavior

On each new Minecraft connection, unauthenticated players enter the spectator/peaceful lobby and receive a browser login link. Successful Authentik authentication sends them to Create. The first successful login links their Minecraft username to their Authentik sub; subsequent logins must use that same Authentik identity. Disconnecting revokes the Minecraft authentication session and outstanding login links. Existing browser SSO can make the next Authentik login quick; this does not force password/MFA entry every time. Configure Authentik flows if you require fresh MFA on every authorization.

An existing OIDC plugin provides code exchange, PKCE, token verification, SQL linking and player routing. Its upstream defaults allow session reuse and its initialization errors can leave Velocity running without authentication. build/oidc-auth.patch changes it for this deployment:

  • Registers a startup gate and exposes /ready only after successful initialization. HAProxy checks that endpoint before admitting Minecraft connections, including after proxy restarts. A missing/failed auth plugin leaves the public entry closed.
  • Binds authentication and browser flows to the live Player instance; discards them on disconnect. No cookie/IP/device-fingerprint session reuse.
  • Validates an OIDC nonce in addition to the upstream PKCE/state/token validation, and supports confidential-client HS256 signing without requiring a public JWKS key.
  • Removes local password registration and account-type management commands, serializes callbacks with disconnect cleanup, and stores new account links before granting access.
  • Allows OIDC subjects longer than 36 characters in a fresh database.

The patch is built against immutable upstream commit 2af266a8ecc065b788d78d029bd6182b309b2cb3. Do not replace it with the unmodified Modrinth JAR. Do not reuse an existing upstream auth database without reviewing its schema and local-password accounts. /ready is internal to HAProxy and is not exposed through the supplied HTTPS configuration.

Online-mode remains enabled at Velocity, so players must also own their Minecraft identity. Backend online-mode is disabled because Velocity authenticates and forwards the real UUID using a shared secret. The backends and database have no published ports. Keep them private; never publish their game or RCON ports. HAProxy sends PROXY v2 so Velocity receives the actual client address; connect through HAProxy, not directly to Velocity.

Configure Authentik

In the Authentik admin interface at https://auth.node-k.at:

  1. Create an OAuth2/OpenID Connect provider for a confidential client, authorization-code flow. An RSA signing key (RS256) is recommended; leaving the signing key unset uses Authentik’s HS256 mode, which the patched bridge validates with the confidential client secret. Enable the standard openid and profile scope mappings, including preferred_username.
  2. Set the redirect URI to strict matching: https://mc-login.node-k.at/login/callback. Substitute your callback hostname if different.
  3. Create an application with slug minecraft, linked to this provider. Its issuer should be https://auth.node-k.at/application/o/minecraft/; copy the actual issuer from its discovery document if you use another slug or issuer mode.
  4. Bind an access policy/group to the application for allowed players. Every account allowed to authorize this application can enroll a Minecraft account; the stack does not impose a separate Authentik group filter or one-Minecraft-account-per-user policy.
  5. Copy the client ID and secret into the deployment host's .env. The bridge uses the standard stable sub claim as its account identifier, not email or username. No LDAP outpost is needed.

The callback domain is a new proposed name, not a DNS record created by this repository. Configure DNS and HTTPS before opening the Minecraft listener. An HTTP forward-auth middleware by itself cannot authenticate Minecraft TCP traffic.

Deploy on your server

Use Docker Engine with the Compose plugin. A starting allocation is roughly 14–18 GiB host RAM: 6 GiB Java heap/8 GiB container for Create, 2 GiB heap/3 GiB container for the NeoForge lobby, plus the proxy, database and OS. Adjust for player count, loaded chunks and machinery.

cp .env.example .env
# Generate four distinct values; paste them into the four secret settings.
openssl rand -hex 32

Fill all empty settings in .env; use a single-line client secret without YAML quote/backslash characters because the container substitutes it into YAML. Set EULA=TRUE only after accepting https://www.minecraft.net/eula. Keep .env private; it is ignored by Git. Review memory/player settings and callback hostname.

The existing Traefik instance terminates HTTPS on its external Docker network. Set PROXY_NETWORK to that network's name (default proxy), AUTH_CALLBACK_HOST to the player-login hostname, and PANEL_HOST to the administrator panel hostname. Both hostnames must resolve to the server. Labels use your existing websecure entrypoint and leresolver certificate resolver. Only the Velocity callback and panel join that external network; game backends and MariaDB remain on private networks. Minecraft TCP 25565 is published directly through HAProxy.

Traefik routes only /login/callback on AUTH_CALLBACK_HOST to Velocity port 8085; /ready is not routed. It routes the entire PANEL_HOST to panel port 4326. The base Compose file publishes neither HTTP port and starts no separate reverse proxy. The existing Traefik network must exist before starting the stack.

Persistent state uses bind mounts under ./data: proxy/, create/, lobby/, paper/, auth-db/ and panel/. Everything under this directory is ignored by Git except data/.gitkeep. The panel runs as UID/GID 1000 and needs a writable directory. Before a fresh deployment, prepare it on the Docker host:

mkdir -p data/panel
# Needed when the directory is not already owned by UID/GID 1000:
sudo chown 1000:1000 data/panel

docker compose -f compose.yaml config --quiet
docker compose -f compose.yaml up -d --build
docker compose -f compose.yaml logs -f proxy create lobby mc-panel

Use -f compose.yaml on the server so a workstation's ignored compose.override.yaml is not loaded. The bind mounts use z/Z labels for SELinux hosts. No Docker socket is mounted into this stack. Existing worlds/account links from named-volume deployments are not automatically copied into these directories; this configuration is prepared for a fresh server deployment.

First boot downloads server distributions, loader libraries, mods and plugins. The image build needs Docker Hub, GitHub/codeload, Maven Central and Paper's Maven repository; initial server startup also needs the Minecraft/Paper/NeoForge and Modrinth download endpoints. Authentication requires outbound HTTPS from Velocity to Authentik and Minecraft account services.

Velocity is fixed to the 4.1.0 snapshot stream because the upstream bridge targets that API and Java 25. Its exact build, Paper's build, container tags and automatically resolved transitive mod dependencies are not digest-locked. Main mod/plugin IDs and the auth source commit are pinned. Record image digests and resolved server/dependency artifacts after staging if you need completely repeatable deployments; do not upgrade Minecraft independently of the manifests.

Local testing with Authentik

For a workstation test, set MC_BIND=127.0.0.1, AUTH_REDIRECT_URI=http://localhost:8085/login/callback, and PANEL_URL=http://localhost:4326 in .env. Register those exact redirect URIs (panel callback: http://localhost:4326/auth/callback) in their respective Authentik providers. The ignored local compose.override.yaml can publish callback port 8085 and panel port 4326 on loopback; the base file does not publish them. An existing Docker network named by PROXY_NETWORK is still required. Your browser and Minecraft client must run on the same workstation. On the server, leave AUTH_REDIRECT_URI and PANEL_URL unset to derive their HTTPS values from AUTH_CALLBACK_HOST and PANEL_HOST.

Mods and plugins

manifests/server.txt installs Create, FerriteCore (memory), ModernFix (loading/memory fixes), Lithium (server tick optimization), Spark (profiling), Clumps (XP orb merging), Jade (block information support), NeoForwarding, and Distant Horizons (LOD generation and streaming). Backend permissions currently use Minecraft OP; LuckPerms remains on Velocity for authentication/server access and on optional Paper. The NeoForge 1.21.1 LuckPerms 5.4.140 build is excluded because it throws Capability has not been initialised during player login behind Velocity (upstream issue: https://github.com/LuckPerms/LuckPerms/issues/4183). NeoForwarding is pinned to 1.3.0, the secret-leak fix; upstream labels that release beta. Run the complete stack in staging before trusting a world to it.

manifests/paper.txt installs LuckPerms and WorldEdit on the optional Paper service (docker compose --profile paper up -d paper); Paper includes Spark. It is retained separately and is not part of the modded login route. The NeoForge authentication lobby uses the same server/addon manifests as Create, runs in forced spectator mode with PvP disabled, and uses data/lobby/. A Create client cannot join the Paper lobby because its required mod channels are missing there. The optional Paper world uses data/paper/.

manifests/client.txt lists the matching Create client, performance mods, Jade, JEI (recipes) and AppleSkin (food information). Download node-k-craft-client.mrpack from this repository’s Forgejo Releases. Local builds write dist/node-k-craft-client.mrpack. Import it in Prism Launcher (Add Instance → Import), sign in with a Microsoft account that owns Java Edition, use Java 21 with 4–6 GiB memory, and connect to localhost:25565 for this local test. After changing the manifests or Compose configuration, run python3 scripts/build-client-pack.py --update-lock and commit manifests/client.lock.json with the changes. This records exact dependency versions, download URLs and hashes. Normal builds use python3 scripts/build-client-pack.py offline and reject stale lockfiles. No server configuration or credentials are included. The manifests also serve as readable installation references. Install the named version IDs through Modrinth/Prism or use their Modrinth version pages, allow dependency installation, and add every chosen content addon. Server-only mods such as NeoForwarding, Clumps and LuckPerms do not go in the client pack. Optional client FPS mods: Sodium and Entity Culling for NeoForge 1.21.1. Keep one exported, versioned client pack for everyone once the addon selection is final.

Useful later options: a NeoForge claims mod for land protection, a compatible homes/teleport mod, graves, and Simple Voice Chat. These are not installed yet because they change gameplay or require more client/network configuration. Paper EssentialsX cannot provide homes inside the separate NeoForge world.

Choose Create addons

The selected addons are enabled in manifests/addons.txt, including Steam n Rails. Install every enabled entry and its required dependencies on clients, then recreate the Create container. Comment out any addons you do not want before deploying. These projects publish NeoForge 1.21.1 candidates; that metadata alone does not prove compatibility with Create 6.0.10 or with each other. Check each release's Create requirements and test before generating the production world.

Addon Purpose Candidate version
Steam ’n’ Rails (unofficial NeoForge port) Expanded train tracks, bogies, signals and railway building 0.2.1
Copycats+ Shapeable building blocks using other blocks' textures 3.0.9
Crafts & Additions Electric motors, alternators, wiring and energy storage 1.7.2
Enchantment Industry Automated enchanting and liquid experience 2.4.2
Create Deco Factory decoration, railings and industrial building materials 2.1.3
Create Connected Extra drivetrain and utility components 1.3.3
Stuff & Additions Powered tools and equipment 2.1.4.b
Central Kitchen Automation integrations for food mods 2.6.2b
Create Aeronautics Physics-based vehicles, aircraft and contraptions; requires Sable 1.3.2

Create Aeronautics is pinned to 44pLdPGg in the shared addon manifest, so both NeoForge backends and the client pack install it with its required Sable dependency. Distant Horizons 3.3.3 (9w34y8ai) is pinned in both server and client manifests for server-side LOD generation, streaming and client rendering. Both NeoForge backends install it through the shared server manifest. Playtest Aeronautics vehicles and distant terrain rendering with the complete pack before production use.

A conservative first selection is Copycats+, Create Deco and Create Connected. Pick Crafts & Additions if you want electric energy alongside mechanical power. Central Kitchen is more useful after selecting a supported food mod. Steam ’n’ Rails is enabled using the unofficial NeoForge port, pinned to stable release HOPX5oro (0.2.1). Its JAR declares Minecraft 1.21.1, Create >=6.0.7 and NeoForge >=21.1.233, Crafts & Additions 1.7.2 requires NeoForge >=21.1.248, which is the server and client loader baseline. The original Steam ’n’ Rails project uses a different release line; use the linked port for this pack. Declared dependency checks passed; train behavior and interactions with the other addons still need a staging playtest.

Managed templates are reapplied on each container restart. Edit repository templates rather than their generated copies inside Docker volumes. Server templates copy to /data; the proxy uses forwarding.secret.txt so its shared secret is interpolated reliably.

Minecraft control panel

mc-panel builds the custom Vue/Nuxt UI application in panel/. It uses the vendored node-K design system and a Minecraft RCON transport adapted from the checked-out RCON Web Admin. It provides both worlds' connection status, a command console, player administration, repeating timed commands and an audit trail. Komodo retains container deployment and power controls. No Docker socket, SSH credentials or world files are exposed to the panel.

Configure a separate confidential Authentik OIDC provider/application for administrators, with slug minecraft-admin. Register the strict redirect URI http://localhost:4326/auth/callback for local use, or https://YOUR-PANEL-HOST/auth/callback for production. Use an RSA signing key and the openid, profile, and email scope mappings. Include an ID-token groups claim (Authentik's profile mapping normally includes it), create a minecraft-admins group and restrict the application to that group. The panel independently checks this claim; missing or mismatched groups deny access.

Copy the PANEL_* settings from .env.example to your private .env. Set the client ID/secret, PANEL_HOST to the browser-facing hostname and a random PANEL_SESSION_PASSWORD of at least 32 characters (openssl rand -hex 32). The issuer defaults to https://auth.node-k.at/application/o/minecraft-admin/. These credentials are separate from the player login provider. With the data directory prepared, start with docker compose -f compose.yaml up -d --build mc-panel and open https://${PANEL_HOST}.

Traefik connects to the panel on internal port 4326 over the external proxy network. No separate WebSocket port is needed. Set PANEL_HOST in .env; PANEL_URL defaults to https://${PANEL_HOST} and should only need overriding for local development. RCON stays on Docker's internal Minecraft network.

Schedules and audit.jsonl persist in data/panel/. Run one panel instance against this directory. Timed commands survive restarts; missed intervals run once and resume from the current time. Deleting a schedule does not cancel a command already dispatched. Any allowed panel administrator can execute arbitrary Minecraft commands, including permissions and stop; use Komodo for intentional shutdown because Docker may restart the container. Administrator group changes take effect when the eight-hour panel session expires or the user signs in again. Sign-out clears this panel's session; Authentik SSO remains active.

Legacy widgets, users and schedules are not automatically imported. The new panel's console shows commands issued from that browser and their responses, not the server's complete log stream. Use Komodo for full logs.

Administration, backups and validation

Use the local container console, without exposing RCON:

docker compose exec create rcon-cli list
docker compose exec create rcon-cli op YourMinecraftName
docker compose exec lobby rcon-cli list

Grant permissions deliberately; ordinary players must not receive wildcard/admin permissions. Authentik controls entry, while Minecraft OP/LuckPerms control in-game privileges. The account link is username-based upstream: a Minecraft username change needs an operator-reviewed database relink; the forwarded Microsoft UUID keeps world inventory identity stable. Do not hand-edit an online database.

Bind-mounted directories under data/ retain worlds, player data, plugins, account links and panel schedules across container recreation. docker compose down does not remove these files. Back up the entire data/ directory, .env and configuration. For the simplest consistent backup, stop the stack, archive/snapshot data/, then restart it. Copying a live MariaDB data directory is not a valid database backup. No scheduled backups are silently configured here; attach this directory to your server's backup system and test a restore. Archive or rotate data/panel/audit.jsonl according to your retention policy.

Deployment acceptance checks (the repository does not have access to your Authentik client or a real modded player):

  1. Fresh matching client receives the Authentik link and stays in the lobby before login. /server create and other commands cannot bypass it.
  2. An allowed Authentik account reaches Create; denied users and the wrong linked Authentik identity cannot.
  3. Disconnect/reconnect always requires a new browser flow; completing an old link after reconnect fails.
  4. Stop/restart the proxy while Authentik or MariaDB is unreachable: the public Minecraft entry must stay closed. A missing auth JAR must also leave HAProxy with no healthy backend.
  5. Confirm no backend/RCON/database ports are reachable from outside. Test Create crafting, contraptions, train travel and server switching with the final addon pack.
  6. Back up, restore into an isolated copy, and verify world/player/account-link data.

Upstream references

Client pack releases

.forgejo/workflows/client-release.yaml builds on an ubuntu-22.04 runner when a v* tag is pushed. Enable repository Actions and provide a runner with that label, Python 3 and Node.js for checkout. The workflow uses its built-in token to create a draft release, upload the .mrpack and .mrpack.sha256, then publish the release. Interrupted draft uploads can be retried; published releases are never overwritten.

After changing pack inputs, refresh the lockfile:

python3 scripts/build-client-pack.py --update-lock

Review and commit the manifests, lockfile and other changes before creating and pushing a release tag:

git tag v1.0.0
git push origin v1.0.0

For a local preview, run python3 scripts/build-client-pack.py --version v1.0.0. The tag becomes the pack’s versionId. Builds from the same lockfile and version produce identical pack bytes. Generated files under dist/ are ignored by Git. Dependency updates happen only through --update-lock; review its diff before committing. Server containers still resolve required dependencies at startup independently of the client lockfile.

Distant Horizons server pregeneration

After updating the server manifest on the deployment host, recreate the Create service to install Distant Horizons:

docker compose -f compose.yaml up -d --force-recreate create

Once startup completes, generate Overworld LODs around coordinates 0,0 with a radius of 256 chunks (4,096 blocks):

docker compose -f compose.yaml exec create rcon-cli dh pregen start minecraft:overworld 0 0 256
docker compose -f compose.yaml logs -f create

The task runs on the server without a connected player or open SSH session. Run it while the server is quiet and monitor CPU, memory and disk usage. LOD data persists under data/create/. Keep Distant Generation enabled on server and clients; check the client's DH F3 status for server support after joining. For available commands and settings, use help dh through RCON or in-game command completion. See the official DH server guide.

Komodo restart countdown

scripts/komodo-pre-deploy.sh can run as a Komodo Stack pre-deploy hook:

bash scripts/komodo-pre-deploy.sh YOUR_COMPOSE_PROJECT_NAME

Run it from the repository root on the Periphery host, with Bash, Docker CLI, flock and timeout available. Use the existing Docker Compose project name. It announces a ten-minute countdown to running Create, lobby and optional Paper backends, flushes world saves, and then returns so Komodo can deploy. Docker or RCON errors abort the hook. Fresh installations with no running backends skip the countdown. Allow at least 15 minutes for the operation. Configure Compose up extra arguments to include --force-recreate so manifest changes are installed; enable builds for the local images and leave Destroy Before Deploy disabled. Preserve the deployment directory and persistent data/ when updating.

Release-event webhooks were verified against Komodo 2.2.0. Configure Forgejo to send published release events to a Komodo Action webhook with a matching secret. The Action should run:

await komodo.execute("DeployStack", { stack: "node-k-craft" });

Keep the Stack on main with the pre-deploy hook configured above. Publishing the client release triggers the countdown and deployment of the latest main; it does not pin deployment to the release tag. No extra deployment branch is used. Ensure draft creation or release edits do not trigger additional deployments when configuring the webhook.