Deployment guide

Geyser

Overview

Geyser is the open-source bridge between Minecraft: Bedrock Edition and Minecraft: Java Edition. Bedrock players on consoles, phones, tablets and Windows normally cannot join a Java Edition server. Geyser sits in front of your Java server, accepts Bedrock players on port 19132 and translates their connection to the Java protocol, so both editions can play together on a server you run.

This SecureAnyCloud image deploys Geyser 2.12.0 on Ubuntu 24.04 on Azure, with NetherNet already enabled and the service sandboxed.

Geyser is a proxy, not a game server. The image contains no Minecraft server, no Mojang game assets and no accepted Minecraft EULA. You point Geyser at your own Java Edition server (on this VM or elsewhere), and your players use their own Minecraft clients. On its first start, Geyser downloads Mojang's client jar to extract language strings and default skins. Each VM fetches its own copy, and the image ships none of it.

NetherNet is already on. From Bedrock 26.60 (planned for 27 October 2026), Bedrock clients connect only with NetherNet, Mojang's new connection method. Geyser 2.12.0 still defaults to the old RakNet method, so this image switches Geyser to NetherNet with its built-in signaling and players can keep joining.

A client first asks the server how to connect with a small web request on TCP 19132, which is called signaling. It then plays over an encrypted WebRTC connection on UDP 19132. Your network security group must allow both TCP and UDP on port 19132.

What you get:

  • NetherNet set up for Azure. Built-in signaling runs on TCP 19132 and gameplay on UDP 19132. On first boot, the VM reads its public IPv4 address from the Azure Instance Metadata Service and tells players to connect to it. Without that, Geyser would offer only the VM's private address.
  • A contained service. Geyser runs as the geyser system account (no shell, no password) under a sandboxed systemd unit: no capabilities, a read-only operating system with only /var/lib/geyser writable, private /tmp and devices, and a system call filter. The Geyser jar is owned by root, so the service cannot change its own code.
  • A key unique to your VM. NetherNet uses a server identity key. Geyser creates it on your VM's first start (/var/lib/geyser/nethernet/identity.pem, readable only by the geyser account), and no key is baked into the image. Each VM also gets its own anonymous statistics ID.
  • Azure metadata blocked for Geyser. Geyser fetches URLs that players and servers influence (skin textures, resource packs). The service is therefore denied the Azure Instance Metadata Service (169.254.169.254) and the Azure platform endpoint (168.63.129.16), while it can still reach your Java server and Microsoft sign-in.
  • Players authenticate. Geyser keeps online mode (auth-type: online). Bedrock players sign in with their own Microsoft accounts and Bedrock logins are validated, so nobody can join as someone else. There is no administrator login, web console or management API, so there is no password to manage.
  • Exactly the ports players need. TCP and UDP 19132 for players and TCP 22 for SSH.
  • A pinned, checked download. Geyser 2.12.0 build 1265 from GeyserMC, verified against the sha256 that GeyserMC publishes.
  • A fully patched base. Ubuntu 24.04 LTS, patched at build time, with unattended security upgrades switched on.

The image contains Geyser Standalone 2.12.0 build 1265 (/opt/geyser/Geyser-Standalone.jar), OpenJDK 21 (Ubuntu package), Ubuntu 24.04 LTS and the Azure Linux agent.

Before you start

  • Recommended VM size: Standard_B2s (2 vCPU / 4 GiB). Geyser itself uses about 0.65 GB of memory, so size up if you also run your Java server on this VM.
  • Player ports: 19132 TCP (NetherNet signaling) and 19132 UDP (NetherNet gameplay).
  • SSH: 22, restricted to your own address.
  • Public IP: give the VM a public IP address so players on the internet can reach it.
  • Admin credential: none exists. Geyser has no admin login, web console or credential of any kind.
  • Java server: not included. You provide your own Minecraft: Java Edition server, on this VM or elsewhere, and point Geyser at it.

Geyser needs outbound internet access to reach Microsoft sign-in, GeyserMC and Mojang's download servers. If your Java Edition server runs on another machine, that machine must accept connections on its Java port (25565 by default) from this VM.

Network security group

