From 7af792ea3fcbbf7685b6bb5cb2436a68ba2cb13c Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Sun, 10 May 2026 13:44:53 -0400 Subject: [PATCH 01/18] feat: velocity proxy --- nix/oyasai-purpur.nix | 130 +++++------- .../src/stacks/platform-services.ts | 194 ++++++------------ packages/oyasai-velocity.nix | 95 ++++++--- 3 files changed, 186 insertions(+), 233 deletions(-) diff --git a/nix/oyasai-purpur.nix b/nix/oyasai-purpur.nix index 7ca48de88..86e3e2d8d 100644 --- a/nix/oyasai-purpur.nix +++ b/nix/oyasai-purpur.nix @@ -1,9 +1,6 @@ { lib, jre, - oyasaiDockerTools, - stdenv, - mc-monitor, writeShellApplication, writeTextFile, formats, @@ -18,6 +15,7 @@ icon ? null, properties ? { }, paperConfig ? null, + passthru ? { }, }: let @@ -30,87 +28,63 @@ let paperGlobalYml = if paperConfig != null then (formats.yaml { }).generate "paper-global.yml" paperConfig else null; +in +writeShellApplication { + inherit name passthru; - result = writeShellApplication { - inherit name; - - runtimeInputs = [ - coreutils - jre - ]; - - text = '' - # Technically not required but prepopulate the cache to ensure - # reproducibility. - mkdir -p cache - cp --no-preserve=ownership,mode ${package.vanillaJar} cache/mojang_${version}.jar + runtimeInputs = [ + coreutils + jre + ]; - # Server icon - ${lib.optionalString (icon != null) '' - cp --no-preserve=ownership,mode ${icon} server-icon.png - ''} + text = '' + # Technically not required but prepopulate the cache to minimize + # non-determinism. + mkdir -p cache + cp --no-preserve=ownership,mode ${package.vanillaJar} cache/mojang_${version}.jar - # Plugins - mkdir -p plugins - rm -rf plugins/.paper-remapped - rm -f plugins/*.jar + # Server icon + ${lib.optionalString (icon != null) '' + cp --no-preserve=ownership,mode ${icon} server-icon.png + ''} - # Cleaner to inject as `--add-plugin` but doesn't work with Plugman well :( - ${lib.optionalString (plugins != [ ]) '' - cp --no-preserve=ownership,mode ${lib.concatStringsSep " " plugins} plugins - ''} + # Plugins + mkdir -p plugins + rm -rf plugins/.paper-remapped + rm -f plugins/*.jar - # Doesn't have cli flag - ${lib.optionalString (paperConfig != null) '' - mkdir -p config - cp --no-preserve=ownership,mode ${paperGlobalYml} config/paper-global.yml - ''} + # Cleaner to inject as `--add-plugin` but doesn't work with Plugman well :( + ${lib.optionalString (plugins != [ ]) '' + cp --no-preserve=ownership,mode ${lib.concatStringsSep " " plugins} plugins + ''} - # Sighs. Doesn't take rcon password as a envvar. - { - cat ${serverProperties} - if [[ -n "''${RCON_PASSWORD:-}" ]]; then - printf 'rcon.password=%s\n' "''${RCON_PASSWORD}" - fi - } >server.properties + ${lib.optionalString (paperConfig != null) '' + mkdir -p config + cp --no-preserve=ownership,mode ${paperGlobalYml} config/paper-global.yml + ''} - MEMORY="''${MEMORY:-2G}" + # Sighs. Doesn't take rcon password as a envvar. + { + cat ${serverProperties} + if [[ -n "''${RCON_PASSWORD:-}" ]]; then + printf 'rcon.password=%s\n' "''${RCON_PASSWORD}" + fi + } >server.properties - # Mostly taken from: https://exa.y2k.diy/garden/jvm-args/ - exec java \ - -Xmx"''${MEMORY}" \ - -Xms"''${MEMORY}" \ - -XX:+UseZGC \ - -XX:+UseCompactObjectHeaders \ - -XX:-OmitStackTraceInFastThrow \ - -Dfile.encoding=UTF-8 \ - -Dcom.mojang.eula.agree=true \ - -Dpaper.disableStartupVersionCheck \ - -jar "${package}/lib/minecraft/server.jar" \ - nogui \ - "$@" - ''; + MEMORY="''${MEMORY:-2G}" - passthru = lib.optionalAttrs stdenv.hostPlatform.isLinux { - docker = oyasaiDockerTools.buildLayeredImage { - inherit name; - config = { - Cmd = [ (lib.getExe result) ]; - WorkingDir = "/data"; - Healthcheck = { - Test = [ - "CMD" - (lib.getExe mc-monitor) - "status" - ]; - Interval = 5 * 1000000000; - Timeout = 5 * 1000000000; - StartPeriod = 60 * 1000000000; - Retries = 20; - }; - }; - }; - }; - }; -in -result + exec java \ + -Xmx"''${MEMORY}" \ + -Xms"''${MEMORY}" \ + -XX:+UseZGC \ + -XX:+AlwaysPreTouch \ + -XX:+DisableExplicitGC \ + -XX:+PerfDisableSharedMem \ + -XX:-OmitStackTraceInFastThrow \ + -Dfile.encoding=UTF-8 \ + -Dcom.mojang.eula.agree=true \ + -jar "${package}/lib/minecraft/server.jar" \ + nogui \ + "$@" + ''; +} diff --git a/packages/oyasai-cdktf/src/stacks/platform-services.ts b/packages/oyasai-cdktf/src/stacks/platform-services.ts index e99900761..42088051a 100644 --- a/packages/oyasai-cdktf/src/stacks/platform-services.ts +++ b/packages/oyasai-cdktf/src/stacks/platform-services.ts @@ -17,6 +17,8 @@ type Props = Readonly<{ }>; export class PlatformServices extends OyasaiPlatformTerraformStack { + private readonly workdir = join("/opt/platform", this.environment); + constructor( scope: Construct, id: string, @@ -49,26 +51,11 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { const imageIds = JSON.parse(process.env.OYASAI_IMAGE_ID as string); const images = { - // keep-sorted start mariadb: imageIds.mariadb, - minecraftAxiom: imageIds["oyasai-minecraft-axiom"], - minecraftBackup: imageIds["mc-backup"], - minecraftLobby: imageIds["oyasai-minecraft-lobby"], - minecraftMain: imageIds["oyasai-minecraft-main"], mysqlBackup: imageIds["mysql-backup"], + minecraftMain: imageIds["oyasai-minecraft-main"], + minecraftBackup: imageIds["mc-backup"], velocity: imageIds["oyasai-velocity"], - // keep-sorted end - } as const; - - const baseHostPath = join("/opt/platform", this.environment); - const hostPaths = { - // keep-sorted start - mariadb: join(baseHostPath, "mariadb"), - minecraftAxiom: join(baseHostPath, "minecraft-axiom"), - minecraftLobby: join(baseHostPath, "minecraft-lobby"), - minecraftMain: join(baseHostPath, "minecraft-main"), - velocity: join(baseHostPath, "velocity"), - // keep-sorted end } as const; const network = new Network(this, this.t("network"), { @@ -86,11 +73,11 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { volumes: [ { containerPath: "/var/lib/mysql", - hostPath: hostPaths.mariadb, + hostPath: join(this.workdir, "mariadb"), }, { containerPath: "/docker-entrypoint-initdb.d", - hostPath: hostPaths.mariadb, + hostPath: join(this.workdir, "mariadb"), }, ], }); @@ -100,7 +87,7 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { this.t("minecraft-main-container"), { image: images.minecraftMain, - name: "oyasai-minecraft-main", + name: "minecraft-main", dependsOn: [mariadbContainer], restart: "unless-stopped", tty: true, @@ -109,17 +96,16 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { init: true, networksAdvanced: [network], ports: ports({ - tcp: [ - 8100, // Bluemap - 8192, // Votifier - 25575, // Rcon - ], + tcp: [8100, 8192, 25575], + udp: [19132], }), env: envs({ MEMORY: this.isMaster - ? // On-prem has 64GB - "20G" - : // GitHub Action runners have 16GB + ? // On-prem has 64GB but looks like 28GB is the most stable because + // JVM GC overhead. No calculation, based on experiments. + "28G" + : // GitHub Action runners have 16GB, but also runs other containers + // so limiting to 10GB. "10G", RCON_PASSWORD: secrets.get("RCON_PASSWORD"), @@ -131,7 +117,7 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { volumes: [ { containerPath: "/data", - hostPath: hostPaths.minecraftMain, + hostPath: join(this.workdir, "minecraft-main"), }, ], ...(this.isMaster && { @@ -142,66 +128,15 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { }, ); - const minecraftLobbyContainer = new Container( - this, - this.t("minecraft-lobby-container"), - { - image: images.minecraftLobby, - name: "oyasai-minecraft-lobby", - dependsOn: [mariadbContainer], - restart: "unless-stopped", - tty: true, - stdinOpen: true, - destroyGraceSeconds: 2 * 60, - init: true, - networksAdvanced: [network], - env: envs({ - MEMORY: "2G", - PAPER_VELOCITY_SECRET: secrets.get("VELOCITY_FORWARDING_SECRET"), - }), - volumes: [ - { - containerPath: "/data", - hostPath: hostPaths.minecraftLobby, - }, - ], - }, - ); - - const minecraftAxiomContainer = new Container( - this, - this.t("minecraft-axiom-container"), - { - image: images.minecraftAxiom, - name: "oyasai-minecraft-axiom", - dependsOn: [mariadbContainer], - restart: "unless-stopped", - tty: true, - stdinOpen: true, - destroyGraceSeconds: 2 * 60, - init: true, - networksAdvanced: [network], - env: envs({ - MEMORY: "8G", - PAPER_VELOCITY_SECRET: secrets.get("VELOCITY_FORWARDING_SECRET"), - }), - volumes: [ - { - containerPath: "/data", - hostPath: hostPaths.minecraftAxiom, - }, - ], - }, - ); - new Container(this, this.t("velocity-container"), { image: images.velocity, name: "velocity", + dependsOn: [minecraftMainContainer], restart: "unless-stopped", networksAdvanced: [network], ports: ports({ - tcp: [25565], // Java - udp: [19132], // Bedrock + tcp: [25565], + udp: [], }), env: envs({ VELOCITY_FORWARDING_SECRET: secrets.get("VELOCITY_FORWARDING_SECRET"), @@ -210,7 +145,7 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { volumes: [ { containerPath: "/data", - hostPath: hostPaths.velocity, + hostPath: join(this.workdir, "velocity"), }, ], }); @@ -218,54 +153,51 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { if (this.isMaster) { const cloudflareBaseUrl = `https://${secrets.get("CLOUDFLARE_ACCOUNT_ID")}.r2.cloudflarestorage.com`; - const backedupMinecraftContainers = { - ["minecraft-main"]: minecraftMainContainer, - ["minecraft-axiom"]: minecraftAxiomContainer, - } as const; - - for (const [backupName, minecraftContainer] of Object.entries( - backedupMinecraftContainers, - )) { - new Container(this, this.t(`${backupName}-backup-container`), { - name: `${backupName}-backup`, - dependsOn: [minecraftContainer], - image: images.minecraftBackup, - networksAdvanced: [network], - restart: "unless-stopped", - env: envs({ - // keep-sorted start block=yes - AWS_ACCESS_KEY_ID: secrets.get("CLOUDFLARE_ACCESS_KEY_ID"), - AWS_SECRET_ACCESS_KEY: secrets.get("CLOUDFLARE_SECRET_ACCESS_KEY"), - BACKUP_INTERVAL: "6h", - BACKUP_METHOD: "restic", - BACKUP_NAME: backupName, - EXCLUDES: [ - // keep-sorted start - "*.hprof", // Spark profiles - they are huge. - "*.jar", - "*.tmp", - "archive", - "bluemap", - "cache", - "crash-reports", - "debug", - "logs", - "versions", - // keep-sorted end - ].join(","), - PRUNE_RESTIC_RETENTION: - "--keep-daily 7 --keep-weekly 4 --keep-monthly 3", - RCON_HOST: minecraftContainer.name, - RCON_PASSWORD: secrets.get("RCON_PASSWORD"), - RESTIC_ADDITIONAL_TAGS: "", // Set to an empty string to disable additional tags. - RESTIC_PASSWORD: secrets.get("RESTIC_PASSWORD"), - RESTIC_REPOSITORY: `s3:${cloudflareBaseUrl}/${r2Bucket.name}/${backupName}-backup`, - RESTIC_VERBOSE: true, + new Container(this, this.t("minecraft-backup-container"), { + name: "minecraft-main-backup", + dependsOn: [minecraftMainContainer], + image: images.minecraftBackup, + networksAdvanced: [network], + restart: "unless-stopped", + env: envs({ + // keep-sorted start block=yes + AWS_ACCESS_KEY_ID: secrets.get("CLOUDFLARE_ACCESS_KEY_ID"), + AWS_SECRET_ACCESS_KEY: secrets.get("CLOUDFLARE_SECRET_ACCESS_KEY"), + BACKUP_INTERVAL: "6h", + BACKUP_METHOD: "restic", + BACKUP_NAME: minecraftMainContainer.name, + EXCLUDES: [ + // keep-sorted start + "*.hprof", // Spark profiles - they are huge. + "*.jar", + "*.tmp", + "archive", + "bluemap", + "cache", + "crash-reports", + "debug", + "logs", + "versions", // keep-sorted end - }), - volumes: minecraftContainer.volumesInput, - }); - } + ].join(","), + PRUNE_RESTIC_RETENTION: + "--keep-daily 7 --keep-weekly 4 --keep-monthly 3", + RCON_HOST: "minecraft-main", + RCON_PASSWORD: secrets.get("RCON_PASSWORD"), + RESTIC_ADDITIONAL_TAGS: "", // Set to an empty string to disable additional tags. + RESTIC_PASSWORD: secrets.get("RESTIC_PASSWORD"), + RESTIC_REPOSITORY: `s3:${cloudflareBaseUrl}/${r2Bucket.name}/minecraft-main-backup`, + RESTIC_VERBOSE: true, + // keep-sorted end + }), + volumes: [ + { + hostPath: join(this.workdir, "minecraft-main"), + containerPath: "/data", + readOnly: true, + }, + ], + }); new Container(this, this.t("mariadb-backup-container"), { name: "mariadb-backup", diff --git a/packages/oyasai-velocity.nix b/packages/oyasai-velocity.nix index 99bce5a42..caed8a32f 100644 --- a/packages/oyasai-velocity.nix +++ b/packages/oyasai-velocity.nix @@ -1,35 +1,82 @@ { - oyasaiVelocity, - oyasai-plugin-registry, - oyasai-minecraft-main, - oyasai-minecraft-lobby, - oyasai-minecraft-axiom, + lib, + oyasaiDockerTools, + stdenv, + velocityServers, + writeShellApplication, + formats, + coreutils, }: -oyasaiVelocity { +let name = "oyasai-velocity"; + package = velocityServers.velocity; - plugins = with oyasai-plugin-registry.forVersion "velocity"; [ - # keep-sorted start - floodgate-velocity - geyser-velocity - # keep-sorted end - ]; - - velocityConfig = { - config-version = "2.8"; - motd = " OyasaiServer\n 建築勢は集合だ!建築!建築!建築!!!"; + velocityToml = (formats.toml { }).generate "velocity.toml" { + config-version = "2.7"; + bind = "0.0.0.0:25565"; + motd = "OyasaiServer"; + show-max-players = 500; + online-mode = true; + force-key-authentication = true; + prevent-client-proxy-connections = false; player-info-forwarding-mode = "modern"; + announce-forge = false; + kick-existing-players = false; + ping-passthrough = "DISABLED"; + enable-player-address-logging = true; - # Velocity tries to set default - forced-hosts = { }; - - # TODO: set lobby as default server after experimentation. servers = { - main = oyasai-minecraft-main.name; - lobby = oyasai-minecraft-lobby.name; - axiom = oyasai-minecraft-axiom.name; + main = "minecraft-main:25565"; try = [ "main" ]; }; + + forced-hosts = { }; + + advanced = { + compression-threshold = 256; + compression-level = -1; + login-ratelimiting-attempts = 3; + login-ratelimiting-period = 3000; + connection-timeout = 5000; + read-timeout = 30000; + haproxy-protocol = false; + tcp-fast-open = false; + bungee-plugin-message-channel = true; + show-ping-requests = false; + failover-on-unexpected-server-disconnect = true; + announce-proxy-commands = true; + log-command-executions = false; + log-player-summary-on-shutdown = true; + accepts-transfers = false; + }; + + query = { + enabled = false; + port = 25577; + map = "Velocity"; + show-plugins = false; + }; + }; + + final = writeShellApplication { + inherit name; + runtimeInputs = [ coreutils ]; + text = '' + cp --no-preserve=ownership,mode ${velocityToml} velocity.toml + + MEMORY="''${MEMORY:-512M}" + exec ${lib.getExe package} -Xmx"''${MEMORY}" -Xms"''${MEMORY}" "$@" + ''; + passthru = lib.optionalAttrs stdenv.hostPlatform.isLinux { + docker = oyasaiDockerTools.buildLayeredImage { + inherit name; + config = { + Cmd = [ (lib.getExe final) ]; + WorkingDir = "/data"; + }; + }; + }; }; -} +in +final From 5f56d49568c8b771cc3551cbddd42a69fd03d210 Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Sun, 10 May 2026 15:06:02 -0400 Subject: [PATCH 02/18] NOMERGE --- .github/workflows/modify-platform-stack.yaml | 16 ++++++++++++++++ nix/oyasai-purpur.nix | 5 +++++ packages/oyasai-minecraft-main.nix | 2 ++ 3 files changed, 23 insertions(+) diff --git a/.github/workflows/modify-platform-stack.yaml b/.github/workflows/modify-platform-stack.yaml index dea9e604e..eba564582 100644 --- a/.github/workflows/modify-platform-stack.yaml +++ b/.github/workflows/modify-platform-stack.yaml @@ -89,6 +89,22 @@ jobs: nix run .#oyasai-infra -- \ terraform_io::delete_workspace \ ${{ inputs.stack }} + - name: Debug containers NOMERGE + if: inputs.start-proxy + working-directory: . + run: | + echo "=== Waiting 90s for minecraft-main to start ===" + sleep 90 + echo "=== docker ps ===" + docker ps -a + echo "=== minecraft-main logs (last 80) ===" + docker logs --tail 80 minecraft-main 2>&1 || echo "(no logs)" + echo "=== velocity logs ===" + docker logs velocity 2>&1 || echo "(no logs)" + echo "=== paper-global.yml ===" + docker cp minecraft-main:/data/config/paper-global.yml - 2>/dev/null | tar -xOf - paper-global.yml || echo "(could not read)" + echo "=== server.properties ===" + docker cp minecraft-main:/data/server.properties - 2>/dev/null | tar -xOf - server.properties | grep -E "online-mode|velocity|rcon" || echo "(could not read)" - name: Start Proxy if: inputs.start-proxy # It's free, feel free to increase diff --git a/nix/oyasai-purpur.nix b/nix/oyasai-purpur.nix index 86e3e2d8d..4c30964ab 100644 --- a/nix/oyasai-purpur.nix +++ b/nix/oyasai-purpur.nix @@ -5,6 +5,7 @@ writeTextFile, formats, coreutils, + gnused, purpurServers, }: @@ -35,6 +36,7 @@ writeShellApplication { runtimeInputs = [ coreutils jre + gnused ]; text = '' @@ -61,6 +63,9 @@ writeShellApplication { ${lib.optionalString (paperConfig != null) '' mkdir -p config cp --no-preserve=ownership,mode ${paperGlobalYml} config/paper-global.yml + if [ -n "''${PAPER_VELOCITY_SECRET:-}" ]; then + sed -i "s/PAPER_VELOCITY_SECRET_PLACEHOLDER/''${PAPER_VELOCITY_SECRET}/" config/paper-global.yml + fi ''} # Sighs. Doesn't take rcon password as a envvar. diff --git a/packages/oyasai-minecraft-main.nix b/packages/oyasai-minecraft-main.nix index 5f81a1f63..3b36b31d2 100644 --- a/packages/oyasai-minecraft-main.nix +++ b/packages/oyasai-minecraft-main.nix @@ -34,6 +34,8 @@ oyasaiPurpur rec { proxies.velocity = { enabled = true; online-mode = true; + # NOMERGE + secret = "PAPER_VELOCITY_SECRET_PLACEHOLDER"; }; }; From f6c1a5f4396b23365055614a86a581a4b21bf79c Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Sun, 10 May 2026 16:52:49 -0400 Subject: [PATCH 03/18] fix: inject as envvar --- .github/workflows/modify-platform-stack.yaml | 16 ---------------- nix/oyasai-purpur.nix | 5 ----- packages/oyasai-minecraft-main.nix | 2 -- 3 files changed, 23 deletions(-) diff --git a/.github/workflows/modify-platform-stack.yaml b/.github/workflows/modify-platform-stack.yaml index eba564582..dea9e604e 100644 --- a/.github/workflows/modify-platform-stack.yaml +++ b/.github/workflows/modify-platform-stack.yaml @@ -89,22 +89,6 @@ jobs: nix run .#oyasai-infra -- \ terraform_io::delete_workspace \ ${{ inputs.stack }} - - name: Debug containers NOMERGE - if: inputs.start-proxy - working-directory: . - run: | - echo "=== Waiting 90s for minecraft-main to start ===" - sleep 90 - echo "=== docker ps ===" - docker ps -a - echo "=== minecraft-main logs (last 80) ===" - docker logs --tail 80 minecraft-main 2>&1 || echo "(no logs)" - echo "=== velocity logs ===" - docker logs velocity 2>&1 || echo "(no logs)" - echo "=== paper-global.yml ===" - docker cp minecraft-main:/data/config/paper-global.yml - 2>/dev/null | tar -xOf - paper-global.yml || echo "(could not read)" - echo "=== server.properties ===" - docker cp minecraft-main:/data/server.properties - 2>/dev/null | tar -xOf - server.properties | grep -E "online-mode|velocity|rcon" || echo "(could not read)" - name: Start Proxy if: inputs.start-proxy # It's free, feel free to increase diff --git a/nix/oyasai-purpur.nix b/nix/oyasai-purpur.nix index 4c30964ab..c4f2d9bcb 100644 --- a/nix/oyasai-purpur.nix +++ b/nix/oyasai-purpur.nix @@ -5,7 +5,6 @@ writeTextFile, formats, coreutils, - gnused, purpurServers, }: @@ -35,7 +34,6 @@ writeShellApplication { runtimeInputs = [ coreutils - jre gnused ]; @@ -63,9 +61,6 @@ writeShellApplication { ${lib.optionalString (paperConfig != null) '' mkdir -p config cp --no-preserve=ownership,mode ${paperGlobalYml} config/paper-global.yml - if [ -n "''${PAPER_VELOCITY_SECRET:-}" ]; then - sed -i "s/PAPER_VELOCITY_SECRET_PLACEHOLDER/''${PAPER_VELOCITY_SECRET}/" config/paper-global.yml - fi ''} # Sighs. Doesn't take rcon password as a envvar. diff --git a/packages/oyasai-minecraft-main.nix b/packages/oyasai-minecraft-main.nix index 3b36b31d2..5f81a1f63 100644 --- a/packages/oyasai-minecraft-main.nix +++ b/packages/oyasai-minecraft-main.nix @@ -34,8 +34,6 @@ oyasaiPurpur rec { proxies.velocity = { enabled = true; online-mode = true; - # NOMERGE - secret = "PAPER_VELOCITY_SECRET_PLACEHOLDER"; }; }; From 38688d0e134e543b57547a02e7f2e70b260e6ae3 Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Sun, 10 May 2026 17:47:18 -0400 Subject: [PATCH 04/18] NOMERGE debug --- nix/oyasai-purpur.nix | 7 +++++-- packages/oyasai-cdktf/src/stacks/platform-services.ts | 10 ++++++++-- packages/oyasai-velocity.nix | 3 +++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/nix/oyasai-purpur.nix b/nix/oyasai-purpur.nix index c4f2d9bcb..edbdb0436 100644 --- a/nix/oyasai-purpur.nix +++ b/nix/oyasai-purpur.nix @@ -6,6 +6,7 @@ formats, coreutils, purpurServers, + gnused }: { @@ -34,12 +35,13 @@ writeShellApplication { runtimeInputs = [ coreutils + jre gnused ]; text = '' - # Technically not required but prepopulate the cache to minimize - # non-determinism. + # Technically not required but prepopulate the cache to ensure + # reproducability. mkdir -p cache cp --no-preserve=ownership,mode ${package.vanillaJar} cache/mojang_${version}.jar @@ -58,6 +60,7 @@ writeShellApplication { cp --no-preserve=ownership,mode ${lib.concatStringsSep " " plugins} plugins ''} + # Doesn't have cli flag ${lib.optionalString (paperConfig != null) '' mkdir -p config cp --no-preserve=ownership,mode ${paperGlobalYml} config/paper-global.yml diff --git a/packages/oyasai-cdktf/src/stacks/platform-services.ts b/packages/oyasai-cdktf/src/stacks/platform-services.ts index 42088051a..73978ef2d 100644 --- a/packages/oyasai-cdktf/src/stacks/platform-services.ts +++ b/packages/oyasai-cdktf/src/stacks/platform-services.ts @@ -96,8 +96,14 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { init: true, networksAdvanced: [network], ports: ports({ - tcp: [8100, 8192, 25575], - udp: [19132], + tcp: [ + 8100, // Bluemap + 8192, // Votifier + 25575 // Rcon + ], + udp: [ + 19132 // Bedrock + ], }), env: envs({ MEMORY: this.isMaster diff --git a/packages/oyasai-velocity.nix b/packages/oyasai-velocity.nix index caed8a32f..d34bce705 100644 --- a/packages/oyasai-velocity.nix +++ b/packages/oyasai-velocity.nix @@ -61,13 +61,16 @@ let final = writeShellApplication { inherit name; + runtimeInputs = [ coreutils ]; + text = '' cp --no-preserve=ownership,mode ${velocityToml} velocity.toml MEMORY="''${MEMORY:-512M}" exec ${lib.getExe package} -Xmx"''${MEMORY}" -Xms"''${MEMORY}" "$@" ''; + passthru = lib.optionalAttrs stdenv.hostPlatform.isLinux { docker = oyasaiDockerTools.buildLayeredImage { inherit name; From bb88aef33a138a673c173dd4a210a68c95ff6f4c Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Mon, 25 May 2026 00:45:19 -0400 Subject: [PATCH 05/18] refactor: centralize docker --- nix/oyasai-purpur.nix | 133 ++++++++++-------- .../src/stacks/platform-services.ts | 4 +- packages/oyasai-velocity.nix | 41 +----- 3 files changed, 84 insertions(+), 94 deletions(-) diff --git a/nix/oyasai-purpur.nix b/nix/oyasai-purpur.nix index edbdb0436..da17289ef 100644 --- a/nix/oyasai-purpur.nix +++ b/nix/oyasai-purpur.nix @@ -1,12 +1,14 @@ { lib, jre, + oyasaiDockerTools, + stdenv, + mc-monitor, writeShellApplication, writeTextFile, formats, coreutils, purpurServers, - gnused }: { @@ -16,7 +18,6 @@ icon ? null, properties ? { }, paperConfig ? null, - passthru ? { }, }: let @@ -29,65 +30,87 @@ let paperGlobalYml = if paperConfig != null then (formats.yaml { }).generate "paper-global.yml" paperConfig else null; -in -writeShellApplication { - inherit name passthru; - runtimeInputs = [ - coreutils - jre - gnused - ]; + result = writeShellApplication { + inherit name; + + runtimeInputs = [ + coreutils + jre + ]; + + passthru = lib.optionalAttrs stdenv.hostPlatform.isLinux { + docker = oyasaiDockerTools.buildLayeredImage { + inherit name; + config = { + Cmd = [ (lib.getExe result) ]; + WorkingDir = "/data"; + Healthcheck = { + Test = [ + "CMD" + (lib.getExe mc-monitor) + "status" + ]; + Interval = 5 * 1000000000; + Timeout = 5 * 1000000000; + StartPeriod = 60 * 1000000000; + Retries = 20; + }; + }; + }; + }; - text = '' - # Technically not required but prepopulate the cache to ensure - # reproducability. - mkdir -p cache - cp --no-preserve=ownership,mode ${package.vanillaJar} cache/mojang_${version}.jar + text = '' + # Technically not required but prepopulate the cache to ensure + # reproducibility. + mkdir -p cache + cp --no-preserve=ownership,mode ${package.vanillaJar} cache/mojang_${version}.jar - # Server icon - ${lib.optionalString (icon != null) '' - cp --no-preserve=ownership,mode ${icon} server-icon.png - ''} + # Server icon + ${lib.optionalString (icon != null) '' + cp --no-preserve=ownership,mode ${icon} server-icon.png + ''} - # Plugins - mkdir -p plugins - rm -rf plugins/.paper-remapped - rm -f plugins/*.jar + # Plugins + mkdir -p plugins + rm -rf plugins/.paper-remapped + rm -f plugins/*.jar - # Cleaner to inject as `--add-plugin` but doesn't work with Plugman well :( - ${lib.optionalString (plugins != [ ]) '' - cp --no-preserve=ownership,mode ${lib.concatStringsSep " " plugins} plugins - ''} + # Cleaner to inject as `--add-plugin` but doesn't work with Plugman well :( + ${lib.optionalString (plugins != [ ]) '' + cp --no-preserve=ownership,mode ${lib.concatStringsSep " " plugins} plugins + ''} - # Doesn't have cli flag - ${lib.optionalString (paperConfig != null) '' - mkdir -p config - cp --no-preserve=ownership,mode ${paperGlobalYml} config/paper-global.yml - ''} + # Doesn't have cli flag + ${lib.optionalString (paperConfig != null) '' + mkdir -p config + cp --no-preserve=ownership,mode ${paperGlobalYml} config/paper-global.yml + ''} - # Sighs. Doesn't take rcon password as a envvar. - { - cat ${serverProperties} - if [[ -n "''${RCON_PASSWORD:-}" ]]; then - printf 'rcon.password=%s\n' "''${RCON_PASSWORD}" - fi - } >server.properties + # Sighs. Doesn't take rcon password as a envvar. + { + cat ${serverProperties} + if [[ -n "''${RCON_PASSWORD:-}" ]]; then + printf 'rcon.password=%s\n' "''${RCON_PASSWORD}" + fi + } >server.properties - MEMORY="''${MEMORY:-2G}" + MEMORY="''${MEMORY:-2G}" - exec java \ - -Xmx"''${MEMORY}" \ - -Xms"''${MEMORY}" \ - -XX:+UseZGC \ - -XX:+AlwaysPreTouch \ - -XX:+DisableExplicitGC \ - -XX:+PerfDisableSharedMem \ - -XX:-OmitStackTraceInFastThrow \ - -Dfile.encoding=UTF-8 \ - -Dcom.mojang.eula.agree=true \ - -jar "${package}/lib/minecraft/server.jar" \ - nogui \ - "$@" - ''; -} + exec java \ + -Xmx"''${MEMORY}" \ + -Xms"''${MEMORY}" \ + -XX:+UseZGC \ + -XX:+AlwaysPreTouch \ + -XX:+DisableExplicitGC \ + -XX:+PerfDisableSharedMem \ + -XX:-OmitStackTraceInFastThrow \ + -Dfile.encoding=UTF-8 \ + -Dcom.mojang.eula.agree=true \ + -jar "${package}/lib/minecraft/server.jar" \ + nogui \ + "$@" + ''; + }; +in +result diff --git a/packages/oyasai-cdktf/src/stacks/platform-services.ts b/packages/oyasai-cdktf/src/stacks/platform-services.ts index 73978ef2d..16a54c0a0 100644 --- a/packages/oyasai-cdktf/src/stacks/platform-services.ts +++ b/packages/oyasai-cdktf/src/stacks/platform-services.ts @@ -99,10 +99,10 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { tcp: [ 8100, // Bluemap 8192, // Votifier - 25575 // Rcon + 25575, // Rcon ], udp: [ - 19132 // Bedrock + 19132, // Bedrock ], }), env: envs({ diff --git a/packages/oyasai-velocity.nix b/packages/oyasai-velocity.nix index d34bce705..993ff4e4c 100644 --- a/packages/oyasai-velocity.nix +++ b/packages/oyasai-velocity.nix @@ -1,18 +1,9 @@ -{ - lib, - oyasaiDockerTools, - stdenv, - velocityServers, - writeShellApplication, - formats, - coreutils, -}: +{ oyasaiVelocity }: -let +oyasaiVelocity { name = "oyasai-velocity"; - package = velocityServers.velocity; - velocityToml = (formats.toml { }).generate "velocity.toml" { + velocityConfig = { config-version = "2.7"; bind = "0.0.0.0:25565"; motd = "OyasaiServer"; @@ -58,28 +49,4 @@ let show-plugins = false; }; }; - - final = writeShellApplication { - inherit name; - - runtimeInputs = [ coreutils ]; - - text = '' - cp --no-preserve=ownership,mode ${velocityToml} velocity.toml - - MEMORY="''${MEMORY:-512M}" - exec ${lib.getExe package} -Xmx"''${MEMORY}" -Xms"''${MEMORY}" "$@" - ''; - - passthru = lib.optionalAttrs stdenv.hostPlatform.isLinux { - docker = oyasaiDockerTools.buildLayeredImage { - inherit name; - config = { - Cmd = [ (lib.getExe final) ]; - WorkingDir = "/data"; - }; - }; - }; - }; -in -final +} From 39053cb361c46d7a3c6b74736921fa7029bc9414 Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Mon, 25 May 2026 00:59:27 -0400 Subject: [PATCH 06/18] feat: geyser velocity --- .../src/stacks/platform-services.ts | 7 ++-- packages/oyasai-plugin-registry/lock.json | 32 ++++++++----------- packages/oyasai-velocity.nix | 9 +++++- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/packages/oyasai-cdktf/src/stacks/platform-services.ts b/packages/oyasai-cdktf/src/stacks/platform-services.ts index 16a54c0a0..64a603ea4 100644 --- a/packages/oyasai-cdktf/src/stacks/platform-services.ts +++ b/packages/oyasai-cdktf/src/stacks/platform-services.ts @@ -101,9 +101,6 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { 8192, // Votifier 25575, // Rcon ], - udp: [ - 19132, // Bedrock - ], }), env: envs({ MEMORY: this.isMaster @@ -141,8 +138,8 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { restart: "unless-stopped", networksAdvanced: [network], ports: ports({ - tcp: [25565], - udp: [], + tcp: [25565], // Java + udp: [19132], // Bedrock }), env: envs({ VELOCITY_FORWARDING_SECRET: secrets.get("VELOCITY_FORWARDING_SECRET"), diff --git a/packages/oyasai-plugin-registry/lock.json b/packages/oyasai-plugin-registry/lock.json index e911d03d1..78046d382 100644 --- a/packages/oyasai-plugin-registry/lock.json +++ b/packages/oyasai-plugin-registry/lock.json @@ -5,12 +5,6 @@ "hash": "sha256-e0llrauKgRDij8wUYDvnFIJzwuVDAU2RXd9EpjkDHdg=" } }, - "axiom-paper-plugin": { - "1.21.10": { - "url": "https://cdn.modrinth.com/data/evkiwA7V/versions/KIGJ1Vhv/AxiomPaperPlugin-5.0.4-for-MC1.21.10.jar", - "hash": "sha256-zbgssJUQ6j4UGQwQOzNer2sOxAK8pn5QOpm0KLnyBUg=" - } - }, "bkcommonlib": { "1.21.10": { "url": "https://cdn.modrinth.com/data/rTg6ckWb/versions/yZdQcJiN/BKCommonLib-1.21.11-v1-1957.jar", @@ -65,12 +59,6 @@ "hash": "sha256-TVCKrAe29k8Fh/xIH+FeXQoWc89iiv69up3KF7L75Y4=" } }, - "fastasyncvoxelsniper": { - "1.21.10": { - "url": "https://cdn.modrinth.com/data/D7XBSI1y/versions/n77tXMjA/fastasyncvoxelsniper-3.2.4.jar", - "hash": "sha256-k1qgEacp/UDLh/0+Nxwg3UWBMDVH1PksMcvGCOfl7LM=" - } - }, "fastasyncworldedit": { "1.21.10": { "url": "https://cdn.modrinth.com/data/z4HZZnLr/versions/Dx0x0kQW/FastAsyncWorldEdit-Bukkit-2.15.1.jar", @@ -89,10 +77,16 @@ "hash": "sha256-8liZUEOkhpy28e9gURCsHZBmpbHhsxZJWiWwavoMEGA=" } }, + "geyser": { + "1.21.10": { + "url": "https://download.geysermc.org/v2/projects/geyser/versions/2.10.0/builds/1154/downloads/spigot", + "hash": "sha256-ew0GDWTT0Dik8jykLNeLPtexXiH9n+568R+VKmP3zxc=" + } + }, "geyser-velocity": { "velocity": { - "url": "https://download.geysermc.org/v2/projects/geyser/versions/2.10.0/builds/1155/downloads/velocity", - "hash": "sha256-KS6bO7KbBYmesCYhdaqZkgixe6fSNBO2IP0igJfyqnE=" + "url": "https://download.geysermc.org/v2/projects/geyser/versions/2.10.0/builds/1154/downloads/velocity", + "hash": "sha256-/SF/dBIrDNTTo37DSoA0IymfTuxR3d57kmiUZbWUUd0=" } }, "gsit": { @@ -121,8 +115,8 @@ }, "luckperms": { "1.21.10": { - "url": "https://cdn.modrinth.com/data/Vebnzrzj/versions/MBSY8toc/LuckPerms-Bukkit-5.5.53.jar", - "hash": "sha256-/I1OzL8RwehEr0Un8Bi7/ekMGGapq6G/iAFzqOZEzVk=" + "url": "https://cdn.modrinth.com/data/Vebnzrzj/versions/OrIs0S6b/LuckPerms-Bukkit-5.5.17.jar", + "hash": "sha256-1bFgo5cag3LMWDW81VXjfBqmHp3TBVmSGl9CGhG/l90=" } }, "lunachat": { @@ -149,7 +143,7 @@ "hash": "sha256-qfvQIMRGNf6ZvaXP6ARjPVQwDlsAz5B2ja/+feDSywA=" } }, - "multiverseportals": { + "multiverseprotals": { "1.21.10": { "url": "https://cdn.modrinth.com/data/8VMk6P0I/versions/BguzNJ3r/multiverse-portals-5.2.2.jar", "hash": "sha256-RZa2a5rKhidf7lu9Jn7VkhpzCThic7GnP5olJJdHqA4=" @@ -265,8 +259,8 @@ }, "viaversion": { "1.21.10": { - "url": "https://cdn.modrinth.com/data/P1OZGk5p/versions/lrrqDnmv/ViaVersion-5.9.2-SNAPSHOT.jar", - "hash": "sha256-7hV4UNV5Qm9FDVU9Y7spSUED4eURP19bZrgPEBiZI+M=" + "url": "https://cdn.modrinth.com/data/P1OZGk5p/versions/cXLtSQvv/ViaVersion-5.9.2-SNAPSHOT.jar", + "hash": "sha256-AkKV2g8JC2HRtosc4IKJF9jShhQz3yHO1Q9hh1JK5Ts=" } }, "worldborder": { diff --git a/packages/oyasai-velocity.nix b/packages/oyasai-velocity.nix index 993ff4e4c..c45907b71 100644 --- a/packages/oyasai-velocity.nix +++ b/packages/oyasai-velocity.nix @@ -1,8 +1,15 @@ -{ oyasaiVelocity }: +{ oyasaiVelocity, oyasai-plugin-registry }: oyasaiVelocity { name = "oyasai-velocity"; + plugins = with oyasai-plugin-registry.forVersion "velocity"; [ + # keep-sorted start + floodgate-velocity + geyser-velocity + # keep-sorted end + ]; + velocityConfig = { config-version = "2.7"; bind = "0.0.0.0:25565"; From f3113d83d3acf377a8e9424437d93682d7ee339f Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Mon, 25 May 2026 01:03:03 -0400 Subject: [PATCH 07/18] fix: remove unused geyser --- nix/oyasai-purpur.nix | 42 +++++++++++------------ packages/oyasai-plugin-registry/lock.json | 6 ---- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/nix/oyasai-purpur.nix b/nix/oyasai-purpur.nix index da17289ef..8350d6c99 100644 --- a/nix/oyasai-purpur.nix +++ b/nix/oyasai-purpur.nix @@ -39,27 +39,6 @@ let jre ]; - passthru = lib.optionalAttrs stdenv.hostPlatform.isLinux { - docker = oyasaiDockerTools.buildLayeredImage { - inherit name; - config = { - Cmd = [ (lib.getExe result) ]; - WorkingDir = "/data"; - Healthcheck = { - Test = [ - "CMD" - (lib.getExe mc-monitor) - "status" - ]; - Interval = 5 * 1000000000; - Timeout = 5 * 1000000000; - StartPeriod = 60 * 1000000000; - Retries = 20; - }; - }; - }; - }; - text = '' # Technically not required but prepopulate the cache to ensure # reproducibility. @@ -111,6 +90,27 @@ let nogui \ "$@" ''; + + passthru = lib.optionalAttrs stdenv.hostPlatform.isLinux { + docker = oyasaiDockerTools.buildLayeredImage { + inherit name; + config = { + Cmd = [ (lib.getExe result) ]; + WorkingDir = "/data"; + Healthcheck = { + Test = [ + "CMD" + (lib.getExe mc-monitor) + "status" + ]; + Interval = 5 * 1000000000; + Timeout = 5 * 1000000000; + StartPeriod = 60 * 1000000000; + Retries = 20; + }; + }; + }; + }; }; in result diff --git a/packages/oyasai-plugin-registry/lock.json b/packages/oyasai-plugin-registry/lock.json index 78046d382..cc323e6bc 100644 --- a/packages/oyasai-plugin-registry/lock.json +++ b/packages/oyasai-plugin-registry/lock.json @@ -77,12 +77,6 @@ "hash": "sha256-8liZUEOkhpy28e9gURCsHZBmpbHhsxZJWiWwavoMEGA=" } }, - "geyser": { - "1.21.10": { - "url": "https://download.geysermc.org/v2/projects/geyser/versions/2.10.0/builds/1154/downloads/spigot", - "hash": "sha256-ew0GDWTT0Dik8jykLNeLPtexXiH9n+568R+VKmP3zxc=" - } - }, "geyser-velocity": { "velocity": { "url": "https://download.geysermc.org/v2/projects/geyser/versions/2.10.0/builds/1154/downloads/velocity", From 30563cede85beaba50fc931a73718d953dc42788 Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Mon, 25 May 2026 01:20:27 -0400 Subject: [PATCH 08/18] refactor: workdir unify --- .../oyasai-cdktf/src/stacks/platform-services.ts | 12 ++++++++---- packages/oyasai-velocity.nix | 10 +++++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/oyasai-cdktf/src/stacks/platform-services.ts b/packages/oyasai-cdktf/src/stacks/platform-services.ts index 64a603ea4..9e91063dd 100644 --- a/packages/oyasai-cdktf/src/stacks/platform-services.ts +++ b/packages/oyasai-cdktf/src/stacks/platform-services.ts @@ -82,12 +82,16 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { ], }); + // Can change to "oyasai-minecraft-main" for naming consistency but too lazy + // to do the data migration - shun 2026-05 + const minecraftMainWorkDir = join(this.workdir, "minecraft-main"); + const minecraftMainContainer = new Container( this, this.t("minecraft-main-container"), { image: images.minecraftMain, - name: "minecraft-main", + name: "oyasai-minecraft-main", dependsOn: [mariadbContainer], restart: "unless-stopped", tty: true, @@ -120,7 +124,7 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { volumes: [ { containerPath: "/data", - hostPath: join(this.workdir, "minecraft-main"), + hostPath: minecraftMainWorkDir, }, ], ...(this.isMaster && { @@ -185,7 +189,7 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { ].join(","), PRUNE_RESTIC_RETENTION: "--keep-daily 7 --keep-weekly 4 --keep-monthly 3", - RCON_HOST: "minecraft-main", + RCON_HOST: minecraftMainContainer.name, RCON_PASSWORD: secrets.get("RCON_PASSWORD"), RESTIC_ADDITIONAL_TAGS: "", // Set to an empty string to disable additional tags. RESTIC_PASSWORD: secrets.get("RESTIC_PASSWORD"), @@ -195,7 +199,7 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { }), volumes: [ { - hostPath: join(this.workdir, "minecraft-main"), + hostPath: minecraftMainWorkDir, containerPath: "/data", readOnly: true, }, diff --git a/packages/oyasai-velocity.nix b/packages/oyasai-velocity.nix index c45907b71..b99e6c896 100644 --- a/packages/oyasai-velocity.nix +++ b/packages/oyasai-velocity.nix @@ -1,4 +1,8 @@ -{ oyasaiVelocity, oyasai-plugin-registry }: +{ + oyasaiVelocity, + oyasai-plugin-registry, + oyasai-minecraft-main, +}: oyasaiVelocity { name = "oyasai-velocity"; @@ -13,7 +17,7 @@ oyasaiVelocity { velocityConfig = { config-version = "2.7"; bind = "0.0.0.0:25565"; - motd = "OyasaiServer"; + motd = "§l§r §b§lOyasai§f§lServer§r\\n§l§f 建築勢は集合だ!建築!建築!建築!!!"; show-max-players = 500; online-mode = true; force-key-authentication = true; @@ -25,7 +29,7 @@ oyasaiVelocity { enable-player-address-logging = true; servers = { - main = "minecraft-main:25565"; + main = "${oyasai-minecraft-main.name}:25565"; try = [ "main" ]; }; From f8d8ba9339d2535936dd4e5c536d3fe284716498 Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Mon, 25 May 2026 01:27:54 -0400 Subject: [PATCH 09/18] refactor: remove unused configs --- packages/oyasai-velocity.nix | 38 +----------------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/packages/oyasai-velocity.nix b/packages/oyasai-velocity.nix index b99e6c896..a15939972 100644 --- a/packages/oyasai-velocity.nix +++ b/packages/oyasai-velocity.nix @@ -15,49 +15,13 @@ oyasaiVelocity { ]; velocityConfig = { - config-version = "2.7"; - bind = "0.0.0.0:25565"; + config-version = "2.8"; motd = "§l§r §b§lOyasai§f§lServer§r\\n§l§f 建築勢は集合だ!建築!建築!建築!!!"; - show-max-players = 500; - online-mode = true; - force-key-authentication = true; - prevent-client-proxy-connections = false; player-info-forwarding-mode = "modern"; - announce-forge = false; - kick-existing-players = false; - ping-passthrough = "DISABLED"; - enable-player-address-logging = true; servers = { main = "${oyasai-minecraft-main.name}:25565"; try = [ "main" ]; }; - - forced-hosts = { }; - - advanced = { - compression-threshold = 256; - compression-level = -1; - login-ratelimiting-attempts = 3; - login-ratelimiting-period = 3000; - connection-timeout = 5000; - read-timeout = 30000; - haproxy-protocol = false; - tcp-fast-open = false; - bungee-plugin-message-channel = true; - show-ping-requests = false; - failover-on-unexpected-server-disconnect = true; - announce-proxy-commands = true; - log-command-executions = false; - log-player-summary-on-shutdown = true; - accepts-transfers = false; - }; - - query = { - enabled = false; - port = 25577; - map = "Velocity"; - show-plugins = false; - }; }; } From 9a3ed83e15dae27d1f864545b7b442baa4ff9805 Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Mon, 25 May 2026 02:07:14 -0400 Subject: [PATCH 10/18] HACK --- nix/oyasai-purpur.nix | 9 +++++++++ packages/oyasai-minecraft-main.nix | 1 + 2 files changed, 10 insertions(+) diff --git a/nix/oyasai-purpur.nix b/nix/oyasai-purpur.nix index 8350d6c99..d229455b4 100644 --- a/nix/oyasai-purpur.nix +++ b/nix/oyasai-purpur.nix @@ -8,6 +8,7 @@ writeTextFile, formats, coreutils, + gnused, purpurServers, }: @@ -36,6 +37,7 @@ let runtimeInputs = [ coreutils + gnused jre ]; @@ -64,6 +66,13 @@ let ${lib.optionalString (paperConfig != null) '' mkdir -p config cp --no-preserve=ownership,mode ${paperGlobalYml} config/paper-global.yml + # FIXME: Paper supports PAPER_VELOCITY_SECRET env var (@PostProcess in GlobalConfiguration.java, + # added June 2022), but it's unclear whether Purpur's build includes this. In practice the env + # var silently has no effect and we fall back to sed-patching the baked YAML at startup. + # Consider switching to upstream Paper if Purpur-specific features are no longer needed. + if [[ -n "''${PAPER_VELOCITY_SECRET:-}" ]]; then + sed -i "s/PAPER_VELOCITY_SECRET_PLACEHOLDER/''${PAPER_VELOCITY_SECRET}/" config/paper-global.yml + fi ''} # Sighs. Doesn't take rcon password as a envvar. diff --git a/packages/oyasai-minecraft-main.nix b/packages/oyasai-minecraft-main.nix index 5f81a1f63..83f2e50d2 100644 --- a/packages/oyasai-minecraft-main.nix +++ b/packages/oyasai-minecraft-main.nix @@ -34,6 +34,7 @@ oyasaiPurpur rec { proxies.velocity = { enabled = true; online-mode = true; + secret = "PAPER_VELOCITY_SECRET_PLACEHOLDER"; }; }; From 51c465197e803f8e2bead9852df032554d5eca02 Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Mon, 25 May 2026 09:32:29 -0400 Subject: [PATCH 11/18] debug --- .github/workflows/modify-platform-stack.yaml | 16 ++++++++++++++++ packages/oyasai-velocity.nix | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/modify-platform-stack.yaml b/.github/workflows/modify-platform-stack.yaml index dea9e604e..74123a7ed 100644 --- a/.github/workflows/modify-platform-stack.yaml +++ b/.github/workflows/modify-platform-stack.yaml @@ -89,6 +89,22 @@ jobs: nix run .#oyasai-infra -- \ terraform_io::delete_workspace \ ${{ inputs.stack }} + - name: Debug containers NOMERGE + if: inputs.start-proxy + working-directory: . + run: | + echo "=== Waiting 45s for minecraft-main to start ===" + sleep 45 + echo "=== docker ps ===" + docker ps -a + echo "=== minecraft-main logs (last 80) ===" + docker logs --tail 80 minecraft-main 2>&1 || echo "(no logs)" + echo "=== velocity logs ===" + docker logs velocity 2>&1 || echo "(no logs)" + echo "=== paper-global.yml ===" + docker cp minecraft-main:/data/config/paper-global.yml - 2>/dev/null | tar -xOf - paper-global.yml || echo "(could not read)" + echo "=== server.properties ===" + docker cp minecraft-main:/data/server.properties - 2>/dev/null | tar -xOf - server.properties | grep -E "online-mode|velocity|rcon" || echo "(could not read)" - name: Start Proxy if: inputs.start-proxy # It's free, feel free to increase diff --git a/packages/oyasai-velocity.nix b/packages/oyasai-velocity.nix index a15939972..f31186587 100644 --- a/packages/oyasai-velocity.nix +++ b/packages/oyasai-velocity.nix @@ -16,8 +16,9 @@ oyasaiVelocity { velocityConfig = { config-version = "2.8"; - motd = "§l§r §b§lOyasai§f§lServer§r\\n§l§f 建築勢は集合だ!建築!建築!建築!!!"; + motd = " OyasaiServer\\n 建築勢は集合だ!建築!建築!建築!!!"; player-info-forwarding-mode = "modern"; + forced-hosts = { }; servers = { main = "${oyasai-minecraft-main.name}:25565"; From f925be51b2161dad408d9634f2cd29e5d42cc7fc Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Mon, 25 May 2026 09:54:07 -0400 Subject: [PATCH 12/18] remove hack --- .github/workflows/modify-platform-stack.yaml | 16 ---------------- nix/oyasai-purpur.nix | 9 --------- packages/oyasai-minecraft-main.nix | 1 - 3 files changed, 26 deletions(-) diff --git a/.github/workflows/modify-platform-stack.yaml b/.github/workflows/modify-platform-stack.yaml index 74123a7ed..dea9e604e 100644 --- a/.github/workflows/modify-platform-stack.yaml +++ b/.github/workflows/modify-platform-stack.yaml @@ -89,22 +89,6 @@ jobs: nix run .#oyasai-infra -- \ terraform_io::delete_workspace \ ${{ inputs.stack }} - - name: Debug containers NOMERGE - if: inputs.start-proxy - working-directory: . - run: | - echo "=== Waiting 45s for minecraft-main to start ===" - sleep 45 - echo "=== docker ps ===" - docker ps -a - echo "=== minecraft-main logs (last 80) ===" - docker logs --tail 80 minecraft-main 2>&1 || echo "(no logs)" - echo "=== velocity logs ===" - docker logs velocity 2>&1 || echo "(no logs)" - echo "=== paper-global.yml ===" - docker cp minecraft-main:/data/config/paper-global.yml - 2>/dev/null | tar -xOf - paper-global.yml || echo "(could not read)" - echo "=== server.properties ===" - docker cp minecraft-main:/data/server.properties - 2>/dev/null | tar -xOf - server.properties | grep -E "online-mode|velocity|rcon" || echo "(could not read)" - name: Start Proxy if: inputs.start-proxy # It's free, feel free to increase diff --git a/nix/oyasai-purpur.nix b/nix/oyasai-purpur.nix index d229455b4..8350d6c99 100644 --- a/nix/oyasai-purpur.nix +++ b/nix/oyasai-purpur.nix @@ -8,7 +8,6 @@ writeTextFile, formats, coreutils, - gnused, purpurServers, }: @@ -37,7 +36,6 @@ let runtimeInputs = [ coreutils - gnused jre ]; @@ -66,13 +64,6 @@ let ${lib.optionalString (paperConfig != null) '' mkdir -p config cp --no-preserve=ownership,mode ${paperGlobalYml} config/paper-global.yml - # FIXME: Paper supports PAPER_VELOCITY_SECRET env var (@PostProcess in GlobalConfiguration.java, - # added June 2022), but it's unclear whether Purpur's build includes this. In practice the env - # var silently has no effect and we fall back to sed-patching the baked YAML at startup. - # Consider switching to upstream Paper if Purpur-specific features are no longer needed. - if [[ -n "''${PAPER_VELOCITY_SECRET:-}" ]]; then - sed -i "s/PAPER_VELOCITY_SECRET_PLACEHOLDER/''${PAPER_VELOCITY_SECRET}/" config/paper-global.yml - fi ''} # Sighs. Doesn't take rcon password as a envvar. diff --git a/packages/oyasai-minecraft-main.nix b/packages/oyasai-minecraft-main.nix index 83f2e50d2..5f81a1f63 100644 --- a/packages/oyasai-minecraft-main.nix +++ b/packages/oyasai-minecraft-main.nix @@ -34,7 +34,6 @@ oyasaiPurpur rec { proxies.velocity = { enabled = true; online-mode = true; - secret = "PAPER_VELOCITY_SECRET_PLACEHOLDER"; }; }; From 5fa20b844a4ef94b9bc2e581038b266b9fa57d55 Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Mon, 25 May 2026 10:03:00 -0400 Subject: [PATCH 13/18] velocity doesnt depend on mc --- packages/oyasai-cdktf/src/stacks/platform-services.ts | 1 - packages/oyasai-velocity.nix | 11 ++++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/oyasai-cdktf/src/stacks/platform-services.ts b/packages/oyasai-cdktf/src/stacks/platform-services.ts index 9e91063dd..e2d015109 100644 --- a/packages/oyasai-cdktf/src/stacks/platform-services.ts +++ b/packages/oyasai-cdktf/src/stacks/platform-services.ts @@ -138,7 +138,6 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { new Container(this, this.t("velocity-container"), { image: images.velocity, name: "velocity", - dependsOn: [minecraftMainContainer], restart: "unless-stopped", networksAdvanced: [network], ports: ports({ diff --git a/packages/oyasai-velocity.nix b/packages/oyasai-velocity.nix index f31186587..d6febb0fc 100644 --- a/packages/oyasai-velocity.nix +++ b/packages/oyasai-velocity.nix @@ -4,6 +4,9 @@ oyasai-minecraft-main, }: +let + defaultServer = "main"; +in oyasaiVelocity { name = "oyasai-velocity"; @@ -16,13 +19,15 @@ oyasaiVelocity { velocityConfig = { config-version = "2.8"; - motd = " OyasaiServer\\n 建築勢は集合だ!建築!建築!建築!!!"; + motd = " OyasaiServer\n 建築勢は集合だ!建築!建築!建築!!!"; player-info-forwarding-mode = "modern"; + + # Velocity tries to set default forced-hosts = { }; servers = { - main = "${oyasai-minecraft-main.name}:25565"; - try = [ "main" ]; + ${defaultServer} = "${oyasai-minecraft-main.name}:25565"; + try = [ defaultServer ]; }; }; } From ebd0d70d7cb014b73fe77e383d2bdb5fe2dd7a95 Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Mon, 25 May 2026 01:46:21 -0400 Subject: [PATCH 14/18] feat: lobby server --- .../src/stacks/platform-services.ts | 27 +++++++++++++++++++ packages/oyasai-minecraft-lobby.nix | 15 ++++++++--- packages/oyasai-velocity.nix | 10 +++---- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/packages/oyasai-cdktf/src/stacks/platform-services.ts b/packages/oyasai-cdktf/src/stacks/platform-services.ts index e2d015109..d37cf3602 100644 --- a/packages/oyasai-cdktf/src/stacks/platform-services.ts +++ b/packages/oyasai-cdktf/src/stacks/platform-services.ts @@ -53,6 +53,7 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { const images = { mariadb: imageIds.mariadb, mysqlBackup: imageIds["mysql-backup"], + minecraftLobby: imageIds["oyasai-minecraft-lobby"], minecraftMain: imageIds["oyasai-minecraft-main"], minecraftBackup: imageIds["mc-backup"], velocity: imageIds["oyasai-velocity"], @@ -135,6 +136,32 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { }, ); + const minecraftLobbyContainer = new Container( + this, + this.t("minecraft-lobby-container"), + { + image: images.minecraftLobby, + name: "oyasai-minecraft-lobby", + dependsOn: [mariadbContainer], + restart: "unless-stopped", + tty: true, + stdinOpen: true, + destroyGraceSeconds: 2 * 60, + init: true, + networksAdvanced: [network], + env: envs({ + MEMORY: "2G", + PAPER_VELOCITY_SECRET: secrets.get("VELOCITY_FORWARDING_SECRET"), + }), + volumes: [ + { + containerPath: "/data", + hostPath: join(this.workdir, "minecraft-lobby"), + }, + ], + }, + ); + new Container(this, this.t("velocity-container"), { image: images.velocity, name: "velocity", diff --git a/packages/oyasai-minecraft-lobby.nix b/packages/oyasai-minecraft-lobby.nix index 0016f6517..73efff011 100644 --- a/packages/oyasai-minecraft-lobby.nix +++ b/packages/oyasai-minecraft-lobby.nix @@ -1,5 +1,9 @@ { oyasaiPurpur, oyasai-plugin-registry }: +let + # Radius of the lobby build in chunks. Tune this as the build grows. + worldRadius = 5; +in oyasaiPurpur rec { name = "oyasai-minecraft-lobby"; version = "1.21.10"; @@ -10,16 +14,20 @@ oyasaiPurpur rec { difficulty = "peaceful"; enable-rcon = true; enforce-secure-profile = false; + force-gamemode = true; + gamemode = "adventure"; + generate-structures = false; + level-type = "flat"; max-players = 70; - network-compression-threshold = 96; online-mode = false; # handled by velocity pvp = false; - simulation-distance = 8; + simulation-distance = worldRadius; spawn-animals = false; spawn-monsters = false; spawn-npcs = false; spawn-protection = 0; - view-distance = 16; + sync-chunk-writes = false; + view-distance = worldRadius + 2; # A bit of visual buffer # keep-sorted end }; @@ -33,6 +41,7 @@ oyasaiPurpur rec { plugins = with oyasai-plugin-registry.forVersion version; [ # keep-sorted start floodgate + skinsrestorer viaversion # keep-sorted end ]; diff --git a/packages/oyasai-velocity.nix b/packages/oyasai-velocity.nix index d6febb0fc..f7d954b41 100644 --- a/packages/oyasai-velocity.nix +++ b/packages/oyasai-velocity.nix @@ -2,11 +2,9 @@ oyasaiVelocity, oyasai-plugin-registry, oyasai-minecraft-main, + oyasai-minecraft-lobby, }: -let - defaultServer = "main"; -in oyasaiVelocity { name = "oyasai-velocity"; @@ -25,9 +23,11 @@ oyasaiVelocity { # Velocity tries to set default forced-hosts = { }; + # TODO: set lobby as default server after experimentation. servers = { - ${defaultServer} = "${oyasai-minecraft-main.name}:25565"; - try = [ defaultServer ]; + main = oyasai-minecraft-main.name; + lobby = oyasai-minecraft-lobby.name; + try = [ "main" ]; }; }; } From 643c117c83b4490b80112d0b4121867873716cb6 Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Mon, 25 May 2026 10:41:31 -0400 Subject: [PATCH 15/18] backup --- .../src/stacks/platform-services.ts | 123 ++++++++++-------- 1 file changed, 68 insertions(+), 55 deletions(-) diff --git a/packages/oyasai-cdktf/src/stacks/platform-services.ts b/packages/oyasai-cdktf/src/stacks/platform-services.ts index d37cf3602..1b32466ba 100644 --- a/packages/oyasai-cdktf/src/stacks/platform-services.ts +++ b/packages/oyasai-cdktf/src/stacks/platform-services.ts @@ -17,8 +17,6 @@ type Props = Readonly<{ }>; export class PlatformServices extends OyasaiPlatformTerraformStack { - private readonly workdir = join("/opt/platform", this.environment); - constructor( scope: Construct, id: string, @@ -63,6 +61,16 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { name: "network", }); + const hostPathRoot = join("/opt/platform", this.environment); + + // Define all host paths here to avoid crash. + const hostPaths = { + main: join(hostPathRoot, "minecraft-main"), + lobby: join(hostPathRoot, "lobby"), + velocity: join(hostPathRoot, "velocity"), + mariadb: join(hostPathRoot, "mariadb"), + }; + const mariadbContainer = new Container(this, this.t("mariadb-container"), { image: images.mariadb, name: "mariadb", @@ -74,19 +82,15 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { volumes: [ { containerPath: "/var/lib/mysql", - hostPath: join(this.workdir, "mariadb"), + hostPath: hostPaths.mariadb, }, { containerPath: "/docker-entrypoint-initdb.d", - hostPath: join(this.workdir, "mariadb"), + hostPath: hostPaths.mariadb, }, ], }); - // Can change to "oyasai-minecraft-main" for naming consistency but too lazy - // to do the data migration - shun 2026-05 - const minecraftMainWorkDir = join(this.workdir, "minecraft-main"); - const minecraftMainContainer = new Container( this, this.t("minecraft-main-container"), @@ -125,7 +129,7 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { volumes: [ { containerPath: "/data", - hostPath: minecraftMainWorkDir, + hostPath: hostPaths.main, }, ], ...(this.isMaster && { @@ -156,7 +160,7 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { volumes: [ { containerPath: "/data", - hostPath: join(this.workdir, "minecraft-lobby"), + hostPath: hostPaths.lobby, }, ], }, @@ -178,7 +182,7 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { volumes: [ { containerPath: "/data", - hostPath: join(this.workdir, "velocity"), + hostPath: hostPaths.velocity, }, ], }); @@ -186,51 +190,60 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { if (this.isMaster) { const cloudflareBaseUrl = `https://${secrets.get("CLOUDFLARE_ACCOUNT_ID")}.r2.cloudflarestorage.com`; - new Container(this, this.t("minecraft-backup-container"), { - name: "minecraft-main-backup", - dependsOn: [minecraftMainContainer], - image: images.minecraftBackup, - networksAdvanced: [network], - restart: "unless-stopped", - env: envs({ - // keep-sorted start block=yes - AWS_ACCESS_KEY_ID: secrets.get("CLOUDFLARE_ACCESS_KEY_ID"), - AWS_SECRET_ACCESS_KEY: secrets.get("CLOUDFLARE_SECRET_ACCESS_KEY"), - BACKUP_INTERVAL: "6h", - BACKUP_METHOD: "restic", - BACKUP_NAME: minecraftMainContainer.name, - EXCLUDES: [ - // keep-sorted start - "*.hprof", // Spark profiles - they are huge. - "*.jar", - "*.tmp", - "archive", - "bluemap", - "cache", - "crash-reports", - "debug", - "logs", - "versions", + // Backup sidecar containers for Minecraft containers + for (const { container, hostPath } of [ + { container: minecraftMainContainer, hostPath: hostPaths.main }, + { + container: minecraftLobbyContainer, + hostPath: hostPaths.lobby, + }, + ]) { + new Container(this, this.t(`${container.name}-backup-container`), { + name: `${container.name}-backup`, + dependsOn: [container], + image: images.minecraftBackup, + networksAdvanced: [network], + restart: "unless-stopped", + env: envs({ + // keep-sorted start block=yes + AWS_ACCESS_KEY_ID: secrets.get("CLOUDFLARE_ACCESS_KEY_ID"), + AWS_SECRET_ACCESS_KEY: secrets.get("CLOUDFLARE_SECRET_ACCESS_KEY"), + BACKUP_INTERVAL: "6h", + BACKUP_METHOD: "restic", + BACKUP_NAME: container.name, + EXCLUDES: [ + // keep-sorted start + "*.hprof", // Spark profiles - they are huge. + "*.jar", + "*.tmp", + "archive", + "bluemap", + "cache", + "crash-reports", + "debug", + "logs", + "versions", + // keep-sorted end + ].join(","), + PRUNE_RESTIC_RETENTION: + "--keep-daily 7 --keep-weekly 4 --keep-monthly 3", + RCON_HOST: container.name, + RCON_PASSWORD: secrets.get("RCON_PASSWORD"), + RESTIC_ADDITIONAL_TAGS: "", // Set to an empty string to disable additional tags. + RESTIC_PASSWORD: secrets.get("RESTIC_PASSWORD"), + RESTIC_REPOSITORY: `s3:${cloudflareBaseUrl}/${r2Bucket.name}/${container.name}-backup`, + RESTIC_VERBOSE: true, // keep-sorted end - ].join(","), - PRUNE_RESTIC_RETENTION: - "--keep-daily 7 --keep-weekly 4 --keep-monthly 3", - RCON_HOST: minecraftMainContainer.name, - RCON_PASSWORD: secrets.get("RCON_PASSWORD"), - RESTIC_ADDITIONAL_TAGS: "", // Set to an empty string to disable additional tags. - RESTIC_PASSWORD: secrets.get("RESTIC_PASSWORD"), - RESTIC_REPOSITORY: `s3:${cloudflareBaseUrl}/${r2Bucket.name}/minecraft-main-backup`, - RESTIC_VERBOSE: true, - // keep-sorted end - }), - volumes: [ - { - hostPath: minecraftMainWorkDir, - containerPath: "/data", - readOnly: true, - }, - ], - }); + }), + volumes: [ + { + hostPath, + containerPath: "/data", + readOnly: true, + }, + ], + }); + } new Container(this, this.t("mariadb-backup-container"), { name: "mariadb-backup", From 3eae6500f16d321843cd6a83cfb920a46823fa9d Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Mon, 25 May 2026 11:26:44 -0400 Subject: [PATCH 16/18] feat: metrics --- nix/oyasai-purpur.nix | 6 +- packages/oyasai-alloy.nix | 60 ++++++ .../src/stacks/platform-services.ts | 176 ++++++++++++++---- packages/oyasai-grafana.nix | 66 +++++++ packages/oyasai-loki.nix | 53 ++++++ packages/oyasai-mc-monitor-exporter.nix | 29 +++ packages/oyasai-minecraft-lobby.nix | 15 +- packages/oyasai-plugin-registry/lock.json | 26 ++- packages/oyasai-prometheus.nix | 39 ++++ packages/oyasai-velocity.nix | 2 + 10 files changed, 410 insertions(+), 62 deletions(-) create mode 100644 packages/oyasai-alloy.nix create mode 100644 packages/oyasai-grafana.nix create mode 100644 packages/oyasai-loki.nix create mode 100644 packages/oyasai-mc-monitor-exporter.nix create mode 100644 packages/oyasai-prometheus.nix diff --git a/nix/oyasai-purpur.nix b/nix/oyasai-purpur.nix index 8350d6c99..7ca48de88 100644 --- a/nix/oyasai-purpur.nix +++ b/nix/oyasai-purpur.nix @@ -76,16 +76,16 @@ let MEMORY="''${MEMORY:-2G}" + # Mostly taken from: https://exa.y2k.diy/garden/jvm-args/ exec java \ -Xmx"''${MEMORY}" \ -Xms"''${MEMORY}" \ -XX:+UseZGC \ - -XX:+AlwaysPreTouch \ - -XX:+DisableExplicitGC \ - -XX:+PerfDisableSharedMem \ + -XX:+UseCompactObjectHeaders \ -XX:-OmitStackTraceInFastThrow \ -Dfile.encoding=UTF-8 \ -Dcom.mojang.eula.agree=true \ + -Dpaper.disableStartupVersionCheck \ -jar "${package}/lib/minecraft/server.jar" \ nogui \ "$@" diff --git a/packages/oyasai-alloy.nix b/packages/oyasai-alloy.nix new file mode 100644 index 000000000..f55105d6f --- /dev/null +++ b/packages/oyasai-alloy.nix @@ -0,0 +1,60 @@ +{ + lib, + grafana-alloy, + oyasaiDockerTools, + stdenv, + writeTextFile, +}: + +let + alloyConfig = writeTextFile { + name = "config.alloy"; + text = '' + // Discover all Docker containers via the Docker socket. + discovery.docker "all" { + host = "unix:///var/run/docker.sock" + } + + // Relabel: expose container name and log stream as Loki labels. + discovery.relabel "containers" { + targets = discovery.docker.all.targets + + rule { + source_labels = ["__meta_docker_container_name"] + regex = "/(.*)" + target_label = "container" + } + + rule { + source_labels = ["__meta_docker_container_log_stream"] + target_label = "stream" + } + } + + // Tail logs from Docker containers and forward to Loki. + loki.source.docker "all" { + host = "unix:///var/run/docker.sock" + targets = discovery.relabel.containers.output + forward_to = [loki.write.default.receiver] + } + + loki.write "default" { + endpoint { + url = "http://loki:3100/loki/api/v1/push" + } + } + ''; + }; +in +lib.optionalAttrs stdenv.hostPlatform.isLinux { + docker = oyasaiDockerTools.buildLayeredImage { + name = "oyasai-alloy"; + config = { + Cmd = [ + "${grafana-alloy}/bin/alloy" + "run" + "${alloyConfig}" + ]; + }; + }; +} diff --git a/packages/oyasai-cdktf/src/stacks/platform-services.ts b/packages/oyasai-cdktf/src/stacks/platform-services.ts index 1b32466ba..c8dbb4a04 100644 --- a/packages/oyasai-cdktf/src/stacks/platform-services.ts +++ b/packages/oyasai-cdktf/src/stacks/platform-services.ts @@ -49,28 +49,40 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { const imageIds = JSON.parse(process.env.OYASAI_IMAGE_ID as string); const images = { + // keep-sorted start + alloy: imageIds["oyasai-alloy"], + grafana: imageIds["oyasai-grafana"], + loki: imageIds["oyasai-loki"], mariadb: imageIds.mariadb, - mysqlBackup: imageIds["mysql-backup"], + mcMonitorExporter: imageIds["oyasai-mc-monitor-exporter"], + minecraftAxiom: imageIds["oyasai-minecraft-axiom"], + minecraftBackup: imageIds["mc-backup"], minecraftLobby: imageIds["oyasai-minecraft-lobby"], minecraftMain: imageIds["oyasai-minecraft-main"], - minecraftBackup: imageIds["mc-backup"], + mysqlBackup: imageIds["mysql-backup"], + prometheus: imageIds["oyasai-prometheus"], velocity: imageIds["oyasai-velocity"], + // keep-sorted end + } as const; + + const baseHostPath = join("/opt/platform", this.environment); + const hostPaths = { + // keep-sorted start + grafana: join(baseHostPath, "grafana"), + loki: join(baseHostPath, "loki"), + mariadb: join(baseHostPath, "mariadb"), + minecraftAxiom: join(baseHostPath, "minecraft-axiom"), + minecraftLobby: join(baseHostPath, "minecraft-lobby"), + minecraftMain: join(baseHostPath, "minecraft-main"), + prometheus: join(baseHostPath, "prometheus"), + velocity: join(baseHostPath, "velocity"), + // keep-sorted end } as const; const network = new Network(this, this.t("network"), { name: "network", }); - const hostPathRoot = join("/opt/platform", this.environment); - - // Define all host paths here to avoid crash. - const hostPaths = { - main: join(hostPathRoot, "minecraft-main"), - lobby: join(hostPathRoot, "lobby"), - velocity: join(hostPathRoot, "velocity"), - mariadb: join(hostPathRoot, "mariadb"), - }; - const mariadbContainer = new Container(this, this.t("mariadb-container"), { image: images.mariadb, name: "mariadb", @@ -113,11 +125,9 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { }), env: envs({ MEMORY: this.isMaster - ? // On-prem has 64GB but looks like 28GB is the most stable because - // JVM GC overhead. No calculation, based on experiments. - "28G" - : // GitHub Action runners have 16GB, but also runs other containers - // so limiting to 10GB. + ? // On-prem has 64GB + "20G" + : // GitHub Action runners have 16GB "10G", RCON_PASSWORD: secrets.get("RCON_PASSWORD"), @@ -129,7 +139,7 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { volumes: [ { containerPath: "/data", - hostPath: hostPaths.main, + hostPath: hostPaths.minecraftMain, }, ], ...(this.isMaster && { @@ -160,7 +170,33 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { volumes: [ { containerPath: "/data", - hostPath: hostPaths.lobby, + hostPath: hostPaths.minecraftLobby, + }, + ], + }, + ); + + const minecraftAxiomContainer = new Container( + this, + this.t("minecraft-axiom-container"), + { + image: images.minecraftAxiom, + name: "oyasai-minecraft-axiom", + dependsOn: [mariadbContainer], + restart: "unless-stopped", + tty: true, + stdinOpen: true, + destroyGraceSeconds: 2 * 60, + init: true, + networksAdvanced: [network], + env: envs({ + MEMORY: "8G", + PAPER_VELOCITY_SECRET: secrets.get("VELOCITY_FORWARDING_SECRET"), + }), + volumes: [ + { + containerPath: "/data", + hostPath: hostPaths.minecraftAxiom, }, ], }, @@ -190,17 +226,17 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { if (this.isMaster) { const cloudflareBaseUrl = `https://${secrets.get("CLOUDFLARE_ACCOUNT_ID")}.r2.cloudflarestorage.com`; - // Backup sidecar containers for Minecraft containers - for (const { container, hostPath } of [ - { container: minecraftMainContainer, hostPath: hostPaths.main }, - { - container: minecraftLobbyContainer, - hostPath: hostPaths.lobby, - }, - ]) { - new Container(this, this.t(`${container.name}-backup-container`), { - name: `${container.name}-backup`, - dependsOn: [container], + const backedupMinecraftContainers = { + ["minecraft-main"]: minecraftMainContainer, + ["minecraft-axiom"]: minecraftAxiomContainer, + } as const; + + for (const [backupName, minecraftContainer] of Object.entries( + backedupMinecraftContainers, + )) { + new Container(this, this.t(`${backupName}-backup-container`), { + name: `${backupName}-backup`, + dependsOn: [minecraftContainer], image: images.minecraftBackup, networksAdvanced: [network], restart: "unless-stopped", @@ -210,7 +246,7 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { AWS_SECRET_ACCESS_KEY: secrets.get("CLOUDFLARE_SECRET_ACCESS_KEY"), BACKUP_INTERVAL: "6h", BACKUP_METHOD: "restic", - BACKUP_NAME: container.name, + BACKUP_NAME: backupName, EXCLUDES: [ // keep-sorted start "*.hprof", // Spark profiles - they are huge. @@ -227,21 +263,15 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { ].join(","), PRUNE_RESTIC_RETENTION: "--keep-daily 7 --keep-weekly 4 --keep-monthly 3", - RCON_HOST: container.name, + RCON_HOST: minecraftContainer.name, RCON_PASSWORD: secrets.get("RCON_PASSWORD"), RESTIC_ADDITIONAL_TAGS: "", // Set to an empty string to disable additional tags. RESTIC_PASSWORD: secrets.get("RESTIC_PASSWORD"), - RESTIC_REPOSITORY: `s3:${cloudflareBaseUrl}/${r2Bucket.name}/${container.name}-backup`, + RESTIC_REPOSITORY: `s3:${cloudflareBaseUrl}/${r2Bucket.name}/${backupName}-backup`, RESTIC_VERBOSE: true, // keep-sorted end }), - volumes: [ - { - hostPath, - containerPath: "/data", - readOnly: true, - }, - ], + volumes: minecraftContainer.volumesInput, }); } @@ -268,5 +298,71 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { }), }); } + + new Container(this, this.t("mc-monitor-exporter-container"), { + image: images.mcMonitorExporter, + name: "mc-monitor-exporter", + restart: "unless-stopped", + networksAdvanced: [network], + env: envs({ + EXPORT_SERVERS: [ + `${minecraftMainContainer.name}:25565`, + `${minecraftLobbyContainer.name}:25565`, + ].join(","), + }), + }); + + new Container(this, this.t("loki-container"), { + image: images.loki, + name: "loki", + restart: "unless-stopped", + networksAdvanced: [network], + volumes: [ + { + containerPath: "/data", + hostPath: hostPaths.loki, + }, + ], + }); + + new Container(this, this.t("alloy-container"), { + image: images.alloy, + name: "alloy", + restart: "unless-stopped", + networksAdvanced: [network], + volumes: [ + { + containerPath: "/var/run/docker.sock", + hostPath: "/var/run/docker.sock", + }, + ], + }); + + new Container(this, this.t("prometheus-container"), { + image: images.prometheus, + name: "prometheus", + restart: "unless-stopped", + networksAdvanced: [network], + volumes: [ + { + containerPath: "/data", + hostPath: hostPaths.prometheus, + }, + ], + }); + + new Container(this, this.t("grafana-container"), { + image: images.grafana, + name: "grafana", + restart: "unless-stopped", + networksAdvanced: [network], + ports: ports({ tcp: [3000] }), + volumes: [ + { + containerPath: "/data", + hostPath: hostPaths.grafana, + }, + ], + }); } } diff --git a/packages/oyasai-grafana.nix b/packages/oyasai-grafana.nix new file mode 100644 index 000000000..a5656e575 --- /dev/null +++ b/packages/oyasai-grafana.nix @@ -0,0 +1,66 @@ +{ + lib, + grafana, + oyasaiDockerTools, + stdenv, + formats, + runCommandLocal, +}: + +let + datasourceYml = (formats.yaml { }).generate "prometheus.yaml" { + apiVersion = 1; + datasources = [ + { + name = "Prometheus"; + type = "prometheus"; + url = "http://prometheus:9090"; + isDefault = true; + access = "proxy"; + } + ]; + }; + + lokiYml = (formats.yaml { }).generate "loki.yaml" { + apiVersion = 1; + datasources = [ + { + name = "Loki"; + type = "loki"; + url = "http://loki:3100"; + access = "proxy"; + } + ]; + }; + + # Bake data sources into the image via provisioning directory. + # Grafana reads this on startup and auto-configures them. + provisioningDir = runCommandLocal "grafana-provisioning" { } '' + mkdir -p $out/datasources + cp ${datasourceYml} $out/datasources/prometheus.yaml + cp ${lokiYml} $out/datasources/loki.yaml + ''; +in +lib.optionalAttrs stdenv.hostPlatform.isLinux { + docker = oyasaiDockerTools.buildLayeredImage { + name = "oyasai-grafana"; + config = { + Cmd = [ + "${grafana}/bin/grafana" + "server" + "--homepath=${grafana}/share/grafana" + ]; + Env = [ + "GF_PATHS_HOME=${grafana}/share/grafana" + "GF_PATHS_PROVISIONING=${provisioningDir}" + "GF_PATHS_DATA=/data" + "GF_PATHS_LOGS=/data/log" + "GF_PATHS_PLUGINS=/data/plugins" + "GF_SERVER_HTTP_PORT=3000" + ]; + ExposedPorts = { + "3000/tcp" = { }; + }; + }; + }; +} diff --git a/packages/oyasai-loki.nix b/packages/oyasai-loki.nix new file mode 100644 index 000000000..b545b952d --- /dev/null +++ b/packages/oyasai-loki.nix @@ -0,0 +1,53 @@ +{ + lib, + grafana-loki, + oyasaiDockerTools, + stdenv, + formats, +}: + +let + lokiYml = (formats.yaml { }).generate "loki.yml" { + auth_enabled = false; + + server.http_listen_port = 3100; + + common = { + instance_addr = "127.0.0.1"; + path_prefix = "/data"; + storage.filesystem = { + chunks_directory = "/data/chunks"; + rules_directory = "/data/rules"; + }; + replication_factor = 1; + ring.kvstore.store = "inmemory"; + }; + + schema_config.configs = [ + { + from = "2020-10-24"; + store = "tsdb"; + object_store = "filesystem"; + schema = "v13"; + index = { + prefix = "index_"; + period = "24h"; + }; + } + ]; + }; +in +lib.optionalAttrs stdenv.hostPlatform.isLinux { + docker = oyasaiDockerTools.buildLayeredImage { + name = "oyasai-loki"; + config = { + Cmd = [ + "${grafana-loki}/bin/loki" + "-config.file=${lokiYml}" + ]; + ExposedPorts = { + "3100/tcp" = { }; + }; + }; + }; +} diff --git a/packages/oyasai-mc-monitor-exporter.nix b/packages/oyasai-mc-monitor-exporter.nix new file mode 100644 index 000000000..585dfbb8c --- /dev/null +++ b/packages/oyasai-mc-monitor-exporter.nix @@ -0,0 +1,29 @@ +{ + lib, + mc-monitor, + oyasaiDockerTools, + stdenv, + writeShellApplication, +}: + +let + result = writeShellApplication { + name = "oyasai-mc-monitor-exporter"; + runtimeInputs = [ mc-monitor ]; + text = '' + exec mc-monitor export-for-prometheus "$@" + ''; + passthru = lib.optionalAttrs stdenv.hostPlatform.isLinux { + docker = oyasaiDockerTools.buildLayeredImage { + name = "oyasai-mc-monitor-exporter"; + config = { + Cmd = [ (lib.getExe result) ]; + ExposedPorts = { + "8080/tcp" = { }; + }; + }; + }; + }; + }; +in +result diff --git a/packages/oyasai-minecraft-lobby.nix b/packages/oyasai-minecraft-lobby.nix index 73efff011..0016f6517 100644 --- a/packages/oyasai-minecraft-lobby.nix +++ b/packages/oyasai-minecraft-lobby.nix @@ -1,9 +1,5 @@ { oyasaiPurpur, oyasai-plugin-registry }: -let - # Radius of the lobby build in chunks. Tune this as the build grows. - worldRadius = 5; -in oyasaiPurpur rec { name = "oyasai-minecraft-lobby"; version = "1.21.10"; @@ -14,20 +10,16 @@ oyasaiPurpur rec { difficulty = "peaceful"; enable-rcon = true; enforce-secure-profile = false; - force-gamemode = true; - gamemode = "adventure"; - generate-structures = false; - level-type = "flat"; max-players = 70; + network-compression-threshold = 96; online-mode = false; # handled by velocity pvp = false; - simulation-distance = worldRadius; + simulation-distance = 8; spawn-animals = false; spawn-monsters = false; spawn-npcs = false; spawn-protection = 0; - sync-chunk-writes = false; - view-distance = worldRadius + 2; # A bit of visual buffer + view-distance = 16; # keep-sorted end }; @@ -41,7 +33,6 @@ oyasaiPurpur rec { plugins = with oyasai-plugin-registry.forVersion version; [ # keep-sorted start floodgate - skinsrestorer viaversion # keep-sorted end ]; diff --git a/packages/oyasai-plugin-registry/lock.json b/packages/oyasai-plugin-registry/lock.json index cc323e6bc..e911d03d1 100644 --- a/packages/oyasai-plugin-registry/lock.json +++ b/packages/oyasai-plugin-registry/lock.json @@ -5,6 +5,12 @@ "hash": "sha256-e0llrauKgRDij8wUYDvnFIJzwuVDAU2RXd9EpjkDHdg=" } }, + "axiom-paper-plugin": { + "1.21.10": { + "url": "https://cdn.modrinth.com/data/evkiwA7V/versions/KIGJ1Vhv/AxiomPaperPlugin-5.0.4-for-MC1.21.10.jar", + "hash": "sha256-zbgssJUQ6j4UGQwQOzNer2sOxAK8pn5QOpm0KLnyBUg=" + } + }, "bkcommonlib": { "1.21.10": { "url": "https://cdn.modrinth.com/data/rTg6ckWb/versions/yZdQcJiN/BKCommonLib-1.21.11-v1-1957.jar", @@ -59,6 +65,12 @@ "hash": "sha256-TVCKrAe29k8Fh/xIH+FeXQoWc89iiv69up3KF7L75Y4=" } }, + "fastasyncvoxelsniper": { + "1.21.10": { + "url": "https://cdn.modrinth.com/data/D7XBSI1y/versions/n77tXMjA/fastasyncvoxelsniper-3.2.4.jar", + "hash": "sha256-k1qgEacp/UDLh/0+Nxwg3UWBMDVH1PksMcvGCOfl7LM=" + } + }, "fastasyncworldedit": { "1.21.10": { "url": "https://cdn.modrinth.com/data/z4HZZnLr/versions/Dx0x0kQW/FastAsyncWorldEdit-Bukkit-2.15.1.jar", @@ -79,8 +91,8 @@ }, "geyser-velocity": { "velocity": { - "url": "https://download.geysermc.org/v2/projects/geyser/versions/2.10.0/builds/1154/downloads/velocity", - "hash": "sha256-/SF/dBIrDNTTo37DSoA0IymfTuxR3d57kmiUZbWUUd0=" + "url": "https://download.geysermc.org/v2/projects/geyser/versions/2.10.0/builds/1155/downloads/velocity", + "hash": "sha256-KS6bO7KbBYmesCYhdaqZkgixe6fSNBO2IP0igJfyqnE=" } }, "gsit": { @@ -109,8 +121,8 @@ }, "luckperms": { "1.21.10": { - "url": "https://cdn.modrinth.com/data/Vebnzrzj/versions/OrIs0S6b/LuckPerms-Bukkit-5.5.17.jar", - "hash": "sha256-1bFgo5cag3LMWDW81VXjfBqmHp3TBVmSGl9CGhG/l90=" + "url": "https://cdn.modrinth.com/data/Vebnzrzj/versions/MBSY8toc/LuckPerms-Bukkit-5.5.53.jar", + "hash": "sha256-/I1OzL8RwehEr0Un8Bi7/ekMGGapq6G/iAFzqOZEzVk=" } }, "lunachat": { @@ -137,7 +149,7 @@ "hash": "sha256-qfvQIMRGNf6ZvaXP6ARjPVQwDlsAz5B2ja/+feDSywA=" } }, - "multiverseprotals": { + "multiverseportals": { "1.21.10": { "url": "https://cdn.modrinth.com/data/8VMk6P0I/versions/BguzNJ3r/multiverse-portals-5.2.2.jar", "hash": "sha256-RZa2a5rKhidf7lu9Jn7VkhpzCThic7GnP5olJJdHqA4=" @@ -253,8 +265,8 @@ }, "viaversion": { "1.21.10": { - "url": "https://cdn.modrinth.com/data/P1OZGk5p/versions/cXLtSQvv/ViaVersion-5.9.2-SNAPSHOT.jar", - "hash": "sha256-AkKV2g8JC2HRtosc4IKJF9jShhQz3yHO1Q9hh1JK5Ts=" + "url": "https://cdn.modrinth.com/data/P1OZGk5p/versions/lrrqDnmv/ViaVersion-5.9.2-SNAPSHOT.jar", + "hash": "sha256-7hV4UNV5Qm9FDVU9Y7spSUED4eURP19bZrgPEBiZI+M=" } }, "worldborder": { diff --git a/packages/oyasai-prometheus.nix b/packages/oyasai-prometheus.nix new file mode 100644 index 000000000..c3fe8382c --- /dev/null +++ b/packages/oyasai-prometheus.nix @@ -0,0 +1,39 @@ +{ + lib, + prometheus, + oyasaiDockerTools, + stdenv, + formats, +}: + +let + prometheusYml = (formats.yaml { }).generate "prometheus.yml" { + global = { + scrape_interval = "15s"; + evaluation_interval = "15s"; + }; + scrape_configs = [ + { + job_name = "minecraft"; + static_configs = [ { targets = [ "mc-monitor-exporter:8080" ]; } ]; + } + ]; + }; +in +lib.optionalAttrs stdenv.hostPlatform.isLinux { + docker = oyasaiDockerTools.buildLayeredImage { + name = "oyasai-prometheus"; + config = { + Cmd = [ + "${prometheus}/bin/prometheus" + "--config.file=${prometheusYml}" + "--storage.tsdb.path=/data" + "--web.console.libraries=${prometheus}/share/prometheus/console_libraries" + "--web.console.templates=${prometheus}/share/prometheus/consoles" + ]; + ExposedPorts = { + "9090/tcp" = { }; + }; + }; + }; +} diff --git a/packages/oyasai-velocity.nix b/packages/oyasai-velocity.nix index f7d954b41..99bce5a42 100644 --- a/packages/oyasai-velocity.nix +++ b/packages/oyasai-velocity.nix @@ -3,6 +3,7 @@ oyasai-plugin-registry, oyasai-minecraft-main, oyasai-minecraft-lobby, + oyasai-minecraft-axiom, }: oyasaiVelocity { @@ -27,6 +28,7 @@ oyasaiVelocity { servers = { main = oyasai-minecraft-main.name; lobby = oyasai-minecraft-lobby.name; + axiom = oyasai-minecraft-axiom.name; try = [ "main" ]; }; }; From 372966c56758b5485870c6c8e2db71ae979448d4 Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Thu, 28 May 2026 00:50:54 -0400 Subject: [PATCH 17/18] dashboard --- .../oyasai-grafana-dashboard.json | 462 ++++++++++++++++++ .../package.nix} | 24 +- 2 files changed, 484 insertions(+), 2 deletions(-) create mode 100644 packages/oyasai-grafana/oyasai-grafana-dashboard.json rename packages/{oyasai-grafana.nix => oyasai-grafana/package.nix} (68%) diff --git a/packages/oyasai-grafana/oyasai-grafana-dashboard.json b/packages/oyasai-grafana/oyasai-grafana-dashboard.json new file mode 100644 index 000000000..252a0478c --- /dev/null +++ b/packages/oyasai-grafana/oyasai-grafana-dashboard.json @@ -0,0 +1,462 @@ +{ + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "index": 1, + "text": "Offline" + }, + "1": { + "color": "green", + "index": 0, + "text": "Online" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "expr": "mc_status_healthy{host=\"oyasai-minecraft-main\"}", + "instant": true, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Main Server", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "index": 1, + "text": "Offline" + }, + "1": { + "color": "green", + "index": 0, + "text": "Online" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "expr": "mc_status_healthy{host=\"oyasai-minecraft-lobby\"}", + "instant": true, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Lobby Server", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "expr": "mc_status_players_online{host=\"oyasai-minecraft-main\"}", + "instant": true, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Main Players Online", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "expr": "mc_status_players_online{host=\"oyasai-minecraft-lobby\"}", + "instant": true, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Lobby Players Online", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 4 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "expr": "mc_status_players_online{host=\"oyasai-minecraft-main\"}", + "legendFormat": "Main", + "refId": "A" + }, + { + "expr": "mc_status_players_online{host=\"oyasai-minecraft-lobby\"}", + "legendFormat": "Lobby", + "refId": "B" + } + ], + "title": "Players Online", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 12 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "expr": "mc_status_response_time_seconds{host=\"oyasai-minecraft-main\"}", + "legendFormat": "Main", + "refId": "A" + }, + { + "expr": "mc_status_response_time_seconds{host=\"oyasai-minecraft-lobby\"}", + "legendFormat": "Lobby", + "refId": "B" + } + ], + "title": "Server Response Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "loki", + "uid": "loki" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 12 + }, + "id": 7, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "expr": "{container=~\"oyasai-minecraft.*\"}", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Minecraft Logs", + "type": "logs" + } + ], + "refresh": "30s", + "schemaVersion": 39, + "tags": ["minecraft"], + "time": { + "from": "now-1h", + "to": "now" + }, + "timezone": "browser", + "title": "Oyasai Minecraft", + "uid": "oyasai-minecraft", + "version": 1 +} diff --git a/packages/oyasai-grafana.nix b/packages/oyasai-grafana/package.nix similarity index 68% rename from packages/oyasai-grafana.nix rename to packages/oyasai-grafana/package.nix index a5656e575..6201d7e6a 100644 --- a/packages/oyasai-grafana.nix +++ b/packages/oyasai-grafana/package.nix @@ -13,6 +13,7 @@ let datasources = [ { name = "Prometheus"; + uid = "prometheus"; type = "prometheus"; url = "http://prometheus:9090"; isDefault = true; @@ -26,6 +27,7 @@ let datasources = [ { name = "Loki"; + uid = "loki"; type = "loki"; url = "http://loki:3100"; access = "proxy"; @@ -33,12 +35,30 @@ let ]; }; - # Bake data sources into the image via provisioning directory. + dashboardsDir = runCommandLocal "grafana-dashboards" { } '' + mkdir -p $out + cp ${./oyasai-grafana-dashboard.json} $out/minecraft.json + ''; + + dashboardsProvisionerYml = (formats.yaml { }).generate "dashboards.yaml" { + apiVersion = 1; + providers = [ + { + name = "oyasai"; + type = "file"; + disableDeletion = true; + options.path = "${dashboardsDir}"; + } + ]; + }; + + # Bake data sources and dashboards into the image via provisioning directory. # Grafana reads this on startup and auto-configures them. provisioningDir = runCommandLocal "grafana-provisioning" { } '' - mkdir -p $out/datasources + mkdir -p $out/datasources $out/dashboards cp ${datasourceYml} $out/datasources/prometheus.yaml cp ${lokiYml} $out/datasources/loki.yaml + cp ${dashboardsProvisionerYml} $out/dashboards/oyasai.yaml ''; in lib.optionalAttrs stdenv.hostPlatform.isLinux { From d02bebd70a4473bb2f63eeba2254e9edcd457f78 Mon Sep 17 00:00:00 2001 From: Shun Ueda Date: Thu, 28 May 2026 10:46:36 -0400 Subject: [PATCH 18/18] no ui --- packages/oyasai-cdktf/src/stacks/platform-services.ts | 7 ------- packages/oyasai-grafana/package.nix | 5 +++++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/oyasai-cdktf/src/stacks/platform-services.ts b/packages/oyasai-cdktf/src/stacks/platform-services.ts index c8dbb4a04..496d1ebaf 100644 --- a/packages/oyasai-cdktf/src/stacks/platform-services.ts +++ b/packages/oyasai-cdktf/src/stacks/platform-services.ts @@ -68,7 +68,6 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { const baseHostPath = join("/opt/platform", this.environment); const hostPaths = { // keep-sorted start - grafana: join(baseHostPath, "grafana"), loki: join(baseHostPath, "loki"), mariadb: join(baseHostPath, "mariadb"), minecraftAxiom: join(baseHostPath, "minecraft-axiom"), @@ -357,12 +356,6 @@ export class PlatformServices extends OyasaiPlatformTerraformStack { restart: "unless-stopped", networksAdvanced: [network], ports: ports({ tcp: [3000] }), - volumes: [ - { - containerPath: "/data", - hostPath: hostPaths.grafana, - }, - ], }); } } diff --git a/packages/oyasai-grafana/package.nix b/packages/oyasai-grafana/package.nix index 6201d7e6a..4d0319bc1 100644 --- a/packages/oyasai-grafana/package.nix +++ b/packages/oyasai-grafana/package.nix @@ -47,6 +47,7 @@ let name = "oyasai"; type = "file"; disableDeletion = true; + allowUiUpdates = false; options.path = "${dashboardsDir}"; } ]; @@ -77,6 +78,10 @@ lib.optionalAttrs stdenv.hostPlatform.isLinux { "GF_PATHS_LOGS=/data/log" "GF_PATHS_PLUGINS=/data/plugins" "GF_SERVER_HTTP_PORT=3000" + "GF_AUTH_ANONYMOUS_ENABLED=true" + "GF_AUTH_ANONYMOUS_ORG_ROLE=Admin" + "GF_AUTH_DISABLE_LOGIN_FORM=true" + "GF_USERS_ALLOW_SIGN_UP=false" ]; ExposedPorts = { "3000/tcp" = { };