Skip to main content

Minecraft Worlds

Quickstart: Bring a World Up

  1. Allocate IP, SNI Route, and DNS:

    sudo ipctl create <name> --dns
    

    (Or /data/minecraft/ipctl create <name> --dns. Allocates backend IPv6 fd77:7777:5010::1337:3c:d00d:<hex>/64, configures HAProxy SNI routing, adds <name>.drheck.dev in Unbound on danknasty, and syncs public A/AAAA records to deSEC).

  2. Configure the backend server: In your backend server's server.properties:

    server-ip=fd77:7777:5010::1337:3c:d00d:<hex>
    server-port=25565
    online-mode=true
    

    (Backend servers run stock vanilla Minecraft, online-mode=true, listening on standard port 25565).

  3. Start the server: Launch your Minecraft server on UL. It binds to its dedicated IPv6 address.

  4. Connect: Players connect using <name>.drheck.dev (standard port 25565). Microsoft account authentication takes place directly between the client and your backend server.


Quickstart: Take a World Down

  1. Stop the backend server: Run stop in the Minecraft server console or terminate its process.

  2. What Players See When Offline: HAProxy monitors backend health checks. If the server process is stopped:

    • In the Multiplayer Server List: The ping MOTD displays: [Server Name] is currently offline. Ping Luke
    • When Clicking "Join Server": The client immediately disconnects with a clean red Minecraft disconnect screen: "[Server Name] is currently offline. Ping Luke" (No connection timeouts, hanging, or generic connection refused errors).
  3. Disable or remove router mapping & DNS (Teardown):

    • Temporary takedown (disable in router only):
      sudo ipctl route disable <name>
      
    • Complete teardown (release IP, remove HAProxy route, purge DNS):
      sudo ipctl delete <name> --dns
      

Architecture & Design Details

1. Unified Endpoint Model

All public and LAN client connections resolve to the single Minecraft Router endpoint pair:

  • IPv4: 10.0.0.113
  • IPv6: fd77:7777:5010::1337:f1ac:d00d:3c

Whether connecting via pokelovers.drheck.dev or any other server subdomain, players connect to this exact IP pair on standard port 25565.

2. Transparent TCP SNI Handshake Routing & Offline Fallback (HAProxy)

Rather than using an application-level proxy (which breaks vanilla Diffie-Hellman encryption and forces online-mode=false), HAProxy performs transparent pre-handshake packet inspection:

  1. HAProxy listens dual-stack on 10.0.0.113:25565 and [fd77:7777:5010::1337:f1ac:d00d:3c]:25565.
  2. When a player connects, HAProxy peeks at the initial unencrypted Minecraft handshake packet (0x00) via /etc/haproxy/minecraft.lua and extracts the requested hostname (e.g. pokelovers.drheck.dev) and connection state (Status vs Login).
  3. If the backend server is UP: It splices the raw TCP stream directly to that server's dedicated internal IPv6 address on port 25565. The client and backend perform native Microsoft/Mojang online-mode=true authentication directly over the spliced TCP connection.
  4. If the backend server is DOWN: HAProxy immediately handles the stream with Lua applet mc_offline which crafts and sends a valid Minecraft protocol packet:
    • Status Ping: JSON MOTD with red text: <name> is currently offline. Ping Luke
    • Login Join: Disconnect packet 0x00 rendering a red bold error screen: <name> is currently offline. Ping Luke

3. Backend IPv6 Address Scheme

Each backend Minecraft server is isolated on a dedicated Unique Local Address (ULA) on interface eth77 of ul:

  • Prefix: fd77:7777:5010::1337:3c:d00d:<hex>/64
  • Port: Each server binds to standard port 25565 on its assigned IPv6 address.
  • Security: Backend IPv6 addresses are completely internal and hidden behind the proxy router.

4. Web Map (BlueMap) & Authentik Gated HTTPS

Interactive 3D web mapping for Minecraft 26.3 is powered by BlueMap running in standalone watch mode:

  • Service: bluemap-pokelovers.service running from /data/minecraft/poke lovers/bluemap.
    • Automatically updates the rendered 3D map as chunks change in /data/minecraft/poke lovers/world.
    • Serves live map data & Server-Sent Events (SSE) updates on internal port 8100.
  • Reverse Proxy & Authentication: NGINX terminates SSL on port 443 at /etc/nginx/sites-available/pokelovers and gates access behind Authentik ForwardAuth (/outpost.goauthentik.io/auth/nginx).
    • Serves directly at https://pokelovers.drheck.dev.
    • Unauthenticated visitors are automatically redirected to auth.drheck.dev to log in before viewing the 3D map.

