Skip to content

Latest commit

ย 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

do-games - Minecraft network (Velocity + backends) on k3s

Rust Ratatui License: MIT Issues PRs Welcome GitHub Stars

Self-hosted Minecraft network on a k3s cluster (MetalLB + Traefik + cert-manager already in place), with a Velocity proxy in front and backend servers (Forge, Paper, Fabric, or Vanilla) behind it. Everything is versioned in this repo: the effective configuration (secrets included) can be recreated on a new machine without retyping any manual command.

All management (create/edit/delete a server, deploy, view pod status and logs) is done through a Rust TUI - see Using the TUI below. That's the recommended way to interact with this repo.

Architecture

Internet โ”€โ”€โ–ถ LoadBalancer Service (MetalLB, port 25565)
                        โ”‚
                        โ–ผ
                 Velocity pod (proxy)
                        โ”‚  "modern" forwarding + shared secret
                        โ–ผ
        ClusterIP Service <server>:25565  (one per backend server)
                        โ”‚
                        โ–ผ
                 Backend pod (Forge/Paper/Fabric/Vanilla)
  • Velocity (itzg/mc-proxy): config mounted from a ConfigMap (velocity.toml), forwarding secret mounted from a k8s Secret, exposed as a LoadBalancer.
  • Backends (itzg/minecraft-server): ONLINE_MODE=FALSE (Velocity handles authentication), exposed internally only (ClusterIP). Forge/Paper/Fabric get the mod/plugin that lets them understand Velocity's "modern" forwarding; Vanilla can't (see Known limitations).

Repo structure

do-games/
โ”œโ”€โ”€ .sops.yaml                        # SOPS encryption rule (age)
โ”œโ”€โ”€ .gitignore
โ”œโ”€โ”€ servers/                          # every game's servers live here, one folder ("group") per game
โ”‚   โ””โ”€โ”€ minecraft/                    # the "minecraft" group - currently the only one
โ”‚       โ”œโ”€โ”€ namespaces.yaml
โ”‚       โ”œโ”€โ”€ kustomization.yaml        # generated by the TUI - do not hand-edit
โ”‚       โ”œโ”€โ”€ cf-api-key.enc.yaml       # optional, SOPS/age-encrypted - only if using CurseForge mods
โ”‚       โ”œโ”€โ”€ velocity/
โ”‚       โ”‚   โ”œโ”€โ”€ settings.toml         # proxy settings (motd, max players) - editable
โ”‚       โ”‚   โ”œโ”€โ”€ configmap.yaml        # generated by the TUI
โ”‚       โ”‚   โ”œโ”€โ”€ deployment.yaml
โ”‚       โ”‚   โ”œโ”€โ”€ service.yaml
โ”‚       โ”‚   โ””โ”€โ”€ forwarding-secret.enc.yaml  # shared secret, SOPS/age-encrypted
โ”‚       โ””โ”€โ”€ <server-name>/            # one folder per backend server
โ”‚           โ”œโ”€โ”€ server.toml           # source of truth - editable (or via the TUI)
โ”‚           โ”œโ”€โ”€ deployment.yaml       # generated by the TUI
โ”‚           โ”œโ”€โ”€ pvc.yaml              # generated by the TUI
โ”‚           โ””โ”€โ”€ service.yaml          # generated by the TUI
โ””โ”€โ”€ tui/                              # management tool (Rust/Ratatui)
    โ”œโ”€โ”€ Cargo.toml
    โ””โ”€โ”€ src/...

Servers are organized into group folders under servers/ (g in the TUI creates a new one) - in prep for other games later, each getting its own group alongside minecraft. Today only the minecraft group is wired up to anything (Velocity proxy, kustomization.yaml, the do-games-minecraft namespace); a server placed in any other group gets its manifests generated but isn't deployed by deploy.sh/D yet.

Important rule: every .yaml file under servers/minecraft/ (except the SOPS secret) is a generated artifact produced by the TUI from the server.toml files and velocity/settings.toml. Don't hand-edit them - running cargo run in tui/ rewrites them entirely on every change, so any manual edit would be lost. The only files meant to be edited directly are servers/minecraft/<name>/server.toml and servers/minecraft/velocity/settings.toml (or, more simply, use the TUI, which edits them for you).

Prerequisites

On the machine used to manage/deploy the cluster:

  • kubectl, configured with a context pointing at the k3s cluster
  • sops (โ‰ฅ 3.13) and age (age, age-keygen)
  • An age key whose public key matches the one in .sops.yaml, present locally at ~/.config/sops/age/keys.txt
  • Rust/cargo (to run the TUI) - installable via rustup

