Gaming Azure

Velocity 4.0.0 on Ubuntu 24.04 on Azure User Guide

| Product: Velocity 4.0.0 on Ubuntu 24.04 on Azure

Overview

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

Velocity is a modern, high-performance proxy for Minecraft Java Edition, built by PaperMC. It is not a game server — it has no world, no players database of its own, and nothing to build or explore. Instead it sits in front of one or more backend Minecraft servers you already run (a hub, a survival world, a minigame lobby) and routes players between them, so your community connects to one address and moves between your servers without disconnecting.

This image ships the independent Velocity proxy only. No Mojang or Minecraft asset of any kind is downloaded, bundled, or referenced — Velocity is licensed separately (GPL-3.0) from the game it proxies for.

What this appliance proves, and what it does not claim

Velocity boots and listens standalone: on a fresh VM the proxy starts, binds the Minecraft listener, and answers a real Minecraft Server List Ping (the same handshake a Minecraft client uses to show a server in its multiplayer list) with a valid version/MOTD response. That is proven automatically on every deployment — see Verifying your deployment.

End-to-end player routing is not claimed by this image, because it depends on backend Minecraft servers that are entirely your own infrastructure — this is a single-VM proxy appliance, not a network of game servers. Point Velocity at your own backend(s) in velocity.toml (see Configuring your backend servers) to complete the picture.

What you get

Capability What it means in practice
A single public entry point Players connect to one address; Velocity routes them to the right backend server.
Modern player-info forwarding Player identity/IP is forwarded to backends over Velocity's own authenticated protocol — not the legacy, spoofable BungeeCord format.
A forwarding secret that is never shared The secret Velocity uses to authenticate itself to backends is generated fresh, with real entropy, on this VM's first boot — never baked into the image, never the same across two deployments.
online-mode enabled Players are authenticated against Mojang by default, so usernames cannot be spoofed.
A protocol-level health proof The image can prove the proxy is alive and speaking the real Minecraft wire protocol, not just that a port answers.

Licence

Velocity is free software licensed under the GNU General Public License, version 3. That is upstream's licence, verified from the LICENSE file at the commit this build was compiled from — not from a badge.

The image ships the obligations with the binary:

  • /usr/share/doc/velocity/LICENSE — the full GPL-3.0 text.
  • /usr/share/doc/velocity/SOURCE.txt — the exact upstream build and commit the installed binary came from, a link to the corresponding source tree, and a written offer for that source.

cloudimg's own additions (the first-boot bootstrap, the Server List Ping probe, and the round-trip audit) are kept in separate files under /usr/local, and are themselves licensed GPL-3.0-or-later. Velocity and Minecraft are trademarks of their respective owners; cloudimg is not affiliated with or endorsed by PaperMC or Mojang Studios.


Before you start

