Minecraft Worlds
Quickstart: Bring a World Up
-
Allocate IP, SNI Route, and DNS:
sudo ipctl create <name> --dns(Or
/data/minecraft/ipctl create <name> --dns. Allocates backend IPv6fd77:7777:5010::1337:3c:d00d:<hex>/64, configures HAProxy SNI routing, adds<name>.drheck.devin Unbound ondanknasty, and syncs public A/AAAA records to deSEC). -
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 port25565). -
Start the server: Launch your Minecraft server on UL. It binds to its dedicated IPv6 address.
-
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
-
Stop the backend server: Run
stopin the Minecraft server console or terminate its process. -
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).
- In the Multiplayer Server List: The ping MOTD displays:
-
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
- Temporary takedown (disable in router only):
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:
- HAProxy listens dual-stack on
10.0.0.113:25565and[fd77:7777:5010::1337:f1ac:d00d:3c]:25565. - When a player connects, HAProxy peeks at the initial unencrypted Minecraft handshake packet (
0x00) via/etc/haproxy/minecraft.luaand extracts the requested hostname (e.g.pokelovers.drheck.dev) and connection state (Status vs Login). - 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/Mojangonline-mode=trueauthentication directly over the spliced TCP connection. - If the backend server is DOWN: HAProxy immediately handles the stream with Lua applet
mc_offlinewhich 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
0x00rendering a red bold error screen:<name> is currently offline. Ping Luke
- Status Ping: JSON MOTD with red text:
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
25565on 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.servicerunning 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.
- Automatically updates the rendered 3D map as chunks change in
- Reverse Proxy & Authentication: NGINX terminates SSL on port 443 at
/etc/nginx/sites-available/pokeloversand 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.devto log in before viewing the 3D map.
- Serves directly at
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>andAddress=fd77:7777:5010::1337:3c:d00d:<hex>/64in/etc/systemd/network/20-wired.networkand triggersnetworkctl reload. - Router Configuration (
ul): Updates/etc/haproxy/minecraft_hosts.mapand backend stanzas (with health checks and offline responder) in/etc/haproxy/haproxy.cfg, then reloadshaproxy.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--dnsis passed:- Updates the git working tree at
/home/luke/git/router_config/unbound.conf. - Verifies syntax using
doas unbound-checkconf. - Deploys the configuration to
/var/unbound/etc/unbound.conf. - Restarts Unbound via
doas rcctl restart unbound. - Verifies local resolution via
dig @10.0.0.1. - Executes
python3 /home/luke/git/router_config/dns_danger_sync.py --forceto synchronize public A and AAAA records on deSEC.io.
- Updates the git working tree at
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).
- Creates timestamped, ACL/xattr-preserved,
- Restore Script:
/home/luke/git/backups/ul/scripts/restore/minecraft_router [timestamp]- Verifies PAR2 checksums/parity.
- Extracts configurations to
/and triggers live reloads ofsystemd-networkdandhaproxy.
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{\text{Directory}, \quad \text{Manifest } (\texttt{server.json}), \quad \text{Network } (\texttt{ipctl}), \quad \text{Units } (\texttt{systemd}), \quad \text{Lifecycle Policy}\right}$$
/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
}
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:
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.
ondemand (Wake-on-Join + Idle Auto-Stop):
save-all $\longrightarrow$ stop) once $0$ players have been online continuously for $T_{\text{idle}}$ minutes (default $20\text{ min}$).
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:
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
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
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:
/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:
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.
Step 2 — Server Binary & Custom Modpacks:
/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:
/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/.
Step 3 — Interactive Seed Preview & Reroll Studio:
String.hashCode() rules) or click 🎲 Roll Random Seed to cycle through candidate seeds.
Instant 2D Biome & Structure Map Preview (cubiomes Engine):
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"):
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 End-to-End Automated Provisioning Pipeline
When Create Server is clicked in the Web UI, the backend executes the following atomic pipeline:
/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/.
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.
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.
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.