Move pack/custom/ -> ./custom/ and bind-mount it into caddy at
/srv/pack/custom (served URL pack.${BASE_DOMAIN}/custom/ unchanged).
Keeping jars outside the packwiz pack root stops `packwiz refresh`
from indexing them as direct files on top of the mods/*.pw.toml
external refs (drops 9 bogus custom/*.jar entries from index.toml).
add-custom-mod.sh now writes jars to ./custom/. Trim the blessingskin
caddy override to just the conf swap (volumes merge by target).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
142 lines
5.7 KiB
YAML
142 lines
5.7 KiB
YAML
# ──────────────────────────────────────────────────────────────────
|
|
# Ulicraft LAN-party stack — Minecraft 1.21.1 NeoForge + Drasl auth
|
|
# ──────────────────────────────────────────────────────────────────
|
|
# Single source of config: BASE_DOMAIN + HOST_LAN_IP in .env.
|
|
# DNS is provided by YOUR party DNS server — see tooling/dns-records.sh for the
|
|
# records to add (online vs airgap). A turnkey dnsmasq is available as an
|
|
# optional layer: docker-compose.dnsmasq.yml.
|
|
# - caddy is the only HTTP ingress; holds mcnet aliases so the stack
|
|
# resolves auth.${BASE_DOMAIN} / packwiz.${BASE_DOMAIN} internally too
|
|
# - drasl handles auth (password login; NO Keycloak/OIDC in this stack)
|
|
# - packwiz pack is served by caddy at packwiz.${BASE_DOMAIN}
|
|
# - minecraft installs mods via PACKWIZ_URL, authlib-injector -> auth.
|
|
# ──────────────────────────────────────────────────────────────────
|
|
|
|
services:
|
|
# Core ingress. Static site + air-gap mirror are layered in via override
|
|
# files (docker-compose.static.yml / .mirror.yml) — see build-stack.sh --up.
|
|
caddy:
|
|
image: caddy:alpine
|
|
container_name: caddy
|
|
restart: unless-stopped
|
|
ports:
|
|
- "80:80"
|
|
environment:
|
|
BASE_DOMAIN: ${BASE_DOMAIN}
|
|
volumes:
|
|
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- ./caddy/conf.d/00-core.caddy:/etc/caddy/conf.d/00-core.caddy:ro
|
|
- ./caddy/conf.d/10-static.caddy:/etc/caddy/conf.d/10-static.caddy:ro
|
|
- ./www:/srv/www:ro
|
|
- ./mirror/launcher:/srv/launcher:ro
|
|
- ./caddy/caddy-root-ca.crt:/srv/ca-pub/ca.crt:ro
|
|
- ./pack:/srv/pack:ro
|
|
# Custom (locally-hosted) mod jars live OUTSIDE ./pack so packwiz refresh
|
|
# doesn't index them as direct files — they're external download targets
|
|
# referenced by pack/mods/*.pw.toml. Served at pack.${BASE_DOMAIN}/custom/.
|
|
- ./custom:/srv/pack/custom:ro
|
|
networks:
|
|
mcnet:
|
|
# Only the stack's own subdomains in the base. The upstream spoof-host
|
|
# aliases live in docker-compose.mirror.yml — adding them here would
|
|
# hijack maven.neoforged.net / libraries.minecraft.net for ALL mcnet
|
|
# containers, breaking the ONLINE NeoForge install during pre-bake.
|
|
aliases:
|
|
- "auth.${BASE_DOMAIN}"
|
|
- "pack.${BASE_DOMAIN}"
|
|
drasl:
|
|
image: unmojang/drasl:latest
|
|
container_name: drasl
|
|
restart: unless-stopped
|
|
# Internal-only: no published host port. Reached via Caddy at
|
|
# auth.${BASE_DOMAIN} (Caddy holds the network alias on mcnet).
|
|
volumes:
|
|
- ./drasl/config:/etc/drasl:ro
|
|
- drasl_state:/var/lib/drasl
|
|
networks:
|
|
- mcnet
|
|
# No env vars — config is the rendered ./drasl/config/config.toml
|
|
# (from config.toml.tmpl via tooling/render-config.sh).
|
|
|
|
minecraft:
|
|
image: itzg/minecraft-server:java21
|
|
container_name: minecraft
|
|
restart: unless-stopped
|
|
stdin_open: true
|
|
tty: true
|
|
ports:
|
|
- "25565:25565"
|
|
- "24454:24454/udp" # Simple Voice Chat, if used
|
|
environment:
|
|
EULA: "TRUE"
|
|
TYPE: "NEOFORGE"
|
|
VERSION: "1.21.1"
|
|
# Verify NEOFORGE_VERSION against projects.neoforged.net before deploy.
|
|
NEOFORGE_VERSION: "21.1.209" # pin a known-good build; update deliberately
|
|
|
|
# ── Memory / performance ────────────────────────────────────
|
|
INIT_MEMORY: "4G"
|
|
MAX_MEMORY: "10G" # 8 players + 100 mods comfortably
|
|
USE_AIKAR_FLAGS: "TRUE"
|
|
JVM_XX_OPTS: "-XX:+UseG1GC"
|
|
|
|
# ── Auth: offline mode + authlib-injector pointing at Drasl ─
|
|
ONLINE_MODE: "FALSE"
|
|
# mount authlib-injector.jar at /extras/authlib-injector.jar
|
|
JVM_OPTS: "-javaagent:/extras/authlib-injector.jar=http://auth.${BASE_DOMAIN}/authlib-injector"
|
|
|
|
# ── Server config ───────────────────────────────────────────
|
|
DIFFICULTY: "normal"
|
|
MODE: "survival"
|
|
MOTD: "Uli LAN party"
|
|
MAX_PLAYERS: "10"
|
|
VIEW_DISTANCE: "10"
|
|
SIMULATION_DISTANCE: "8"
|
|
ENFORCE_SECURE_PROFILE: "FALSE" # required for authlib-injector on 1.21+
|
|
ALLOW_FLIGHT: "TRUE" # many tech mods need this
|
|
|
|
# ── Mod source: packwiz pack served by caddy ────────────────
|
|
PACKWIZ_URL: "http://pack.${BASE_DOMAIN}/pack.toml"
|
|
|
|
# ── RCON for remote admin ───────────────────────────────────
|
|
ENABLE_RCON: "TRUE"
|
|
RCON_PASSWORD: ${RCON_PASSWORD}
|
|
RCON_PORT: 25575
|
|
|
|
TZ: "Europe/Madrid"
|
|
volumes:
|
|
- mc_data:/data
|
|
- ./runtime:/extras:ro # authlib-injector.jar lives here
|
|
depends_on:
|
|
- drasl
|
|
- caddy # packwiz pack is served by caddy
|
|
networks:
|
|
- mcnet
|
|
|
|
# Optional: backups
|
|
mc-backup:
|
|
image: itzg/mc-backup
|
|
container_name: mc-backup
|
|
restart: unless-stopped
|
|
environment:
|
|
BACKUP_INTERVAL: "6h"
|
|
RCON_HOST: "minecraft"
|
|
RCON_PASSWORD: ${RCON_PASSWORD}
|
|
PRUNE_BACKUPS_DAYS: "14"
|
|
TZ: "Europe/Madrid"
|
|
volumes:
|
|
- mc_data:/data:ro
|
|
- ./backups:/backups
|
|
depends_on:
|
|
- minecraft
|
|
networks:
|
|
- mcnet
|
|
|
|
volumes:
|
|
drasl_state:
|
|
mc_data:
|
|
|
|
networks:
|
|
mcnet:
|
|
driver: bridge
|