To recreate secret access on a new machine: copy ~/.config/sops/age/keys.txt from a machine that already has the key (this file is never in the repo, see .gitignore). Without this key, sops cannot decrypt forwarding-secret.enc.yaml and deployment will fail at the secret step.

Using the TUI

cd tui
cargo run

Prebuilt binaries (Linux x86_64, macOS x86_64/arm64) are attached to GitHub Releases - built by .github/workflows/release.yml whenever a v*.*.* tag is pushed. CI (.github/workflows/ci.yml) runs cargo fmt --check, clippy, build, and test on every push/PR that touches tui/.

It can be launched from any working directory (it locates the repo root automatically), but cargo run itself must be run from tui/ (or use cargo run --manifest-path tui/Cargo.toml from the repo root). If no servers/ directory is found in any parent of the current directory, it falls back to treating the current directory as the repo root and creates servers/minecraft/ there (with a default Velocity config) instead of failing - useful when running the built binary outside a checkout, but usually not what you want; run it from inside the repo.

On startup, it re-reads every servers/*/*/server.toml and immediately regenerates all manifests - that's expected, it guarantees what's on disk always matches the current server.toml files exactly.

Main screen (server list)

Servers are listed grouped by folder (minecraft/ today, more later), ungrouped servers (directly under servers/) first.

