# ────────────────────────────────────────────────────────────────── # 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. # - ingress is layered on, NOT in this base file: caddy (docker-compose.caddy.yml, # LAN/air-gap, holds mcnet aliases) OR the host's nginx (docker-compose.nginx.yml # + Let's Encrypt, production) # - drasl handles auth (password login; NO Keycloak/OIDC in this stack) # - packwiz pack is served by the ingress at pack.${BASE_DOMAIN} # - minecraft installs mods via PACKWIZ_URL, authlib-injector -> auth. # ────────────────────────────────────────────────────────────────── services: # NOTE: the HTTP(S) ingress is NOT in this base file. # - LAN / air-gap: caddy (docker-compose.caddy.yml) — added by build-stack.sh # - production: the HOST's nginx + Let's Encrypt (docker-compose.nginx.yml # publishes drasl to localhost; nginx/ulicraft.conf.tmpl) # Base alone (drasl + minecraft + mc-backup) is not a complete deployment — # always layer one ingress file on top. 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 the ingress ────────── # http + caddy alias by default; the nginx override switches this to # https + host-gateway (extra_hosts) for the production path. 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 # ingress (caddy/nginx) is layered separately 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