5. Network & DNS Automation (ipctl)

The tool /data/minecraft/ipctl (symlinked in /home/luke/bin/ipctl) automates the entire lifecycle:

  • Local Network (ul): Appends or removes # <name> and Address=fd77:7777:5010::1337:3c:d00d:<hex>/64 in /etc/systemd/network/20-wired.network and triggers networkctl reload.
  • Router Configuration (ul): Updates /etc/haproxy/minecraft_hosts.map and backend stanzas (with health checks and offline responder) in /etc/haproxy/haproxy.cfg, then reloads haproxy.service.
  • SSH Key Verification: Verifies that key SHA256:7Xr0vCxgfpPhbKE3hN9fE2IC9y4x3Kj+kYmXwzgJY3s (ultra-arikui) is loaded in the agent before running remote router commands.
  • Router DNS (danknasty): When --dns is passed:
    1. Updates the git working tree at /home/luke/git/router_config/unbound.conf.
    2. Verifies syntax using doas unbound-checkconf.
    3. Deploys the configuration to /var/unbound/etc/unbound.conf.
    4. Restarts Unbound via doas rcctl restart unbound.
    5. Verifies local resolution via dig @10.0.0.1.
    6. Executes python3 /home/luke/git/router_config/dns_danger_sync.py --force to synchronize public A and AAAA records on deSEC.io.

Backups & Disaster Recovery

The router configurations, Lua scripts, mapping tables, network configurations, and the ipctl tool are integrated into the automated backup infrastructure:

  • Backup Script: /home/luke/git/backups/ul/scripts/backup/minecraft_router
    • Creates timestamped, ACL/xattr-preserved, par2-verified archives in /data/backups/services/current/minecraft_router/.
    • Automatically invoked during the global daily backup run (run_all_backups).
  • Restore Script: /home/luke/git/backups/ul/scripts/restore/minecraft_router [timestamp]
    • Verifies PAR2 checksums/parity.
    • Extracts configurations to / and triggers live reloads of systemd-networkd and haproxy.

6. Minecraft Server Manager & On-Demand Architecture (Design Specification)