NetherNet needs both protocols on 19132. Opening only UDP (enough for the old RakNet method) or only TCP is the most common reason Bedrock players cannot connect. Run these commands from your workstation, with your own resource names:

az network nsg rule create --resource-group <your-rg> --nsg-name <your-nsg> \
  --name geyser-nethernet-tcp --priority 1010 \
  --access Allow --protocol Tcp --direction Inbound \
  --source-address-prefixes Internet --destination-port-ranges 19132
az network nsg rule create --resource-group <your-rg> --nsg-name <your-nsg> \
  --name geyser-nethernet-udp --priority 1020 \
  --access Allow --protocol Udp --direction Inbound \
  --source-address-prefixes Internet --destination-port-ranges 19132

Keep the SSH rule on 22 restricted to your own address. Nothing else needs to be open on this VM.

First boot

Connect over SSH as the admin user you chose when you created the VM:

ssh azureuser@<public-ip>

The login message shows the address players connect to. Geyser and its first-boot unit are active within a minute of boot:

systemctl is-active geyser geyser-firstboot
active active

First boot records the VM's addresses, read from the Azure Instance Metadata Service, in a note for you. The note holds connection details only, never a secret:

sudo cat /root/geyser-instance-info.txt
# Geyser - instance information (written by geyser-firstboot.service).
# Geyser has no administrator login: this note holds connection details only, never a secret. GEYSER_PUBLIC_IP=172.190.69.85 GEYSER_PRIVATE_IP=10.0.0.10 GEYSER_BEDROCK_PORT=19132 (TCP signaling + UDP NetherNet) GEYSER_CONFIG=/var/lib/geyser/config.yml
# Point Geyser at your Java Edition server: edit the java: section of the config, then
#
sudo systemctl restart geyser

Your addresses will differ. Bedrock players enter GEYSER_PUBLIC_IP with port 19132. If the VM has no public IP, the note says none and Geyser offers players its private address, which works only inside your virtual network or VPN.

Prove NetherNet signaling answers

The image ships a self-test. It makes the same signaling request that every current Bedrock client makes before it joins (GET /v1/join on TCP 19132) and checks Geyser's reply.

On the very first boot, Geyser needs up to a minute before signaling answers, because it first downloads Mojang's language files and default skins for this VM. The command below waits for it:

for i in $(seq 1 45); do geyser-selftest >/dev/null 2>&1 && break; sleep 2; done; geyser-selftest
OK NetherNet signaling: name="Geyser" protocol=2193 version=26.52 players=0/100 onlineAuth=true

This proves that the Java runtime started, the configuration parsed, NetherNet is on, signaling answers and online authentication is enforced. It does not need your Java server yet.

The same request works from your own computer against the public address, which also proves that the NSG allows TCP 19132:

curl -s http://<public-ip>:19132/v1/join

It returns a short JSON record with "name":"Geyser", the protocol, the Bedrock version and "onlineAuth":true.

NetherNet is set in the bedrock section of the configuration, and first boot filled in the address players are sent to:

sudo grep -n -E '^ transport:|^ advertise-addresses:|^ - ' /var/lib/geyser/config.yml
31: transport: nethernet 50: advertise-addresses: 51: - '172.190.69.85' 75: advertise-addresses: []

Line 50 is the built-in signaling list, which holds your public address. Line 75 belongs to the external signaling service, which this image does not use.

Point Geyser at your Java server

Geyser itself wrote the shipped config.yml at build time, and it keeps Geyser's defaults apart from NetherNet. Its java section points at 127.0.0.1 port 25565, which works only if you run your Java server on this same VM:

sudo grep -n -A10 '^java:' /var/lib/geyser/config.yml
84:java: 85-
# The IP address of the Java Edition server. 86- address: 127.0.0.1 87- 88-
# The port of the Java Edition server. 89- port: 25565 90- 91-
# What type of authentication Bedrock players will be checked against when logging into the Java server. 92-
# Can be "floodgate" (see https://wiki.geysermc.org/floodgate/), "online", or "offline". 93- auth-type: online 94-

