What Is Velocity?

Velocity is a modern Minecraft proxy software developed by the PaperMC team. It brings multiple Minecraft servers together under a single IP, letting players move between servers seamlessly. It is the modern, secure and performant alternative to BungeeCord.

BungeeCord vs Velocity Comparison

FeatureBungeeCordVelocity
DeveloperSpigotMC (md_5)PaperMC
PerformanceMedium — its older architecture uses more CPU/RAMHigh — up to 50% lower resource usage
ForwardingBungeeGuard or legacy IP forwardingModern forwarding (HMAC-based, secure)
SecurityIP spoofing vulnerability (requires an extra plugin)Built-in security, UUID spoofing protection
Plugin APIOld, callback-basedModern, event-driven, CompletableFuture support
Backend SupportSpigot, PaperPaper, Purpur, Fabric, Forge
DDoS ProtectionNone (a plugin is required)Built-in rate limiting
1.21+ CompatibilityYesYes — priority support
Active DevelopmentSlowVery active
CommunityLarge (legacy)Growing (modern)

Installing Velocity

Create a separate user and directory for Velocity. The proxy runs independently of the backend servers.

Requirements

  • Java 17 or newer
  • Minimum 512 MB RAM (for the proxy)
  • A separate port (default: 25577)
  • Additional RAM and ports for the backend servers

Download and First Start

# Create the Velocity directory
mkdir -p ~/velocity && cd ~/velocity

# Download the latest build (3.4.0 is an example — check the current version)
wget https://api.papermc.io/v2/projects/velocity/versions/3.4.0/builds/latest/downloads/velocity-3.4.0.jar

# First start — let the config files be generated
java -Xms512M -Xmx512M -jar velocity-3.4.0.jar

# Stop it with Ctrl+C

After the first start the following files are created:

Directory Structure

text
~/velocity/
├── velocity-3.4.0.jar        # Main proxy file
├── velocity.toml              # Main configuration
├── forwarding.secret          # Modern forwarding key
├── server-icon.png            # Server list icon (64x64)
├── plugins/                   # Velocity plugins
└── logs/                      # Log files

velocity.toml Configuration

velocity.toml is the main config file that defines every aspect of the proxy's behaviour. We go through each setting in detail:

General Settings

SettingDefaultRecommendedDescription
<code>bind</code>0.0.0.0:255770.0.0.0:25577The IP and port the proxy listens on
<code>motd</code>A Velocity ServerCustom MOTDThe description shown in the server list (supports colour codes)
<code>show-max-players</code>500500Maximum player count shown in the server list
<code>online-mode</code>truetrueMojang authentication (premium check)
<code>force-key-presence</code>truetrueRequire chat signing keys (1.19.1+)
<code>player-info-forwarding-mode</code>NONEmodernIP/UUID forwarding mode (modern = the most secure)
<code>announce-forge</code>falsefalseSend the mod list to Forge clients
toml
# velocity.toml — General settings
bind = "0.0.0.0:25577"
motd = "&b&lKEYDAL &fNetwork &7| &aWelcome to our server!"
show-max-players = 500
online-mode = true
force-key-presence = true
player-info-forwarding-mode = "modern"

Server Definitions

You define the backend servers in the [servers] section. Each server consists of a name and an IP:port pair.

toml
[servers]
lobby = "127.0.0.1:25565"
survival = "127.0.0.1:25566"
skyblock = "127.0.0.1:25567"
creative = "127.0.0.1:25568"

# Servers tried in order when a player first connects
try = ["lobby"]

Forced Hosts

You can route different domain names straight to different servers:

toml
[forced-hosts]
"play.example.com" = ["lobby"]
"survival.example.com" = ["survival"]
"skyblock.example.com" = ["skyblock"]
"creative.example.com" = ["creative"]

All domain names must point in DNS to the same server IP and to the Velocity port (25577). Velocity then routes to the correct backend based on the incoming hostname.

text
# DNS A records (all to the same IP)
play.example.com       A    185.x.x.x
survival.example.com   A    185.x.x.x
skyblock.example.com   A    185.x.x.x

# SRV record (required when the port is 25577)
_minecraft._tcp.play.example.com  SRV  0 0 25577 play.example.com

Advanced Settings

SettingDefaultDescription
<code>login-ratelimit</code>3000Minimum time between login attempts (ms)
<code>connection-timeout</code>5000Backend connection timeout (ms)
<code>read-timeout</code>30000Data read timeout (ms)
<code>haproxy-protocol</code>falseHAProxy PROXY protocol support (for TCPShield)
<code>ping-passthrough</code>DISABLEDTake ping information from the backend (ALL, MODS, DESCRIPTION, DISABLED)
toml
[advanced]
compression-threshold = 256
compression-level = -1
login-ratelimit = 3000
connection-timeout = 5000
read-timeout = 30000
haproxy-protocol = false
tcp-fast-open = false
bungee-plugin-message-channel = true
show-ping-requests = false
failover-on-unexpected-server-disconnect = true
announce-proxy-commands = true
log-command-executions = false
log-player-connections = true