Design Goal: Provide a unified web control plane (https://mc.drheck.dev, gated by Authentik) and daemon that formalizes what a Minecraft server is on ul, automates ipctl networking and systemd lifecycle management, wakes sleeping servers on-demand via HAProxy when players join, and provides an interactive seed-preview and modpack studio when creating new worlds.

6.1 Canonical Definition of a "Server"

Every managed Minecraft world on ul is a self-contained unit consisting of five standardized components:

$$\text{Server}(\text{slug}) = \left\lbrace\text{Directory}, \quad \text{Manifest } (\texttt{server.json}), \quad \text{Network } (\texttt{ipctl}), \quad \text{Units } (\texttt{systemd}), \quad \text{Lifecycle Policy}\right\rbrace$$

Component Path / Identifier Description
1. Dedicated Directory /data/minecraft/<dir_name>/ Holds server.json, server .jar / libraries, mods/ or plugins/, server.properties, whitelist.json, ops.json, world/, and optional bluemap/. Existing worlds in /data/minecraft/* are adopted in-place without moving files.
2. Declarative Manifest /data/minecraft/<dir_name>/server.json Stores the human display name, DNS slug (<slug>), loader type (paper, vanilla, fabric, neoforge, forge, custom), Minecraft version, JVM path (/usr/lib/jvm/java-26-openjdk/bin/java), heap size (-Xms/-Xmx), custom modpack reference, seed, and lifecycle mode (manual, ondemand, always).
3. Network & DNS (ipctl) fd77:7777:5010::1337:3c:d00d:<hex>/64 Dedicated internal IPv6 address on eth77, bound in server.properties (server-ip= and server-port=25565, plus internal RCON on port 25575), mapped in /etc/haproxy/minecraft_hosts.map, and published as <slug>.drheck.dev on danknasty Unbound and deSEC.io.
4. Dedicated systemd Service minecraft@<slug>.service (+ optional bluemap@<slug>.service) Runs the server under systemd via /usr/local/bin/mc-launch <slug>. Selects the per-server JVM binary directly (never mutating the global archlinux-java symlink) and performs graceful RCON save-all + stop on shutdown.
5. Lifecycle Policy manual | ondemand | always Controls when the server consumes RAM on ul (see Section 6.2).
Click to expand example /data/minecraft/poke lovers/server.json manifest
{
  "name": "Poke Lovers",
  "slug": "pokelovers",
  "directory": "/data/minecraft/poke lovers",
  "domain": "pokelovers.drheck.dev",
  "bind_ipv6": "fd77:7777:5010::1337:3c:d00d:1",
  "game_port": 25565,
  "rcon_port": 25575,
  "loader": "vanilla",
  "mc_version": "26.3",
  "jar_file": "server_26.3.jar",
  "java_bin": "/usr/lib/jvm/java-26-openjdk/bin/java",
  "memory_min_gb": 20,
  "memory_max_gb": 20,
  "use_aikars_flags": true,
  "modpack": null,
  "seed": "-41829401829401",
  "lifecycle_mode": "ondemand",
  "idle_shutdown_minutes": 20,
  "bluemap_enabled": true,
  "bluemap_port": 8100,
  "owner": "luke",
  "admins": ["luke", "ikura"],
  "sync_ingame_ops": true
}

6.2 Resource Management & Server Lifecycle Modes

Because each active Minecraft JVM allocates $8\text{--}20\text{ GiB}$ of pinned heap memory (-Xms = -Xmx with +AlwaysPreTouch), running all $25+$ worlds in /data/minecraft simultaneously is impossible. Each server is assigned one of three lifecycle modes:

  1. manual (Cold / Archived):
    • minecraft@<slug>.service is disabled and stopped ($0\text{ GiB}$ RAM).
    • Only starts when explicitly clicked in the Web UI.
    • When stopped, HAProxy returns: <name> is currently offline. Ping Luke.
  2. ondemand (Wake-on-Join + Idle Auto-Stop):
    • Stopped by default ($0\text{ GiB}$ RAM).
    • Automatically started when a player clicks Join Server in Minecraft (intercepted by HAProxy Lua).
    • Automatically stopped (save-all $\longrightarrow$ stop) once $0$ players have been online continuously for $T_{\text{idle}}$ minutes (default $20\text{ min}$).
  3. always (Pinned Production):
    • minecraft@<slug>.service is enabled and active across host reboots (systemctl enable --now).
    • Used for primary worlds that should remain online $24/7$ regardless of player count.

6.3 HAProxy On-Demand Wake-on-Join Architecture

The Minecraft protocol sends a Handshake packet (0x00) at the start of every TCP connection containing a Next State VarInt:

  • $\text{Next State} = 1$ (Status Ping): Sent when a client refreshes their Multiplayer Server List (or when internet bots scan port 25565).
  • $\text{Next State} = 2$ (Login Join): Sent only when a player clicks Join Server.

/etc/haproxy/minecraft.lua already extracts both txn.mc_host and txn.mc_state. Extending mc_offline enables zero-overhead on-demand activation without waking servers on server-list pings:

$$\text{Client Packet } (\texttt{0x00}) \longrightarrow \begin{cases} \text{state} = 1 \text{ (Status Ping)} & \longrightarrow \text{Return JSON MOTD } (\text{Sleeping / Starting / Offline}) \text{ without waking} \\ \text{state} = 2 \text{ (Login Join)} & \longrightarrow \text{If } \texttt{ondemand}\text{: trigger } \texttt{mc-manager} \text{ wake socket + return countdown packet} \end{cases}$$

State Machine for ondemand Worlds

Server State Multiplayer List Ping (state = 1) MOTD Player Clicks "Join Server" (state = 2)
SLEEPING §e⚡ <name> is sleeping (On-Demand) §7— §aClick Join to wake! HAProxy Lua sends WAKE <slug> to /run/mc-manager/wake.sock, transitions server to STARTING, and disconnects client with: "⚡ Waking up <name>... Server will be ready in ~20 seconds!"
STARTING §6⏳ <name> is booting up... §7Please wait ~15s Disconnects client with live elapsed timer: "⏳ <name> is currently booting (12s elapsed)... Rejoin in a few seconds!"
ONLINE Native backend MOTD + live player count (spliced directly to backend JVM) Native TCP splice to [fd77:7777:5010::1337:3c:d00d:<hex>]:25565 with full Microsoft online-mode=true auth
OFFLINE (manual) §c<name> is currently offline. Ping Luke Disconnects client with: "<name> is currently offline. Ping Luke"

Idle Shutdown Timer

For any running server in ondemand mode, mc-manager tracks the time since the last player disconnected ($N_{\text{players}} = 0$) after an initial post-boot grace period ($T_{\text{grace}} = 5\text{ min}$ so players have plenty of time to connect after waking the server). When:

$$t_{\text{empty}} \ge T_{\text{idle}} \quad (\text{default } 20\text{ min})$$

mc-manager issues save-all and stop via RCON and stops minecraft@<slug>.service, freeing the JVM heap until the next join request.


6.4 Web Control Plane (https://mc.drheck.dev)

The web frontend is served over HTTPS behind Nginx + Authentik ForwardAuth and provides two primary workspaces:

1. Existing Servers Dashboard

  • Server Fleet Cards:
    • Displays every registered server with its live state badge (ONLINE, STARTING, SLEEPING (ON-DEMAND), OFFLINE), online player list, RAM/CPU usage, Minecraft version, loader type (Paper, Vanilla, Fabric, Forge), and <slug>.drheck.dev endpoint.
    • One-Click Controls: Start, Stop, Restart, and a dropdown to switch the server between Manual, On-Demand, and Always On.
    • Live Console & Config Drawer: WebSocket log stream (journalctl -u minecraft@<slug>.service -f) with RCON command input, visual server.properties / whitelist.json / ops.json editor, and BlueMap 3D map toggle.
  • Legacy Directory Adoption:
    • Scans /data/minecraft/* for directories without a server.json manifest (e.g., kochan 26.2, hermitcraft 9, Project Sekai, superworld 1.20.1) and provides a one-click Adopt & Productionize wizard that generates server.json, provisions an ipctl IPv6 route + DNS entry, and wires up minecraft@<slug>.service.

2. Create New Server Studio

Creating a new world follows a 3-step guided workflow in the Web UI:

  1. Step 1 — Identity, Networking & Resource Profile:
    • Display Name & Subdomain: Entering Create World auto-suggests slug createworld (createworld.drheck.dev).
    • Lifecycle Policy: Select On-Demand (default), Always On, or Manual, plus heap size (4 GiB – 24 GiB) and optional BlueMap 3D Web Map provisioning.
  2. Step 2 — Server Binary & Custom Modpacks:
    • Server Engine Selector:
      • Paper / Purpur / Vanilla / Fabric / NeoForge / Forge: Queries live upstream release APIs (or local cached jars in /data/minecraft/jars/) so you can pick the exact Minecraft version and build from a dropdown.
      • Automatically pairs the required JVM runtime (/usr/lib/jvm/java-26-openjdk, java-21-openjdk, java-17-openjdk, or java-8-openjdk).
    • Custom Modpack & Mod Library:
      • Local Custom Modpacks (/data/minecraft/modpacks/): Choose from your curated custom modpacks (.zip server packs, .mrpack Modrinth packs, or reusable mods/ + config/ bundles) or upload a new custom pack directly in the browser.
      • Individual Mods & Plugins: Upload .jar files or search Modrinth / Paper Hangar filtered by the selected Minecraft version and loader to drop mods/plugins straight into mods/ or plugins/.
  3. Step 3 — Interactive Seed Preview & Reroll Studio:
    • Seed Input or Reroll:
      • Enter a specific seed (integer or text string, converted using standard Java String.hashCode() rules) or click 🎲 Roll Random Seed to cycle through candidate seeds.
    • Instant 2D Biome & Structure Map Preview (cubiomes Engine):
      • Uses a headless native cubiomes C renderer on ul to generate a high-resolution 2D biome colour map ($\pm 2048$ blocks around $(0,0)$) in $< 50\text{ ms}$ per seed, overlaid with exact structure icons (Spawn point, Villages, Ancient Cities, Woodland Mansions, Ocean Monuments, Pillager Outposts, Strongholds, and Nether Fortresses/Bastions).
      • Lets you click 🎲 Roll Random Seed repeatedly until you spot an ideal spawn island, continent, or structure cluster, or bookmark candidate seeds side-by-side before creating the server.
    • Post-Creation Seed Swap ("Reroll World"):
      • On any newly created server (for example, if you log in or check its 3D BlueMap render and want a different terrain layout), clicking Swap Seed & Regenerate World in the server dashboard stops minecraft@<slug>.service, writes the new level-seed to server.properties and server.json, clears world/, world_nether/, and world_the_end/, and restarts the server on the new seed.

6.5 Authentik Gate, OIDC Role-Based Access Control (RBAC) & In-Game /op Sync

Access to https://mc.drheck.dev is protected by a two-layer Authentik security model that gates the entire web interface at the Nginx perimeter while using native OpenID Connect (OAuth2Provider) claims to grant granular Server Admin privileges both in the web UI and inside the Minecraft servers themselves.

$$ ext{\text{Authentik SSO } ( exttt{\texttt{auth.drheck.dev}) \longrightarrow egin{\begin{cases} ext{\text{Layer 1: Nginx ForwardAuth } ( exttt{\texttt{ProxyProvider}) & \longrightarrow ext{\text{Blocks unauthenticated requests at perimeter} \\ ext{\text{Layer 2: OIDC JWT + Scopes } ( exttt{\texttt{OAuth2Provider}) & \longrightarrow ext{\text{Maps } exttt{\texttt{groups} ext{\text{ &and } exttt{\texttt{entitlements} ext{\text{ to RBAC + RCON } exttt{\texttt{/op} \end{cases}$$

1. Authentik OIDC Scopes & Claim Evaluation

mc-manager authenticates users via OIDC Authorization Code Flow with PKCE against https://auth.drheck.dev/application/o/mc/ (verifying RS256 JWT tokens against https://auth.drheck.dev/application/o/mc/jwks/) and inspects three identity sources on every HTTP request and WebSocket frame:

  • profile Scope (groups claim): Reads Authentik group memberships (["mc_admins", "mc_server_admins", "ul_ssh", ...]).
  • entitlements Scope (roles claim): Reads application-scoped entitlements attached to the mc Authentik Application (e.g., admin:pokelovers, admin:all, creator).
  • Per-Server server.json ACLs (owner & admins): Allows a Super Admin (or the creator of a server) to grant specific Authentik usernames ("admins": ["ikura", "haruka"]) full admin privileges on an individual server directly from the web UI.

2. Three-Tier Privilege Matrix

Role Tier Authentik Group / Entitlement Match Web UI & Infrastructure Capabilities In-Game Minecraft Privileges
1. Super Admin Group authentik Admins or mc_admins (e.g., luke) Full Control Plane Access: Create/delete any server, run ipctl --dns, adopt legacy /data/minecraft/* folders, configure any heap size or always mode, access all live RCON consoles, and assign per-server admins. Automatic Level 4 Operator (/op) + bypassesPlayerLimit: true on all servers with sync_ingame_ops: true.
2. Server Admin Group mc_server_admins, Entitlement admin:<slug>, or username listed in server.json admins Delegated Server Administration: Can start/stop/restart assigned servers, switch between manual and ondemand, use the Live RCON Console & Log Stream, edit server.properties / whitelist.json, upload/toggle mods, and create new ondemand servers in the Create New Server Studio (capped at 12 GiB heap). Automatic Level 4 Operator (/op) on every server where they hold Server Admin rights.
3. Player / Viewer Any authenticated drheck.dev user Dashboard & Wake Access: View live server status, online players, seeds, and BlueMap 3D maps; click Wake Server on ondemand worlds; download client .mrpack / .zip modpack bundles. Cannot stop servers, view RCON consoles, or alter configs. Standard player (automatically added to whitelist.json if account is linked).

3. Automatic OIDC-to-Minecraft /op (ops.json) Synchronization

Each authenticated user can link their Minecraft Java Edition username once in the mc.drheck.dev profile bar (stored in Authentik user attributes user.attributes.mc_username and resolved to an immutable Mojang UUID via https://api.mojang.com/users/profiles/minecraft/<username>):

  • Granting Admin: When a user is added to mc_admins, mc_server_admins, or a server's admins list in server.json, mc-manager automatically writes their UUID and username into /data/minecraft/<dir>/ops.json ("level": 4, "bypassesPlayerLimit": true) and whitelist.json. If the server is currently ONLINE, mc-manager immediately sends whitelist add <mc_user> and op <mc_user> over the internal ULA IPv6 RCON socket (25575) so they receive operator privileges in-game without a server restart.
  • Revoking Admin: Removing a user from the Authentik group or the server's admins list immediately removes their entry from ops.json and dispatches deop <mc_user> over RCON.

6.6 End-to-End Automated Provisioning Pipeline

When Create Server is clicked in the Web UI, the backend executes the following atomic pipeline:

  1. Directory & Manifest Creation: Creates /data/minecraft/<slug>/, writes server.json and eula.txt (eula=true), downloads the selected Paper/Vanilla/Fabric/Forge binary, and extracts any selected custom modpack into mods/, plugins/, and config/.
  2. Network & DNS Allocation (ipctl): Calls ipctl create <slug> --dns to assign the next available fd77:7777:5010::1337:3c:d00d:<hex>/64 address on eth77, register the HAProxy SNI backend (with mc_offline on-demand wake support), and publish <slug>.drheck.dev on danknasty Unbound and deSEC.io.
  3. server.properties Generation: Writes server-ip=fd77:7777:5010::1337:3c:d00d:<hex>, server-port=25565, level-seed=<seed>, online-mode=true, and RCON credentials bound exclusively to the internal ULA IPv6 address.
  4. Systemd Activation: Registers minecraft@<slug>.service (and bluemap@<slug>.service if enabled) and either starts the server immediately or places it into SLEEPING (ON-DEMAND) ready for the first player join.