High-Performance Minecraft Proxy in Go: gate-plugged
/ 7 min read
Table des matières
In multiplayer Minecraft hosting, multi-server networks have been the architectural standard for over a decade. To link disparate gamemodes (survival, creative, minigames, hubs) behind a single public IP address without disconnecting players between transitions, a packet-level reverse proxy sits at the network edge.
Historically, this tier has been dominated by JVM-based software such as BungeeCord and Velocity. While mature and feature-rich, Java proxies impose a heavy Java Virtual Machine runtime overhead, regularly demanding 512 MB to 1 GB of memory solely to route TCP packets, alongside garbage collection pauses during sudden connection spikes. Furthermore, orchestrating backend server lifecycles—spinning up instances only when players attempt to connect and shutting them down when idle to conserve hosting resources—traditionally requires brittle custom daemon scripts.
To create an ultra-lightweight, self-contained, and deeply integrated gateway for my self-hosted stack, I developed gate-plugged (FRFlo/gate-plugged). Built on top of the modern Go-based Minekube Gate proxy framework, it bundles modular extensions: on-demand server scaling via the Pelican Panel API, deep client and mod inspection (Forge, NeoForge, Lunar Client via Protobuf, Bedrock/Geyser), and centralized administration without requiring plugins on backend game instances.
Here is the architectural deep dive into this high-performance game proxy.
Architecture Overview
gate-plugged serves as the sole public gateway for the Minecraft network. It terminates player TCP connections, negotiates protocol configuration handshakes, and communicates with the Pelican Panel daemon to scale backend game containers:
flowchart TB
subgraph Players["Minecraft Players"]
JavaClient["Standard Java Client"]
LunarClient["Lunar Client (Protobuf)"]
ModdedClient["Forge / NeoForge (Mods)"]
BedrockClient["Bedrock / Geyser"]
end
subgraph GateProxy["gate-plugged (Native Go Binary)"]
TCPListener["TCP Listener (:25565)<br>Velocity Modern Forwarding"]
PluginRouter["Minekube Gate Plugin Engine"]
subgraph Plugins["Internal Plugins"]
Detection["plugins/detection<br>(Forge, Lunar PB, Bedrock)"]
PelicanPlugin["plugins/pelican<br>(Auto-Start & Auto-Stop)"]
Vanish["plugins/vanish<br>(Tablist & Packet Filtering)"]
Commands["plugins/commands<br>(F3 Brand Spoofing)"]
end
end
subgraph Infrastructure["Backend Infrastructure (Pelican / Docker)"]
PelicanAPI["Pelican Panel API<br>(/api/client/servers/{id}/power)"]
LimboServer["Limbo Server (:25566)<br>(Ultra-lightweight holding world)"]
GameServerA["Survival Server (Spigot/Paper)"]
GameServerB["Modded Server (Forge/Fabric)"]
end
Players -->|TCP Connection :25565| TCPListener
TCPListener --> PluginRouter
PluginRouter --> Detection
PluginRouter --> PelicanPlugin
PluginRouter --> Vanish
PluginRouter --> Commands
PelicanPlugin -->|"REST API Power Signal"| PelicanAPI
PluginRouter -->|Holding player during container boot| LimboServer
PluginRouter -->|Route when ready| GameServerA
PluginRouter -->|Direct routing| GameServerB
1. Why Gate (Go) Over a JVM Proxy?
One of the foremost challenges when hosting multiple game workloads on a shared VPS alongside databases, RAG sidecars, and AI agent services is memory contention. Minimizing baseline daemon footprints is paramount.
Minekube Gate is a clean-room Go implementation of the Minecraft protocol (framing, zlib compression, AES/CFB8 encryption, and Velocity modern player forwarding):
- Sub-30 MB Memory Footprint: Where Velocity typically demands 512 MB to 1 GB of heap space,
gate-pluggedoperates comfortably within a ~25 MB memory budget. - Lightweight Goroutines over OS Threads: Every connecting client is managed by concurrent Go goroutines without the memory overhead of JVM thread stacks.
- Instant Cold Boot (< 50 ms): As a compiled static binary in a container, cold starts are instantaneous, completely eliminating JVM JIT warm-up time.
Security via Velocity Modern Forwarding
To forward player IPs, Mojang UUIDs, and skin properties to backend servers without permitting header spoofing, gate-plugged implements Velocity’s modern forwarding protocol.
Every backend connection packet is signed with an HMAC-SHA256 digest keyed with a shared secret passed via an environment variable (GATE_VELOCITY_SECRET):
forwarding: mode: velocity velocity: secret: "${GATE_VELOCITY_SECRET}"2. Pelican Integration: On-Demand Scaling
In traditional multi-server setups, running idle modded or minigame servers around the clock wastes significant RAM.
The plugins/pelican extension connects gate-plugged to the REST API of Pelican Panel (the modern Go/PHP successor to Pterodactyl), implementing automated two-way lifecycle management:
sequenceDiagram
autonumber
actor Player as Player
participant Proxy as gate-plugged
participant Limbo as Limbo Server
participant Pelican as Pelican Panel API
participant Backend as Survival Server (Paper)
Player->>Proxy: Connect (/server survival)
Proxy->>Pelican: GET /servers/{id} (Check State)
alt Server Offline
Proxy->>Pelican: POST /servers/{id}/power (signal: start)
Proxy->>Player: Route to Limbo holding room
Player->>Limbo: Waiting in empty void world (0 load)
loop Availability Polling
Proxy->>Backend: TCP Ping & Protocol Handshake
end
Backend-->>Proxy: Server Ready & Accepting Connections
Proxy->>Player: Seamless transfer to Survival Server
else Server Running
Proxy->>Player: Direct connection
end
The Role of the Limbo Server
When a player requests an offline server, gate-plugged does not disconnect them with a generic error. Instead, it routes them to an internal Limbo instance (limbo:25566).
Limbo is a mock Minecraft server that loads neither terrain nor world entities, idling at under 10 MB of RAM. The player waits safely in a void world with real-time boot status messages presented in the boss bar or chat, while the pelican plugin monitors container initialization and performs the handoff once the target server is ready.
Intelligent Auto-Stop
Conversely, the autostop.go worker monitors backend world populations. When a backend server reports 0 players for a configurable duration (such as 10 minutes), gate-plugged issues a graceful shutdown request (signal: stop) to the Pelican API, immediately reclaiming host resources for the rest of the VPS.
3. Advanced Mod & Client Detection (plugins/detection)
Maintaining server integrity requires auditing client modpacks and client identities during login. Traditionally, this is fragmented across multiple heavy Bukkit plugins or third-party anti-cheat modules.
gate-plugged consolidates this inspection directly at the proxy layer by parsing Plugin Messaging Channels during the protocol configuration phase:
flowchart LR
Client["Minecraft Client"] -->|Plugin Channel Handshake| Proxy["gate-plugged Listener"]
Proxy --> ChannelRouter{"Channel Detected?"}
ChannelRouter -->|fml:handshake / fml:login| ForgeParser["Forge / NeoForge Mod Parser<br>(Extract mod IDs & versions)"]
ChannelRouter -->|lunarclient:pm / lunar:message| LunarParser["Protobuf Lunar Decoder<br>(Extract ID, version, settings)"]
ChannelRouter -->|minecraft:brand| BrandParser["Brand Filter<br>(Fabric, Vanilla, Cheat Clients)"]
ForgeParser --> PolicyEngine["Action Policy Engine (actions.go)"]
LunarParser --> PolicyEngine
BrandParser --> PolicyEngine
PolicyEngine -->|Compliant| Allow["Permit Connection & Log Metadata"]
PolicyEngine -->|Non-Compliant| Kick["Immediate Kick with Custom Reason"]
Forge & NeoForge Mod Inspection (forge.go)
Forge clients negotiate installed mod lists with the server over custom messaging channels (fml:handshake on legacy releases, fml:login on modern versions).
gate-plugged decodes this binary handshake directly:
- Extracts full mod IDs and semantic versions installed on the client machine.
- Validates the list against custom blacklists or whitelists in
plugged.yml. - Rejects prohibited utility mods or cheat injectors before the player reaches backend game worlds.
Protobuf Decoding for Lunar Client (lunar.go)
For users connecting via Lunar Client, data exchange utilizes Protocol Buffers (Protobuf) across the lunarclient:pm channel.
The embedded lunar_pb package compiles Protobuf definitions into the Go proxy binary, enabling gate-plugged to:
- Authenticate valid Lunar Client sessions.
- Enforce allowed visual toggles (disabling wall player nametags, configuring custom crosshairs, etc.).
- Log client build signatures for auditing.
4. Network-Level Vanish (plugins/vanish)
Hiding staff members (/vanish) across multi-server networks often introduces synchronization bugs: player list updates (Tablist / PlayerInfo) and switch announcements must be propagated manually across backend nodes.
gate-plugged’s plugins/vanish module solves this by operating above the game servers:
PlayerInfoUpdatePacket Filtering: When staff members toggle/vanish, the proxy rewrites Tablist update packets directed to standard players, wiping all references to the staff member’s UUID and nametag.- Suppression of Switch Announcements: Global server connection notifications (
ServerConnectedEvent) are intercepted, preventing accidental staff join alerts in public chat. - Universal Backend Compatibility: Because filtering occurs in the proxy pipeline, vanish works transparently across Paper 1.21, Forge 1.20, and isolated standalone minigame servers alike.
Conclusion
By swapping out traditional JVM proxies for a compiled Go gateway, gate-plugged shrinks memory consumption tenfold while delivering sub-millisecond packet routing.
Pairing Minekube Gate with the Pelican Panel API achieves a serverless-style gaming infrastructure: secondary game worlds consume CPU cycles and memory only when players are actively online.
The project is open source on GitHub: github.com/FRFlo/gate-plugged.