Adding Backend Servers

Each backend server is an independent Paper/Purpur instance. It runs on its own port and has its own config files.

Backend Directory Structure

text
~/servers/
├── lobby/                 # Port 25565
│   ├── paper.jar
│   ├── server.properties
│   └── config/paper-global.yml
├── survival/              # Port 25566
│   ├── paper.jar
│   ├── server.properties
│   └── config/paper-global.yml
└── skyblock/              # Port 25567
    ├── paper.jar
    ├── server.properties
    └── config/paper-global.yml

server.properties Settings

The following settings are critical in each backend server's server.properties file:

SettingValueDescription
<code>server-port</code>25565/25566/25567A different port for each server
<code>server-ip</code>127.0.0.1Only accept connections from localhost
<code>online-mode</code>falseAuthentication is handled by Velocity
<code>prevent-proxy-connections</code>falseDo not block proxy connections

Modern Forwarding Configuration

Modern forwarding is Velocity's HMAC-based secure IP/UUID forwarding system. Backend servers receive the player's real IP, UUID and skin data securely.

Step 1: The Velocity Side

toml
# velocity.toml
player-info-forwarding-mode = "modern"

This setting is already present by default. The forwarding.secret file is generated automatically on the first start.

Step 2: Copying the Secret

cat ~/velocity/forwarding.secret
# Example output: aB3dEf7GhI9jKlMnOpQrStUvWxYz12

You need to copy this key to every backend server.

Step 3: Paper Backend Configuration

On each backend server, edit the config/paper-global.yml file:

yaml
# config/paper-global.yml
proxies:
  velocity:
    enabled: true
    online-mode: true
    secret: "aB3dEf7GhI9jKlMnOpQrStUvWxYz12"   # exactly the same as forwarding.secret

Forwarding Mode Comparison

ModeSecurityDescription
<code>none</code>NoneNo IP/UUID forwarding — the backend cannot receive the real data
<code>legacy</code>LowBungeeCord compatible — open to IP spoofing
<code>bungeeguard</code>MediumLegacy protected with a BungeeGuard token
<code>modern</code>HighHMAC-based — only Velocity supports it, requires Paper/Purpur

Switching Between Servers

Velocity ships built-in commands so players can move between servers:

CommandDescriptionPermission
<code>/server lobby</code>Switch to the specified servervelocity.command.server
<code>/server</code>Show the server listvelocity.command.server
<code>/glist</code>List the players on all serversvelocity.command.glist
<code>/send Player survival</code>Send a player to the specified servervelocity.command.send
<code>/send all lobby</code>Send all players to the lobbyvelocity.command.send
<code>/velocity plugins</code>List the installed Velocity pluginsvelocity.command.plugins
<code>/velocity reload</code>Reload velocity.tomlvelocity.command.reload

For a server selection menu (GUI) you can use plugins such as LobbyAPI, HubCommand or ServerSelectorX.

Security

Security is critical in a network setup. A wrong configuration opens the door to UUID spoofing, unauthorised access and DDoS attacks.

Firewall: Open Only the Proxy Port

Rule: only the Velocity port (25577) and SSH (22) should be open to the outside. The backend ports (25565-25567) must be unreachable from outside.

# Firewall settings with UFW

# SSH access
sudo ufw allow 22/tcp

# Velocity proxy port (open to the outside — the single entry point)
sudo ufw allow 25577/tcp

# Backend ports CLOSED — they already listen on 127.0.0.1
# But block them explicitly to be sure:
sudo ufw deny 25565/tcp
sudo ufw deny 25566/tcp
sudo ufw deny 25567/tcp

# Enable the firewall
sudo ufw enable
sudo ufw status verbose
PortStateDescription
22/tcpALLOWSSH access
25577/tcpALLOWVelocity proxy — the port players connect to
25565/tcpDENYLobby backend — localhost only
25566/tcpDENYSurvival backend — localhost only
25567/tcpDENYSkyblock backend — localhost only

Close Backend Port 25565

Use the server-ip=127.0.0.1 setting in the backend servers' server.properties file so they only accept connections from localhost:

properties
# In every backend server's server.properties file:
server-ip=127.0.0.1
online-mode=false

IPWhitelist

