{ config, pkgs, lib, ... }: let mcDataDir = "/srv/minecraft/data"; modrinthMods = builtins.concatStringsSep "," [ # Performance "lithium" "krypton" "ferrite-core" "c2me-fabric" "noisium" "linearly-optimized" "vmp-fabric" "ksyxis" "scalablelux" "lmd" "structure-layout-optimizer" "threadtweak" "crashexploitfixer" # Anti-cheat "anti-xray" "grimac" "no-chat-reports" # Worldgen — terrain (Stardust Labs suite) "terralith" "tectonic" "amplified-nether" "nullscape" # Worldgen — structures (YUNG's suite) "yungs-api" "yungs-better-dungeons" "yungs-better-strongholds" "yungs-better-ocean-monuments" "yungs-better-mineshafts" "yungs-better-desert-temples" "yungs-better-jungle-temples" "yungs-better-witch-huts" "yungs-better-nether-fortresses" "yungs-better-end-island" "yungs-extras" "yungs-bridges" # Client mod compatibility (optional on client side) "appleskin" "simple-voice-chat" # QoL "oneplayersleep" "netherportalfix" "blossomlib" "blossomtpa" "double-shulker-shell-drops" "afkplus" # Moderation "ledger" "styled-chat" # Discord bridge "discord-mc-chat" # Utilities "chunky" ]; jvmFlags = builtins.concatStringsSep " " [ "-XX:+UseG1GC" "-XX:+ParallelRefProcEnabled" "-XX:MaxGCPauseMillis=200" "-XX:+UnlockExperimentalVMOptions" "-XX:+DisableExplicitGC" "-XX:G1NewSizePercent=30" "-XX:G1MaxNewSizePercent=40" "-XX:G1HeapRegionSize=8M" "-XX:G1ReservePercent=20" "-XX:G1MixedGCCountTarget=4" "-XX:InitiatingHeapOccupancyPercent=15" "-XX:G1MixedGCLiveThresholdPercent=90" "-XX:SurvivorRatio=32" "-XX:+PerfDisableSharedMem" "-XX:MaxTenuringThreshold=1" ]; in { virtualisation.docker.enable = true; virtualisation.oci-containers.backend = "docker"; virtualisation.oci-containers.containers.minecraft = { image = "itzg/minecraft-server:java21"; ports = [ "25565:25565" # Minecraft "24454:24454/udp" # Simple Voice Chat ]; volumes = [ "${mcDataDir}:/data" ]; environment = { EULA = "TRUE"; TYPE = "FABRIC"; VERSION = "1.21.4"; MEMORY = "2560M"; MAX_PLAYERS = "10"; DIFFICULTY = "hard"; PVP = "FALSE"; VIEW_DISTANCE = "10"; SIMULATION_DISTANCE = "10"; ENABLE_WHITELIST = "TRUE"; ENFORCE_WHITELIST = "TRUE"; WHITELIST = "jetpham"; OPS = "jetpham"; MOTD = "meet cool people \\u00A7e\\u0026\\u0026\\u00A7r build cool things"; OVERRIDE_ICON = "TRUE"; MODRINTH_PROJECTS = modrinthMods; JVM_XX_OPTS = jvmFlags; }; environmentFiles = [ "/run/minecraft-seed.env" ]; extraOptions = [ "--memory=3g" "--cpus=2" "--restart=unless-stopped" "--pids-limit=256" "--security-opt=no-new-privileges" ]; }; # Ensure data directory exists systemd.tmpfiles.rules = [ "d ${mcDataDir} 0755 root root -" ]; # Write seed env file and copy mod configs before container starts systemd.services.minecraft-mod-configs = { description = "Set up mod configs and seed for Minecraft container"; wantedBy = [ "multi-user.target" ]; before = [ "docker-minecraft.service" ]; after = [ "agenix.service" ]; serviceConfig = { Type = "oneshot"; ExecStart = pkgs.writeShellScript "setup-mod-configs" '' set -euo pipefail # Write seed from agenix secret SEED=$(cat ${config.age.secrets.minecraft-seed.path}) printf 'SEED=%s\n' "$SEED" > /run/minecraft-seed.env chmod 600 /run/minecraft-seed.env # Server icon cp ${../server-icon.png} ${mcDataDir}/server-icon.png # Mod configs mkdir -p ${mcDataDir}/plugins/Chunky cp ${../configs/chunky.yml} ${mcDataDir}/plugins/Chunky/config.yml mkdir -p ${mcDataDir}/config cp ${../configs/anti-xray.toml} ${mcDataDir}/config/anti-xray.toml ''; }; }; }