Velocity 4.0.0 on Ubuntu 24.04 on Azure User Guide
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.


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:
- Copy the value of
VELOCITY_FORWARDING_SECRETfrom/root/velocity-credentials.txton this VM. - 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. - Enable modern forwarding on the backend server's own config (Paper:
config/paper-global.yml→proxies.velocity.enabled: true, with the same secret). - 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.

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

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.