To make backend servers accept only connections coming from Velocity's IP, you can use the IPWhitelist plugin:

  • Install the IPWhitelist plugin on the backend servers
  • In the config, allow only 127.0.0.1 (or Velocity's IP)
  • Anyone who connects directly is rejected

DDoS Protection

  • TCPShield — Free Minecraft DDoS protection, reverse proxy
  • Cloudflare Spectrum — Paid, enterprise-grade TCP proxy
  • OVH Game DDoS Protection — Built into OVH servers
  • Velocity's built-in rate limiting — brute force protection through the login-ratelimit: 3000 setting

LuckPerms Network Setup

For permission management across the network you need to install LuckPerms on every server and use a shared database. That way permissions stay in sync on all servers.

MySQL Database Setup

# MySQL installation (Ubuntu/Debian)
sudo apt install mysql-server -y
sudo mysql_secure_installation

# Create the database and user for LuckPerms
sudo mysql -u root -p
CREATE DATABASE luckperms;
CREATE USER 'luckperms'@'localhost' IDENTIFIED BY 'your_strong_password';
GRANT ALL PRIVILEGES ON luckperms.* TO 'luckperms'@'localhost';
FLUSH PRIVILEGES;
EXIT;

LuckPerms Config (On Every Server)

In the LuckPerms config.yml file on Velocity, lobby, survival and every backend server:

yaml
# plugins/LuckPerms/config.yml (the same on all servers)
storage-method: mysql

data:
  address: localhost:3306
  database: luckperms
  username: luckperms
  password: 'your_strong_password'
  pool-settings:
    maximum-pool-size: 10

# Instant synchronisation between servers
messaging-service: sql

Server-Scoped Permissions (Context)

With the LuckPerms context system you can assign permissions that apply only to certain servers:

text
# fly permission only on the survival server
lp user Player permission set essentials.fly true server=survival

# gamemode permission only on the creative server
lp group vip permission set essentials.gamemode true server=creative

# Permission valid on all servers (when no context is given)
lp group default permission set essentials.home true

Startup Script

Create a bash script to start every server with a single command:

bash
#!/bin/bash
# start-network.sh

echo "=== Starting the KEYDAL Network ==="

# Start the backend servers with screen
echo "[1/4] Starting lobby..."
screen -dmS lobby bash -c 'cd ~/servers/lobby && java -Xms1G -Xmx1G -XX:+UseG1GC -jar paper.jar --nogui'
sleep 8

echo "[2/4] Starting survival..."
screen -dmS survival bash -c 'cd ~/servers/survival && java -Xms4G -Xmx4G -XX:+UseG1GC -jar paper.jar --nogui'
sleep 8

echo "[3/4] Starting skyblock..."
screen -dmS skyblock bash -c 'cd ~/servers/skyblock && java -Xms2G -Xmx2G -XX:+UseG1GC -jar paper.jar --nogui'
sleep 8

# Start the Velocity proxy last
echo "[4/4] Starting Velocity..."
screen -dmS velocity bash -c 'cd ~/velocity && java -Xms512M -Xmx512M -XX:+UseG1GC -jar velocity.jar'

echo ""
echo "=== All servers started! ==="
echo "To attach: screen -r <name>"
echo "  screen -r lobby"
echo "  screen -r survival"
echo "  screen -r skyblock"
echo "  screen -r velocity"
chmod +x start-network.sh
./start-network.sh

Systemd Service

Create a systemd service so Velocity starts automatically when the machine reboots:

ini
# /etc/systemd/system/velocity.service
[Unit]
Description=Velocity Minecraft Proxy
After=network.target

[Service]
User=minecraft
Group=minecraft
WorkingDirectory=/home/minecraft/velocity
ExecStart=/usr/bin/java -Xms512M -Xmx512M -XX:+UseG1GC -jar velocity.jar
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=velocity

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable velocity.service
sudo systemctl start velocity.service
sudo systemctl status velocity.service

Common Mistakes and Their Fixes

ProblemCauseFix
Player cannot connectThe firewall is blocking port 25577Check <code>ufw allow 25577/tcp</code>
"Unable to connect to server" errorThe backend server is offline or the port is wrongCheck that the backend is running and on the correct port
"If you wish to use IP forwarding" errorModern forwarding is not configuredEnter the velocity secret in paper-global.yml
Skins do not showWrong forwarding modeCheck <code>player-info-forwarding-mode = "modern"</code>
UUID changes between serversonline-mode is inconsistent across the backends<code>online-mode=false</code> on all backends and <code>online-mode: true</code> in paper-global
LuckPerms permissions are not in syncDifferent storage, or messaging-service is missingThe same MySQL connection on all servers and <code>messaging-service: sql</code>
The connection drops when a player switches serversBackend timeout or a network problemIncrease the <code>connection-timeout</code> and <code>read-timeout</code> values
velocity.toml changes are not appliedThe config has not been reloaded<code>/velocity reload</code> or restart the proxy
Forced hosts do not workWrong DNS records or a port mismatchCheck that every subdomain points to the same IP:25577
The backend can be connected to directlyserver-ip=127.0.0.1 is not set or the firewall is missingCheck server.properties and the firewall together

Useful Resources