Edit it with sudo nano /var/lib/geyser/config.yml and set two values:

  • address and port: your Java server's address (an IP or DNS name reachable from this VM) and port. If the Java server runs on this VM, keep 127.0.0.1.
  • auth-type: keep online when your Java server runs in online mode, which is the normal case. Players then sign in with their Microsoft accounts. Use floodgate only if you install the Floodgate plugin on your Java server. In that case, copy that server's key.pem to /var/lib/geyser/key.pem (owner geyser, mode 0640) and treat it as a secret. Avoid offline, because anyone could then join under any name.

Then restart Geyser and wait for signaling:

sudo systemctl restart geyser for i in $(seq 1 30); do geyser-selftest >/dev/null 2>&1 && break; sleep 2; done; geyser-selftest
OK NetherNet signaling: name="Geyser" protocol=2193 version=26.52 players=0/100 onlineAuth=true

When the Java server is reachable, the server list entry shows your Java server's MOTD and player count (passthrough-motd). To check reachability from this VM, run nc -zv YOUR_JAVA_HOST 25565, replacing the host and port with your own.

Connect from a Bedrock client

In Minecraft (Bedrock Edition), open Play, then Servers, then Add Server. Enter a name, the VM's public address as Server Address and 19132 as Port. Then save and join.

Consoles that cannot add servers directly need a DNS or "server list" workaround. See the Geyser documentation.

The trust prompt. Built-in signaling uses plain HTTP, which is Geyser's default. The first time a player joins by IP address, Minecraft shows a "trust on first use" prompt. After the player accepts it, the prompt does not appear again for this server. The gameplay connection itself is encrypted (WebRTC with DTLS). To remove the prompt, serve signaling over HTTPS with a certificate for a domain name (see HTTPS for signaling).

Bedrock client versions. Each Geyser release accepts the Bedrock versions it was built for. Geyser 2.12.0 build 1265 accepts Bedrock 26.52 (protocol 2193), as geyser-selftest shows. Bedrock clients update automatically. When a newer Bedrock release is out, the Geyser log says so with a line starting There's a new Geyser update available to support Bedrock version:

sudo journalctl -u geyser --no-pager -o cat | sed -E 's/\x1b\[[0-9;]*m//g' | grep -E 'Loading Geyser version|Built-in signaling started|new Geyser update|NetherNet support' | tail -3
[09:34:05 INFO] Built-in signaling started on http://0.0.0.0:19132 [09:34:37 INFO] Loading Geyser version 2.12.0-b1265 (git-master-02dbfac) [09:34:43 INFO] Built-in signaling started on http://0.0.0.0:19132

If your players run a newer Bedrock version than this build accepts, upgrade Geyser (see Upgrading Geyser).

Security checks

Only SSH (TCP 22) and Geyser on 19132 listen on the network. TCP 19132 is NetherNet signaling. Geyser opens UDP 19132 for each player's WebRTC connection as they join, so it does not appear in this listing while nobody is connecting. The DHCP client on port 68 is the VM's own network setup.

sudo ss -H -tulnp | grep -v -E '127\.0\.0\.5[34]|\[::1\]|127\.0\.0\.1:323' | awk '{print $1, $5, $7}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g'
udp 10.0.0.10%eth0:68 users:(("systemd-network")) tcp 0.0.0.0:22 users:(("sshd"),("systemd")) tcp *:19132 users:(("java")) tcp [::]:22 users:(("sshd"),("systemd"))

Geyser runs as the unprivileged geyser account in a sandboxed unit, and the unit denies it the Azure metadata addresses. Inspect the unit:

systemctl show geyser -p User -p NoNewPrivileges -p ProtectSystem -p PrivateTmp -p IPAddressDeny
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 User=geyser PrivateTmp=yes ProtectSystem=strict NoNewPrivileges=yes
sudo systemd-analyze security geyser.service --no-pager | tail -1
→ Overall exposure level for geyser.service: 1.5 OK 🙂

The deny list holds only the metadata addresses, so Geyser still reaches your Java server, Microsoft sign-in and GeyserMC. The rule lives in /etc/systemd/system/geyser.service.d/50-sac-imds-deny.conf. Keep it.

The NetherNet identity key was created on this VM, and only the geyser account can read it:

sudo ls -l /var/lib/geyser/nethernet/
total 4 -rw------- 1 geyser geyser 288 Oct 20 09:27 identity.pem

