Choose the proxy deployment intentionally
The existing direct Geyser and Floodgate guide installs both plugins on one Paper server. This guide covers a different architecture: Velocity is already the public entry point for multiple backends, so Geyser runs on the proxy.
Geyser's current proxy guidance is explicit:
- install Geyser only on the proxy
- install Floodgate on the proxy for Bedrock account authentication
- add Floodgate to backends only when backend plugins need its API or related data
- make every backend compatible with the Java protocol version Geyser currently emulates
Check Geyser's supported-versions page before an update. Bedrock clients move quickly, so hard-coded version claims age faster than the network design.
Step 1: Start from a working Velocity network
Complete these checks first:
- Java players can join Velocity and reach a Paper backend.
- Velocity modern forwarding works.
- Backends are not public.
- The proxy has a persistent
/servermount.
Use the Velocity Compose guide and modern forwarding runbook if any of those statements is false.
Step 2: Add the proxy plugins and UDP mapping
The itzg/mc-proxy image can download plugin URLs into the proxy plugin directory. Add the official Geyser and Floodgate Velocity download endpoints and publish the Bedrock listener as UDP:
services:
proxy:
image: itzg/mc-proxy:latest
ports:
- "25565:25577"
- "19132:19132/udp"
environment:
TYPE: VELOCITY
MEMORY: 1G
PLUGINS: "https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/velocity,https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/velocity"
volumes:
- ./proxy:/server
The latest download endpoints are convenient for the first setup but change over time. For controlled production updates, pin reviewed plugin artifacts or preserve known-good JARs and update them as one tested operation.
Render the Compose file before starting:
docker compose config
docker compose up -d proxy
docker compose logs -f proxy
Confirm both plugins load without dependency or Java errors.
Step 3: Configure the Bedrock listener
After the first start, open the Geyser configuration below the proxy's plugin directory and check:
bedrock:
address: 0.0.0.0
port: 19132
clone-remote-port: false
0.0.0.0 allows Geyser to listen on the container interfaces. The Compose mapping publishes that container UDP port on the host. Do not remove /udp; an unsuffixed Compose mapping is TCP and will not carry normal Bedrock traffic.
In the same generated Geyser configuration, locate auth-type and set it to floodgate. The surrounding section name can change between configuration versions, so edit the generated file instead of replacing it wholesale with an older example.
Restart the proxy after changing the configuration:
docker compose restart proxy
docker compose logs --tail=150 proxy
Step 4: Open UDP end to end
Four layers must agree on the same port and protocol:
- Geyser listens on UDP 19132 in the container.
- Compose maps
19132:19132/udp. - The Linux or Windows host firewall allows UDP 19132.
- The router or cloud-provider firewall allows UDP 19132 to the host.
Do not replace the Java TCP rule with the Bedrock rule. Java and Bedrock use different transport paths here:
| Client | Public endpoint |
|---|---|
| Java Edition | play.example.com:25565/tcp |
| Bedrock Edition | play.example.com:19132/udp |
An SRV record used by Java does not remove the Bedrock port in this setup. Tell Bedrock players the explicit port.
Step 5: Validate from inside and outside
Check the Docker mapping:
docker compose port proxy 19132/udp
docker compose logs --tail=150 proxy
Then run Geyser's own external connection test from the proxy console:
geyser connectiontest play.example.com 19132
Finally, test with a Bedrock client from a different network. A local Wi-Fi test alone does not prove that router or provider UDP ingress works.
The first successful login should reach the same backend selected by Velocity. Verify the player in both proxy and backend logs and confirm that their name/UUID behavior matches your Floodgate policy.
When backends need Floodgate too
Basic crossplay does not require Floodgate on every Paper server. Add it only when backend plugins need the Floodgate API, Bedrock skin handling, or Floodgate data.
The official extended procedure requires send-floodgate-data: true on the proxy and the same Floodgate key.pem on the trusted backends. That key enables Bedrock authentication and must never be published or sent to untrusted people. Transfer it as binary data, restrict file access, and rotate/rebuild the trust relationship if it leaks.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Java works, Bedrock shows “Unable to connect” | UDP mapping or firewall missing | Verify all four UDP layers and run geyser connectiontest |
| Geyser is loaded on every backend | Wrong proxy architecture | Keep Geyser only on Velocity |
| Bedrock reaches Geyser but not the backend | Velocity/backend path or version mismatch | Test Java through Velocity and check Geyser supported versions |
| Floodgate login asks for Java authentication | Geyser auth type not set to Floodgate | Align generated Geyser and Floodgate configuration |
AEADBadTagException after copying keys | Floodgate keys differ or were transferred incorrectly | Recreate/copy the exact key according to Floodgate docs |
| UDP 19132 conflicts with another plugin | Two services use the same UDP port | Assign unique UDP listeners |
Next steps
- If Java routing is unreliable, use the Velocity backend runbook.
- Create a clean player hostname with the Minecraft SRV generator, while keeping the Bedrock port explicit.
- Recheck Geyser supported versions before proxy or backend upgrades.