Oier Bravo Urtasun 5172316919 feat(auth): Blessing Skin variant as Drasl alternative
Override stack that swaps Drasl for Blessing Skin Server + MariaDB,
talking to Minecraft via the yggdrasil-api plugin (authlib endpoint
/api/yggdrasil, ONLINE_MODE=TRUE). Caddy auth.* repointed to
blessing-skin:80; Drasl left idle (compose merges depends_on).

Run: docker compose -f docker-compose.yml -f docker-compose.blessingskin.yml up -d

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 18:48:54 +02:00
2026-05-24 04:03:42 +02:00

Ulicraft Server

Self-hosted modded Minecraft (NeoForge 1.21.1) for a LAN party. Runs online (internet present) or fully air-gapped (offline). Self-hosted auth/skins (Drasl), a packwiz modpack, a guest landing page, and a transparent mirror of Mojang/launcher/NeoForge assets so guest laptops need no internet.

Stack

Service Image Role
minecraft itzg/minecraft-server NeoForge 1.21.1 server, :25565
drasl unmojang/drasl Yggdrasil auth + skins (password login)
caddy caddy:alpine ingress: landing, auth/packwiz proxy, asset mirror (tls internal)
mc-backup itzg/mc-backup world backups every 6h via RCON

Optional layers: avahi (mDNS .local), dnsmasq (turnkey DNS) — only if you don't run your own DNS.

Config lives in .env: BASE_DOMAIN (default ulicraft.lan), HOST_LAN_IP, RCON_PASSWORD, ENABLE_MDNS. Services are subdomains: auth., packwiz., mc., plus the apex landing page.

DNS (pick one)

Names must resolve to HOST_LAN_IP. Use your own party DNS server (recommended):

tooling/dns-records.sh online   # service names only (internet present)
tooling/dns-records.sh airgap   # + Mojang/launcher/NeoForge spoofs (offline)

Paste the printed records into your DNS. Use .lan, not .local.local is intercepted by mDNS and won't resolve via unicast DNS.

Alternatives: ENABLE_MDNS=true + BASE_DOMAIN=*.local (zero-config mDNS, macOS/Linux native, Windows needs Bonjour) — but mDNS can't serve the air-gap upstream spoofs. Or layer in docker-compose.dnsmasq.yml for a turnkey DNS.

Simulate the DNS via /etc/hosts (single machine, testing)

No DNS server? Append the records straight to /etc/hosts (marker-wrapped):

tooling/dns-records.sh online hosts | sudo tee -a /etc/hosts   # services only
tooling/dns-records.sh airgap hosts | sudo tee -a /etc/hosts   # + Mojang spoofs

Remove them later:

sudo sed -i '/# >>> ulicraft/,/# <<< ulicraft/d' /etc/hosts

Notes:

  • /etc/hosts has no wildcard — only the listed names resolve.
  • airgap adds the Mojang/launcher/NeoForge hostnames pointing at the LAN; this breaks normal internet access to those domains while present. Use it only while offline; for an online box use online.
  • mc.<domain> works (client defaults to :25565); SRV isn't used via /etc/hosts.

Prerequisites

Docker + Docker Compose; for prep also pnpm, packwiz, jq, curl, envsubst, sha1sum.

Launch

cp .env.example .env            # set BASE_DOMAIN, HOST_LAN_IP, RCON_PASSWORD

tooling/build-stack.sh --prep            # ONLINE, once: render, build landing,
                                         # mirror mods+assets, fetch launcher +
                                         # authlib, pre-bake the server volume

tooling/build-stack.sh --up online       # internet present, no mirror
tooling/build-stack.sh --up airgap       # offline; full asset mirror on :443 (default)
tooling/build-stack.sh --up core         # base only (debugging)

--up prints the DNS records to add for that mode. Down: docker compose -f docker-compose.yml -f docker-compose.static.yml -f docker-compose.mirror.yml down

Join (guests)

  1. Open http://ulicraft.lan, download FjordLauncherUnlocked for your OS.
  2. Add an authlib-injector account, URL http://auth.ulicraft.lan/authlib-injector (register/login at http://auth.ulicraft.lan).
  3. Air-gap only: trust the local CA — download http://ulicraft.lan/ca.crt and add it to your OS trust store (so the HTTPS asset mirror is trusted).
  4. Import the pack: http://packwiz.ulicraft.lan/pack.toml.
  5. Connect to mc.ulicraft.lan (no port needed).

Layout

docker-compose.yml                    # core: drasl, minecraft, mc-backup, caddy
  .static.yml .mirror.yml             # online/airgap layers
  .avahi.yml .dnsmasq.yml             # optional DNS layers
caddy/Caddyfile + conf.d/*.caddy      # ingress vhost snippets (core/static/mirror)
drasl/config/config.toml.tmpl         # auth config (rendered)
dnsmasq/dnsmasq.conf.tmpl             # optional DNS config (rendered)
docker/avahi/                         # mDNS responder image
pack/                                 # packwiz modpack source
landing/                              # Astro site → www/
tooling/                              # build-stack, render-config, dns-records,
                                      #   mirror-mods, mirror-airgap, fetch-launcher
mirror/                               # vendored jars + launcher + asset mirror (gitignored)
plan/                                 # full design docs (0012)

See plan/00-overview.md for the full design.

Description
No description provided
Readme 79 MiB
Languages
Astro 24.6%
CSS 21.7%
JavaScript 16.1%
TypeScript 15%
Shell 11.3%
Other 11.3%