Key Action
โ†‘/โ†“ change selection
a add a new server (defaults to the selected row's group)
e / Enter edit the selected server
d delete the selected server, or the selected group folder if empty (confirm with y/n)
g create a new group folder
v edit the Velocity proxy settings (MOTD, max players shown)
D deploy (kubectl apply -k servers/minecraft/, then decrypts+applies each secret)
X teardown: delete the whole do-games-minecraft namespace (confirm by typing its name)
s view live pod status
q quit

Adding / editing a server

A form with these fields: name, group (folder - leave empty for servers/<name>/, or type an existing/new group name), type (โ†/โ†’ to pick between forge/paper/fabric/vanilla), version, memory (e.g. 8G), PVC storage size (e.g. 20Gi), extra Modrinth mods (comma-separated), CurseForge mods (comma-separated, see below), MOTD, difficulty, Hub (โ†/โ†’ to toggle yes/no).

  • โ†‘/โ†“ or Tab / Shift+Tab: next/previous field
  • โ†/โ†’: change the type or the Hub toggle (only while that field is focused)
  • type directly to edit a text field, Backspace to delete
  • Enter: submit (regenerates all manifests if the input is valid)
  • Esc: cancel without saving anything

Hub: marks this server as where new players land first. It's put at the front of Velocity's try list (velocity/configmap.yaml) regardless of alphabetical server-folder ordering - Velocity connects new players to the first reachable entry in try. If more than one server is marked, they all end up at the front, in their existing relative order (no error, just means whichever of them is reachable first wins). Shown as (hub) next to the server's name on the list screen.

Changing the group of an existing server on save physically moves its folder (server.toml plus its generated manifests) to the new location.

The server name can't be changed once created (the field is locked in edit mode - renaming a server means deleting it and creating another one). The forwarding mod (PCF for Forge, FabricProxy-Lite for Fabric) is added automatically based on the chosen type; no need to list it among the extra mods. If you pick vanilla, a warning is shown: this type cannot do "modern" forwarding with Velocity (see Known limitations).

CurseForge mods

Paste the CurseForge download URL you get from a mod's page (project page or a specific file's page, e.g. https://www.curseforge.com/minecraft/mc-mods/reign-of-nether-rts-in-minecraft/download/8400217) into the "CurseForge mods" field, comma-separated for more than one. The TUI normalizes it to slug:fileId (or just slug/projectId if the URL has no specific file, in which case the newest compatible file is auto-selected) and writes it to curseforge_files in server.toml; that list becomes the CURSEFORGE_FILES env var read by itzg/minecraft-server at container start, which downloads and installs the mods automatically โ€” no manual upload to the PVC needed. A slug:fileId/projectId:fileId string typed directly also works, if you already have it.

This requires a CurseForge API key (mandatory for any CurseForge auto-download, regardless of format): get one at https://console.curseforge.com/, then have it SOPS/age-encrypted into servers/minecraft/cf-api-key.enc.yaml (same pattern as forwarding-secret.enc.yaml โ€” a v1/Secret named curseforge-api-key, namespace do-games-minecraft, with a stringData.apiKey key). D deploy decrypts and applies it right after the forwarding secret, but only if the file exists โ€” clusters with no CurseForge mods configured anywhere don't need it. Only servers with at least one CurseForge mod get the secret mounted (as CF_API_KEY_FILE); other servers are unaffected even if the secret hasn't been set up yet.

Editing Velocity settings (v)

A small form with two fields: MOTD and max players (shown). Tab/Shift+Tab or โ†‘/โ†“ to switch fields, Enter to save (rewrites velocity/ settings.toml and regenerates velocity/configmap.yaml), Esc to cancel. Unlike a server, Velocity has no d delete action anywhere in the TUI - it's the always-on proxy for the whole minecraft group, not a list entry.

Deploy (D)

Runs, in order, kubectl apply -k servers/minecraft/ (creates the do-games-minecraft namespace and all generated manifests) then sops --decrypt ... | kubectl apply -f - for each *.enc.yaml secret (the forwarding secret, and the CurseForge API key if that file exists), streaming output live to the screen. The kustomize apply must run first since the secrets target a namespace it creates. Esc returns to the list without interrupting the deployment if it's still running.

Teardown (X)

Deletes the entire do-games-minecraft namespace (kubectl delete namespace do-games-minecraft), which cascades to everything in it: Velocity, every backend server, every server's PVC (world data included - unrecoverable), and both secrets. There's no equivalent of deploy.sh for this: it's TUI (or manual kubectl) only.

Confirmation requires typing the namespace name exactly, not just y/n - this is the only irreversible, whole-cluster action in the TUI, so it gets more friction than deleting one server. Esc at the confirmation prompt cancels with nothing changed. Once confirmed, it streams on the same screen as D deploy; Esc there returns to the list without interrupting it if still running. servers/ on disk (your server.toml files, generated manifests) is untouched either way - this only affects the cluster, so D redeploys everything from scratch afterwards.

Pod status (s)

Table of pods in the do-games-minecraft namespace, refreshed roughly every 2 seconds (green = Running, yellow = starting up, red = error/CrashLoopBackOff). l opens live logs (kubectl logs -f) for the selected pod. Esc to go back.

Adding your first server / common workflows

  • Add a server: cargo run โ†’ a โ†’ fill in the form โ†’ Enter โ†’ D to deploy.
  • Change memory or disk size of an existing server: cargo run โ†’ select the server โ†’ e โ†’ edit the field โ†’ Enter โ†’ D to apply the change (โš ๏ธ shrinking/growing a PVC afterwards may require manual intervention on the Kubernetes side depending on the storage class - online resizing isn't guaranteed by every provisioner).
  • Delete a server: d then y - removes its servers/minecraft/<name>/ folder and takes it out of kustomization.yaml and Velocity's [servers] table, but does not remove resources already applied to the cluster (PVC included): you need to run a deployment (D) afterwards for the removal to take effect, and delete the PVC by hand if you also want to free the disk (kubectl -n do-games-minecraft delete pvc <name>-data).

DNS

If Velocity's own LoadBalancer Service (servers/minecraft/velocity/service.yaml, port 25565) is directly reachable on your network, kubectl -n do-games-minecraft get svc velocity gives you the IP to point a DNS record at (A + SRV _minecraft._tcp, see below).

If, like this project's own deployment, only ports 80/443 reach your cluster (see infra/velocity-sslh/), point DNS at whatever IP already serves your other HTTPS sites instead, on port 443:

  • A record: <your-subdomain> โ†’ that IP
  • SRV record: _minecraft._tcp.<your-subdomain> โ†’ 0 5 443 <your-subdomain> (so players can type the domain with no port - the SRV target's port is what matters here, not the record type's usual 25565 default)

sslh routes all non-TLS traffic on :443 to Velocity regardless of which domain was typed - there's only one Minecraft network behind the IP, so no Traefik/Velocity-side per-domain config is needed.

Known limitations

  • Vanilla doesn't support "modern" forwarding with Velocity (no mod/plugin layer to read the proxy's data): a vanilla server is declared in velocity.toml but not added to the default try list, and players may not be able to connect to it at all through the proxy. Prefer fabric + FabricProxy-Lite for a vanilla-like experience with secure forwarding.
  • The exact key names expected by Proxy-Compatible-Forge, paper-global.yml, and FabricProxy-Lite were verified against their documentation but not yet tested on a real first boot - fix in tui/src/render/{forge,paper,fabric}.rs if the mod/server requires other keys on first boot.
  • The deploy and status screens require real cluster access (kubectl configured and connected) - not tested end-to-end due to lack of cluster access from the development machine.

Contributing

See CONTRIBUTING.md for the dev workflow and code style, and MAINTAINERS.md for who to reach out to.

Star History

License

MIT - see LICENSE

About

๐ŸŽฎ Manage and deploy game servers

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages