# Minecraft LAN Party Server — Project Context > Self-hosted modded Minecraft server for a LAN party (~8 players), with > persistent auth/skin infrastructure designed to outlive the LAN weekend > and integrate with the existing homelab. ## Goals & Constraints - **Players**: 4–10 friends, LAN-based - **Modpack vibe**: Kitchen-sink (tech + magic + exploration + QoL) - **Pack approach**: Manually curated (~50–100 mods), NOT a forked existing pack - **Minecraft version**: 1.21.1 - **Mod loader**: NeoForge (NOT classic Forge — see Decisions below) - **Network**: LAN party context, but stack is persistent (lives past the weekend) - **Auth**: Self-hosted Yggdrasil-compatible (Drasl) + Keycloak OIDC - **Skins**: Handled by Drasl - **Persistence horizon**: Long-term homelab service, not disposable ## Existing Homelab Context (relevant to this project) - **Keycloak 26.0.7** running in Docker Compose (PostgreSQL 17, production mode, bind-mounted secrets, xforwarded proxy headers). This is the IdP for everything. - **NetBird** as the proxy/networking layer for remote access (NOT used for the LAN party itself — see Decisions). - **AdGuard Home** as recursive DNS resolver — used here for `*.home.local` LAN records. - **Multi-VPS topology**: this Minecraft project likely runs on a local box or beefier homelab node, not a VPS (RAM/CPU requirements). - Operator timezone: Europe/Madrid. ## Key Decisions (with reasoning) ### NeoForge over classic Forge Despite the user originally saying "Forge", the 1.21.x kitchen-sink ecosystem (ATM10, Leaking Kitchen Sink, etc.) is overwhelmingly NeoForge in 2025/2026. The original Forge team essentially migrated to NeoForge. `itzg/minecraft-server` supports both via `TYPE=NEOFORGE`. Going with NeoForge 1.21.1. ### Drasl over Ely.by or vanilla offline mode - **Ely.by**: not self-hosted, Russian-hosted, no control over identity layer. - **Vanilla offline + no auth server**: works for one weekend but skins break, no persistent identity, doesn't match homelab philosophy. - **Drasl**: self-hosted Go service, Yggdrasil-compatible, drop-in Mojang replacement, supports skins + capes, **has first-class OIDC support with PKCE** (perfect for Keycloak), actively maintained (current version 3.4.2+). GPLv3 licensed. ### LAN (not NetBird) for the party itself NetBird is the homelab's remote access layer, but adding a mesh VPN on top of a LAN party introduces complexity for guests with no upside. Drasl and Minecraft live on the LAN; clients reach them via `drasl.home.local` resolved through AdGuard. ### Manual curation (user choice — flagged as expensive) User insisted on manual curation despite my pushback that forking ATM10 or Leaking Kitchen Sink would save weeks of crash-log triage. **Note for future sessions**: if curation gets painful, the fork-and-trim option is still on the table — Leaking Kitchen Sink is the closest match to the requested vibe. ### itzg/minecraft-server as base image The de facto standard. Handles NeoForge installation automatically via `TYPE=NEOFORGE` + `VERSION` + `NEOFORGE_VERSION`. Supports Modrinth and CurseForge mod auto-download via `MODRINTH_PROJECTS` and `CURSEFORGE_FILES`. ## Architecture ``` LAN segment │ ├── AdGuard Home → resolves drasl.home.local, keycloak.home.local │ to the Docker host's LAN IP │ ├── Keycloak (existing, separate compose) │ realm: homelab │ client: drasl (confidential, PKCE S256) │ └── Minecraft host (this project's compose) │ ├── drasl :25585 (Yggdrasil API + web UI) │ └── OIDC → Keycloak │ ├── minecraft :25565 (server) │ :24454/udp (Simple Voice Chat, optional) │ └── -javaagent:authlib-injector.jar=http://drasl.home.local:25585/authlib-injector │ └── ONLINE_MODE=false (required for authlib-injector) │ └── mc-backup (itzg/mc-backup, 6h interval) └── RCON → minecraft Clients (LAN party guests): Prism Launcher → authlib-injector account pointing at Drasl → joins minecraft:25565 ``` ## Auth Flow (end-to-end) 1. Guest opens `http://drasl.home.local:25585` in browser. 2. Clicks "Register with Keycloak" → redirected to Keycloak login. 3. Logs in with homelab Keycloak credentials. 4. Returned to Drasl, picks a player name, uploads a skin. 5. Drasl shows them a "Minecraft Token" on their profile page. 6. In Prism Launcher: Settings → Accounts → Add authlib-injector account with URL `http://drasl.home.local:25585/authlib-injector` and the Minecraft Token as password. 7. Joins Minecraft server. Server validates session against Drasl (via authlib-injector JVM agent), Drasl confirms, player enters with their skin. ## Critical Gotchas ### Minecraft 1.21+ secure profile incompatibility - Server: must set `enforce-secure-profile=false` (compose: `ENFORCE_SECURE_PROFILE=FALSE`) - Drasl: must set `SignPublicKeys = false` - These are linked. The Drasl docs explicitly warn: *"Mixed authentication does not work with `SignPublicKeys = true` on Minecraft 1.21+."* ### authlib-injector JVM agent - Syntax: `-javaagent:/path/to/authlib-injector.jar=` - The URL after `=` is the **API root**, which for Drasl is `/authlib-injector`. - **Must match between server and client.** If server uses `http://drasl.home.local:25585/authlib-injector` and client uses anything else (e.g. an IP), session validation fails silently with "Invalid session". - Download from: https://github.com/yushijinhun/authlib-injector/releases - Mount into the itzg container at `/extras/authlib-injector.jar`. ### Keycloak reachability on LAN day If Keycloak is only reachable via NetBird or only from the public internet, **Drasl OIDC registration breaks on LAN-only day**. Options: 1. Expose Keycloak on the LAN (simplest) 2. Pre-register all guests before the party 3. Temporarily disable OIDC and allow password registration during the LAN (`AllowPasswordLogin = true`, comment out the OIDC block) ### HTTP vs HTTPS for OIDC Modern browsers warn on plain HTTP for OIDC flows but they work. For a production-grade setup, front Drasl with Caddy or Traefik using a local CA cert (smallstep/step-ca pairs well with the existing homelab). For LAN-only HTTP is fine. ### NeoForge version pinning Mods are picky about exact NeoForge minor versions. The compose currently pins `NEOFORGE_VERSION: "21.1.209"` as a placeholder. **Verify against https://projects.neoforged.net/neoforged/neoforge before deploying.** Don't use `"latest"` for a stable server. ### Drasl username vs player name - **Drasl username** = OIDC user's email (used for web UI login) - **Player name** = `preferred_username` from OIDC, or user-chosen if `AllowChoosingPlayerName = true` - Keycloak users must have email set. Verify the realm's email-as-username setting and the `preferred_username` mapper are configured. ### Mod source preference Prefer **Modrinth over CurseForge** for itzg auto-download. CurseForge requires an API key (`CF_API_KEY`) and has been historically flakier with the itzg image. Use CF only for mods that are exclusive to it. ## Compose Stack The working compose file is at `./docker-compose.yml`. Key environment vars documented inline. Adjacent files: ``` . ├── docker-compose.yml ├── .env # RCON_PASSWORD, CF_API_KEY (if needed) ├── drasl/ │ └── config/ │ ├── config.toml # see ./drasl-config.toml example │ └── keycloak-client-secret # single line, no trailing newline ├── extras/ │ ├── authlib-injector.jar # downloaded from yushijinhun's releases │ ├── modrinth-mods.txt # one mod slug per line │ └── cf-mods.txt # only if using CurseForge mods ├── backups/ # mc-backup writes here └── CLAUDE.md # this file ``` ### Memory sizing With ~50–100 mods and 8 concurrent players on 1.21.1: - `INIT_MEMORY: 4G`, `MAX_MEMORY: 10G` is a safe starting point - Use `USE_AIKAR_FLAGS: TRUE` for the well-known GC tuning - Monitor with `mc-monitor` (itzg makes one) if you want metrics ### Backups `itzg/mc-backup` runs alongside, talks to the server via RCON, takes snapshots every 6h, prunes after 14 days. Backups are world-only (no mod jars), which is correct — mods are reproducible from the mod list files. ## Keycloak Client Configuration In the `homelab` realm (or whatever the realm is called): 1. **Clients → Create client** - Client type: `OpenID Connect` - Client ID: `drasl` - Name: `Drasl Minecraft Auth` 2. **Capability config** - Client authentication: **ON** (confidential client) - Authentication flow: **Standard flow** only - Disable: Direct access grants, Implicit, Service accounts 3. **Login settings** - Root URL: `http://drasl.home.local:25585` - Valid redirect URIs: `http://drasl.home.local:25585/web/oidc-callback/Keycloak` (the `Keycloak` at the end MUST match the `Name = "Keycloak"` in Drasl's `[[RegistrationOIDC]]` block — case sensitive) - Web origins: `http://drasl.home.local:25585` 4. **Credentials tab** → copy Client Secret to `./drasl/config/keycloak-client-secret` (no trailing newline). 5. **Advanced tab** → PKCE Code Challenge Method: `S256` ## Client Distribution (LAN guests) ### Recommended launcher **Prism Launcher** — open source, cross-platform (Win/Mac/Linux), first-class authlib-injector support, can import/export instance ZIPs. ### Per-guest one-time setup 1. Install Prism Launcher. 2. Add authlib-injector account: - URL: `http://drasl.home.local:25585/authlib-injector` - Username/password: their Drasl credentials (or Minecraft Token if registered via Keycloak OIDC) 3. Import the modpack instance ZIP. ### What to ship guests - A Prism instance ZIP (right-click instance → Export Instance) - A one-page README with the auth URL and import steps - Optionally `authlib-injector.jar` for guests who refuse Prism ## Open / Pending Work These were on the roadmap when we ended the brainstorm: - [ ] **Mod shortlist for the kitchen-sink pack.** Categories to fill: - Performance (Embeddium/Sodium-equivalent for NeoForge, FerriteCore, ModernFix) - Tech/automation (Mekanism, Create, Immersive Engineering, AE2) - Magic (Ars Nouveau, Botania, Iron's Spells 'n Spellbooks) - Storage (Sophisticated Storage/Backpacks, Functional Storage) - Exploration (YUNG's structures, Repurposed Structures, biome packs) - QoL (JEI, Jade, JEI Resources, Inventory Profiles Next, AppleSkin) - World gen (Tectonic, Terralith — check NeoForge 1.21.1 compat) - Compat glue (Polymorph for recipe conflicts) - Map (XaeroMinimap + Xaero World Map) - Voice (Simple Voice Chat — port 24454/udp already in compose) - [ ] **Decide on dimension/server-side performance mods** (C2ME, Lithium-equivalent) - [ ] **Reverse proxy + local TLS** for Drasl (Caddy + step-ca recommended, given the existing homelab) - [ ] **DNS record** in AdGuard for `drasl.home.local` → Docker host LAN IP - [ ] **Keycloak client** creation per the section above - [ ] **Verify NeoForge version** against current stable before first deploy - [ ] **Bootstrap admin in Drasl**: leave `AllowPasswordLogin = true` initially, create the admin account, then optionally disable password login to force OIDC ## Reference URLs - Drasl repo: https://github.com/unmojang/drasl - Drasl configuration docs: https://github.com/unmojang/drasl/blob/master/doc/configuration.md - Drasl recipes (example configs): https://github.com/unmojang/drasl/blob/master/doc/recipes.md - authlib-injector: https://github.com/yushijinhun/authlib-injector - itzg/minecraft-server: https://github.com/itzg/docker-minecraft-server - itzg NeoForge docs: https://github.com/itzg/docker-minecraft-server/blob/master/docs/types-and-platforms/server-types/forge.md - itzg/mc-backup: https://github.com/itzg/docker-mc-backup - NeoForge versions: https://projects.neoforged.net/neoforged/neoforge - Prism Launcher: https://prismlauncher.org/ - Reference modpacks for inspiration (NOT forking): - ATM10 (Modrinth/CurseForge — ~500 mods, NeoForge 1.21.1) - Leaking Kitchen Sink (CurseForge — trimmed ~150 mods, NeoForge 1.21.1) ## Communication Preferences (carry-over from past sessions) - Concise and direct - Diagnose and resolve without demanding detailed reproduction steps - Prefer iterative single-change updates over large rewrites - Cautious scripts that halt on ambiguity rather than proceeding silently - Avoid over-engineered responses when a simple answer suffices - Spanish or English fine; user is comfortable in both