Item Value
Recommended VM size Standard_B2s (2 vCPU / 4 GiB) — a proxy is far lighter than a game server
Minecraft listener TCP 25577 — this must be reachable from the internet (Java Edition's protocol is TCP only; there is no UDP port to open)
SSH 22, restricted to your own address
Java runtime OpenJDK 25 (installed from the Ubuntu 24.04 archive — never Oracle JDK)

The JVM heap is capped at -Xms512M -Xmx768M, comfortable against the 4 GiB of a Standard_B2s with headroom for the OS. There is no swap in the image — Azure manages swap on the ephemeral resource disk, and this image is verified swap-free at capture time.

Network security group

The Minecraft listener is the one thing that genuinely must be public. Velocity uses TCP 25577 only:

# Run from your workstation, not the VM. Replace the names with your own.
az network nsg rule create --resource-group <your-rg> --nsg-name <your-nsg> \
  --name velocity-proxy-tcp --priority 1010 \
  --access Allow --protocol Tcp --direction Inbound \
  --source-address-prefixes Internet --destination-port-ranges 25577

First boot

On first boot the VM generates its own forwarding secret — 256 bits of entropy from openssl rand -hex 32 — before the proxy is allowed to start at all. Nothing is baked into the image: no secret, no config beyond the shipped placeholders.

This is not incidental ordering. Velocity will silently auto-generate a forwarding secret file if it starts and finds none present — which would be exactly the wrong thing for an image-based appliance, because every customer VM cloned from the same captured image would then share the same secret. This image closes that gap structurally: velocity.service will not start until a bootstrap-ready marker exists, and only this VM's own first-boot script can create that marker, after it has already written a real secret.

Give it a minute after the VM reports Running, then check it:

systemctl is-active velocity-firstboot.service velocity.service
active
active

Then read this VM's credentials:

sudo cat /root/velocity-credentials.txt
# Velocity proxy — per-VM credentials, generated at first boot.
# See: https://www.cloudimg.co.uk/guides/velocity-on-ubuntu-24-04-azure/
VELOCITY_FORWARDING_SECRET=<64 lowercase hex characters, unique to this VM>
VELOCITY_LISTEN_PORT=25577
VELOCITY_PUBLIC_IP=<this VM's public IP>

That file is 0600 root:root. Keep it as you would any other secret — your backend Minecraft servers need the same value (see below) to accept connections forwarded by this proxy.

Terminal showing the Velocity proxy artefact verified by its manifest Main-Class and version, OpenJDK 25 confirmed as the Java runtime with no Oracle JDK anywhere in the banner, velocity-firstboot.service and velocity.service both active, the Minecraft listener bound on all addresses at port 25577 and nothing else new, the 674 line GPL v3 licence shipped with a written source offer, and zero lines of swapon output confirming no swap on the OS disk

Terminal showing the per-VM forwarding secret file and the credentials file both owned root colon root with the secret file mode 600, the credentials file holding a VELOCITY_FORWARDING_SECRET value with its first 12 characters shown and 64 as the total hex character count, a check confirming the value matches the 64 lowercase hex character shape produced by openssl rand -hex 32 and is not a known weak or placeholder value, and velocity.toml shown pointing player-info-forwarding-mode at modern and forwarding-secret-file at the exact same per-VM path with online-mode set to true

Full service log on a clean first start

sudo journalctl -u velocity.service --no-pager -n 15
Started velocity.service - Velocity Minecraft proxy.
main WARN Advanced terminal features are not available in this environment
[INFO]: Booting up Velocity 4.0.0...
[INFO]: Connections will use epoll channels, libdeflate (Linux x86_64) compression, OpenSSL 3.x.x (Linux x86_64) ciphers
[INFO]: Loading localizations...
[INFO]: Loading plugins...
[INFO]: Loaded 1 plugins
[INFO]: Listening on /[0:0:0:0:0:0:0:0]:25577
[INFO]: Done (0.84s)!

Configuring your backend servers

Velocity ships with placeholder backend addresses that are not, and are never claimed to be, real reachable servers — this is a single-VM proxy appliance, so pointing it at your own infrastructure is the one thing only you can do.

sudo cat /etc/velocity/velocity.toml

The [servers] table is where you tell Velocity about your own backends:

[servers]
# PLACEHOLDER — replace with your own backend server(s).
lobby = "127.0.0.1:30066"

try = [
    "lobby"
]

Edit it to point at your real backend server(s):

sudoedit /etc/velocity/velocity.toml
[servers]
lobby    = "10.0.1.10:25565"
survival = "10.0.1.11:25565"

try = [
    "lobby"
]

Then restart the proxy to apply it:

sudo systemctl restart velocity.service

Pairing your backend servers with this proxy

Every backend server behind Velocity needs to be configured to trust this proxy specifically, using the same forwarding secret this VM generated at first boot:

  1. Copy the value of VELOCITY_FORWARDING_SECRET from /root/velocity-credentials.txt on this VM.
  2. On each backend Paper/Velocity-aware server, set velocity-secret-file (or the equivalent config for your server software) to a file containing that exact value.
  3. Enable modern forwarding on the backend server's own config (Paper: config/paper-global.ymlproxies.velocity.enabled: true, with the same secret).
  4. Restart the backend server.

Never hand-type a secret of your own choosing on the backend — always copy the value this VM generated, so the pairing is provably the same secret on both ends.

Terminal showing a real Minecraft Server List Ping against the proxy's own listener: a handshake packet, a status request, and a ping and pong round trip, with the response reporting SLP_OK, the Velocity version, the protocol number, the configured MOTD, and a maximum player count of 500, followed by a raw non-protocol connection to the same port being rejected rather than answered


Verifying your deployment

The image carries its own audit. It does not just check that a port is open — it proves the forwarding secret is genuinely per-VM and matches what the proxy actually loaded, and that the proxy answers a real Minecraft protocol handshake:

sudo /usr/local/sbin/velocity-roundtrip-check.sh
CREDENTIAL_OK: forwarding-secret is 64 hex chars, per-VM, matches the live file
SLP_OK version=Velocity 4.0.0 protocol=767 motd={'color': '#09add3', 'text': 'A Velocity Proxy on cloudimg — configure your backend servers'} players_max=500
ROUNDTRIP_OK

It ends with ROUNDTRIP_OK and exits non-zero — with the word FAIL in its output — if anything failed: the secret file missing or too short, the credentials file out of sync with the live secret, a weak/placeholder value, or the proxy failing to answer a real Server List Ping.

You can also run the Server List Ping probe on its own at any time:

python3 /usr/local/lib/cloudimg/velocity-slp-probe.py 127.0.0.1 25577

Terminal showing the shipped velocity.toml with its CUSTOMER ACTION REQUIRED banner and the servers table's placeholder marked as such, followed by a full run of the round-trip self-audit reporting CREDENTIAL_OK for the per-VM forwarding secret, SLP_OK for a real Minecraft protocol handshake against the proxy's own listener, and a final ROUNDTRIP_OK


Troubleshooting

velocity.service will not start. It is gated on velocity-firstboot.service having already written a per-VM forwarding secret — that is deliberate, so the proxy can never start with no secret and silently generate a shared one. Check firstboot first:

systemctl status velocity-firstboot.service --no-pager
sudo journalctl -u velocity-firstboot.service --no-pager -n 30

If firstboot is active (exited) and the sentinel exists (ls -l /var/lib/cloudimg/velocity-firstboot.done) but velocity.service still refuses to start, check the bootstrap-ready marker:

ls -l /var/lib/cloudimg/velocity-bootstrap-ready
sudo systemctl status velocity.service --no-pager

Players cannot connect through the proxy. Confirm the NSG allows inbound TCP 25577 (there is no UDP port for Java Edition), then confirm your backend server(s) are reachable from this VM and configured with the same forwarding secret:

sudo grep -A3 '^\[servers\]' /etc/velocity/velocity.toml

Then, replacing YOUR-BACKEND-IP with the real address, confirm the backend is reachable from this VM:

nc -zv YOUR-BACKEND-IP 25565

A player is kicked immediately after connecting. This is almost always a forwarding-secret mismatch between this proxy and the backend server it routed the player to — the backend rejects the forwarded player-info packet. Re-copy VELOCITY_FORWARDING_SECRET from /root/velocity-credentials.txt on this VM to the backend's config, exactly, and restart the backend.

Checking the proxy is genuinely alive, not just "port open".

sudo /usr/local/sbin/velocity-roundtrip-check.sh

A FAIL line names exactly what did not check out.


Support

cloudimg images come with 24/7 support. Include the output of sudo /usr/local/sbin/velocity-roundtrip-check.sh and the last lines of sudo journalctl -u velocity.service --no-pager -n 50 with any request.

Velocity itself is community software maintained by PaperMC — for proxy configuration, plugins, and protocol questions, the upstream project and its community are the right place.