Gaming Azure

Geyser 2.12.0 on Ubuntu 24.04 on Azure User Guide

| Product: Geyser 2.12.0 on Ubuntu 24.04 on Azure

Overview

This guide covers the deployment and use of Geyser 2.12.0 on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images.

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.

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 itself to extract language strings and default skins, so each VM fetches its own copy; the image ships none of it.

NetherNet is already on. From Bedrock 26.60 (planned for 27 October 2026) Bedrock clients only connect with NetherNet, Mojang's new connection method. Geyser 2.12.0 still defaults to the old RakNet method; this image switches Geyser to NetherNet with its built-in signaling, so players can keep joining. A client first asks the server how to connect with a small web request on TCP 19132 (this is called signaling), then plays over an encrypted WebRTC connection on UDP 19132. Your network security group must allow both TCP and UDP on port 19132.

The cloudimg image adds:

  • 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 only offer 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); 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), so the service is 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 GeyserMC publishes.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: 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

Item Value
Recommended VM size Standard_B2s (2 vCPU / 4 GiB). Geyser itself uses about 0.65 GB of memory; 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. 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 with 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 this VM's addresses (read from the Azure Instance Metadata Service) in a note for you. It 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. GEYSER_PUBLIC_IP with port 19132 is what Bedrock players enter. If the VM has no public IP the note says none and Geyser offers players its private address, which only works inside your virtual network or VPN.

Terminal showing geyser and geyser-firstboot active, the running Geyser process owned by the dedicated geyser account, and the per VM connection note with the public IP, the private IP and port 19132 for TCP signaling and UDP NetherNet


Prove NetherNet signaling answers

The image ships a self test that makes the same signaling request 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, so 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 the Java runtime started, the configuration parsed, NetherNet is on and signaling answers, and that 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 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 (your public address); line 75 belongs to the external signaling service, which this image does not use.

Terminal showing geyser-selftest reporting OK for the NetherNet signaling probe with Bedrock protocol 2193 version 26.52 and online authentication on, the transport set to nethernet and the advertised address set to the VM's public IP


Point Geyser at your Java server

The shipped config.yml was written by Geyser itself on the build and keeps Geyser's defaults apart from NetherNet. Its java section points at 127.0.0.1 port 25565, which only works 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:

  • 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 (the normal case). Players then sign in with their Microsoft accounts. Use floodgate only if you install the Floodgate plugin on your Java server: copy that server's key.pem to /var/lib/geyser/key.pem (owner geyser, mode 0640) and treat it as a secret. Avoid offline: 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 (replace the host and port).


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, Geyser's default. The first time a player joins by IP address, Minecraft shows a "trust on first use" prompt; after they accept it, it 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, and 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 here 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:

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-cloudimg-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 (empty in this image). A join whose Microsoft sign in token is not genuine is refused before any connection is set up.

Terminal showing the listening sockets limited to SSH and TCP 19132 held by the Geyser JVM, the hardened unit properties including the metadata deny list, the systemd exposure score of 1.5 and the per VM NetherNet identity key readable only by the geyser account


HTTPS for signaling (optional)

To remove the trust prompt, give the server a DNS name (for example 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 it from a public certificate authority, and 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).

Setting Image default Meaning
bedrock.port 19132 Signaling (TCP) and NetherNet gameplay (UDP). Change the NSG rules with it.
bedrock.transport nethernet How players connect. both also accepts the old RakNet method, but then needs a separate webrtc-port (another UDP port).
bedrock.signaling.builtin.advertise-addresses 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 127.0.0.1, 25565 Your Java Edition server.
java.auth-type online Microsoft account sign in. See Point Geyser at your Java server before changing it.
motd.primary-motd, motd.passthrough-motd Geyser, true The server list text; passthrough shows your Java server's own MOTD.
log-player-ip-addresses true Player addresses in Geyser's log. Set false if you do not want them recorded.
enable-metrics true Anonymous usage statistics to bStats, 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 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/cloudimg/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 load balancer address you add later), run sudo geyser-set-address YOUR_ADDRESS and then sudo systemctl restart geyser.

Terminal showing the shipped configuration with bedrock port 19132, transport nethernet, the Java placeholder 127.0.0.1 port 25565 with online authentication, and the licence and notice files shipped with Geyser 2.12.0


Upgrading Geyser

Geyser is a single jar; 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 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

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

Filesystem layout

Mount point Size Description
/ 30 GB Root filesystem
/boot/efi 106 MB UEFI boot partition (Gen2 Hyper V)
/mnt 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 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 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 the advertised address (see Prove NetherNet signaling answers) matches the public address, 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

cloudimg images come with 24/7 support. Include the output of geyser-selftest and the last lines of sudo journalctl -u geyser with any request.

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 (libdatachannel-java, libdatachannel and libjuice under MPL-2.0, usrsctp under BSD-3-Clause; NOTICE.native-webrtc lists where to get their source). Every licence 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.cloudimg
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. cloudimg is not affiliated with or endorsed by Mojang, Microsoft or GeyserMC.