Signaling takes the player's address from the connection. It ignores X-Forwarded-For unless you list a reverse proxy in advanced.bedrock.signaling-trusted-proxies, which is empty in this image. A join whose Microsoft sign-in token is not genuine is refused before any connection is set up.

HTTPS for signaling (optional)

To remove the trust prompt, give the server a DNS name. For example, use an Azure DNS record, or the DNS name label of the VM's public IP, pointing at a static public IP. Get a certificate for the name from a public certificate authority, then point built-in signaling at it in /var/lib/geyser/config.yml:

  • bedrock.signaling.builtin.https.certificate: the path to the certificate chain (PEM), for example /var/lib/geyser/tls/fullchain.pem.
  • bedrock.signaling.builtin.https.private-key: the path to its private key. Keep the files owned by geyser with mode 0600.
  • bedrock.signaling.builtin.advertise-addresses: you can leave the IP address here. Players then add the server by its DNS name.

Restart Geyser. Signaling then serves HTTPS on TCP 19132 and refuses plain HTTP, so geyser-selftest (which speaks HTTP) reports a failure. Check with curl -s https://YOUR_DOMAIN:19132/v1/join instead.

GeyserMC's documentation covers the options in detail, including a reverse proxy in front of the signaling port.

Configuration reference

All settings live in /var/lib/geyser/config.yml, each documented by a comment above it. Restart Geyser after a change (sudo systemctl restart geyser). The list below gives each setting, its image default and its meaning.

  • bedrock.port (default 19132): signaling (TCP) and NetherNet gameplay (UDP). Change the NSG rules along with it.
  • bedrock.transport (default nethernet): how players connect. The value both also accepts the old RakNet method, but then needs a separate webrtc-port (another UDP port).
  • bedrock.signaling.builtin.advertise-addresses (default this VM's public IPv4): the address players are sent to. First boot sets it. See below if the address changes.
  • java.address, java.port (default 127.0.0.1, 25565): your Java Edition server.
  • java.auth-type (default online): Microsoft account sign-in. See Point Geyser at your Java server before changing it.
  • motd.primary-motd, motd.passthrough-motd (default Geyser, true): the server list text. Passthrough shows your Java server's own MOTD.
  • log-player-ip-addresses (default true): player addresses in Geyser's log. Set false if you do not want them recorded.
  • enable-metrics (default true): anonymous usage statistics to bStats, which is Geyser's default. Set false to switch them off. Each VM gets its own random metrics-uuid at first boot.
  • advanced.bedrock.use-haproxy-protocol (default false): keep false unless Geyser sits behind a proxy that sends the PROXY protocol, or players could fake their address.

Geyser's own logs are in the systemd journal (sudo journalctl -u geyser) and in /var/lib/geyser/logs/.

If the VM's public address changes, for example after you deallocate and start a VM whose public IP is dynamic, run first boot again. It updates the advertised address and the note. Then restart Geyser:

sudo rm -f /var/lib/sac/data/geyser-firstboot.done
sudo systemctl restart geyser-firstboot
sudo grep GEYSER_PUBLIC_IP /root/geyser-instance-info.txt
sudo systemctl restart geyser

To advertise a different address yourself, for example a static public IP or a load balancer address you add later, run sudo geyser-set-address YOUR_ADDRESS and then sudo systemctl restart geyser.

Upgrading Geyser

Geyser is a single jar, and your config.yml stays as it is. Pick the release and build from GeyserMC's download page, and replace <version> and <build> below. The commands check the jar against the sha256 that GeyserMC publishes before installing it:

V=<version>; B=<build> API="https://download.geysermc.org/v2/projects/geyser/versions/${V}/builds/${B}" SUM="$(curl -fsSL "${API}" | python3 -c 'import json,sys; print(json.load(sys.stdin)["downloads"]["standalone"]["sha256"])')" curl -fsSL -o /tmp/Geyser-Standalone.jar "${API}/downloads/standalone" echo "${SUM} /tmp/Geyser-Standalone.jar" | sha256sum -c - \
  && sudo install -o root -g root -m 0644 /tmp/Geyser-Standalone.jar /opt/geyser/Geyser-Standalone.jar \
  && sudo systemctl restart geyser rm -f /tmp/Geyser-Standalone.jar

Then check the new version with the journalctl command in Connect from a Bedrock client, and run geyser-selftest.

Server components

  • Geyser Standalone: 2.12.0 build 1265, at /opt/geyser/Geyser-Standalone.jar (root-owned)
  • OpenJDK: 21 (Ubuntu package, with security updates through unattended upgrades), at /usr/lib/jvm
  • Configuration and state: /var/lib/geyser/ (config.yml, permissions.yml, nethernet/, packs, extensions, logs, cache)
  • JVM options: -Xms256m -Xmx1024m, set in /etc/default/geyser
  • Self-test: /usr/local/sbin/geyser-selftest
  • Advertised address helper: /usr/local/sbin/geyser-set-address
  • First boot: geyser-firstboot.service, with a note in /root/geyser-instance-info.txt
  • License texts: /usr/share/doc/geyser/

Filesystem layout

  • / (30 GB): root filesystem
  • /boot/efi (106 MB): UEFI boot partition (Gen2 Hyper-V)
  • /mnt (size varies): Azure temporary resource disk

Security recommendations

  • Allow TCP 22 only from your own networks. Only TCP and UDP 19132 need to be open to players.
  • Keep auth-type: online (or Floodgate with its key kept secret). Offline mode lets anyone impersonate any player.
  • Keep /var/lib/geyser/nethernet/identity.pem private. If you copy it to another server, both servers share one identity.
  • Use HTTPS for signaling when you have a domain name.
  • Protect your Java server too. If it runs elsewhere, allow its Java port only from this VM.
  • Upgrade Geyser when Bedrock updates. Unattended upgrades keep Ubuntu and OpenJDK patched.

Troubleshooting

Bedrock players cannot find the server. Check that the NSG allows TCP and UDP 19132, then run curl -s http://YOUR_PUBLIC_IP:19132/v1/join from your own computer. If that answers but players cannot join, check that the advertised address matches the public address (see Prove NetherNet signaling answers) and that UDP 19132 is open.

Players see "Outdated server" or "Outdated client". Their Bedrock version does not match this Geyser build. See Connect from a Bedrock client and Upgrading Geyser.

Players reach the server list but cannot join a world. Geyser cannot reach your Java server. Check java.address and java.port, the Java server's own firewall, and that auth-type matches the Java server's mode. sudo journalctl -u geyser -n 50 shows the reason.

The service does not start. Run systemctl status geyser --no-pager and sudo journalctl -u geyser -n 50. A YAML mistake in config.yml is the usual cause.

Support and licensing

SecureAnyCloud images come with 24/7 support. With any request, include the output of geyser-selftest and the last lines of sudo journalctl -u geyser.

  • Email: sales@secureanycloud.com
  • Response time: 24/7, with a guaranteed 24-hour response SLA
  • Website: https://secureanycloud.com

Geyser is free software under the MIT License. The jar also bundles the MinecraftAuth library (LGPL-3.0, unmodified), Apache-2.0 and MIT-licensed libraries including Bouncy Castle, and a native WebRTC library for NetherNet. That library comprises libdatachannel-java, libdatachannel and libjuice under MPL-2.0, and usrsctp under BSD-3-Clause. NOTICE.native-webrtc lists where to get their source.

Every license and notice text ships on the image:

ls /usr/share/doc/geyser/ && head -3 /usr/share/doc/geyser/LICENSE
LICENSE LICENSE.mappings-MIT LICENSE.native-webrtc-MPL-2.0 LICENSE.native-webrtc-usrsctp-BSD-3-Clause LICENSE.third-party-Apache-2.0 LICENSE.third-party-BouncyCastle-MIT LICENSE.third-party-MIT LICENSE.third-party-MIT-2 LICENSE_MinecraftAuth NOTICE.native-webrtc NOTICE.third-party README.sac The MIT License Copyright (c) 2019-2026 GeyserMC. http://geysermc.org

Minecraft is a trademark of Mojang Synergies AB. Geyser is a community project of GeyserMC. SecureAnyCloud is not affiliated with or endorsed by Mojang, Microsoft or GeyserMC.

Deploy on Azure

Launch Geyser 2.12.0 on Ubuntu 24.04 on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure