BungeeCord is a proxy that sits in front of multiple Minecraft servers and lets players switch between them without disconnecting. One IP, multiple servers — lobby, survival, minigames, whatever.

This guide uses Waterfall, which is Paper's maintained BungeeCord fork. BungeeCord still works but Waterfall gets more fixes and is what most people run now.

When you actually need this

A proxy network makes sense when you have multiple separate game modes that need to feel like one server. If you just want one survival server, don't bother — it adds complexity and an extra process for no gain.

Architecture

Players connect to:
  Waterfall proxy (:25565, public)
    ├── lobby  (Paper, :25566, internal only)
    ├── survival (Paper, :25567, internal only)
    └── minigames (Paper, :25568, internal only)

The backends are never exposed to the internet. Only the proxy port is public. This is important.

Set up the backend servers first

Each backend is a normal Paper server. Set them up following the Paper setup guide. Then make two changes on each backend:

1. server.properties — set a unique port and disable online-mode:

server-port=25566
online-mode=false

online-mode=false is required because authentication happens at the proxy. The backend can't do it again. This means your firewall rules matter — if a backend is reachable from the internet with online-mode off, anyone can connect without a valid account.

2. spigot.yml — enable BungeeCord IP forwarding:

settings:
  bungeecord: true

This passes the real player IP and UUID through the proxy. Without it, all players appear to come from 127.0.0.1 and UUID-based permissions break.

Restart each backend after these changes.

Install Waterfall

sudo useradd -r -m -d /opt/waterfall -s /bin/bash waterfall
sudo mkdir -p /opt/waterfall
# Download from papermc.io/downloads/waterfall
sudo curl -o /opt/waterfall/waterfall.jar "https://api.papermc.io/v2/projects/waterfall/versions/1.21/builds/BUILD/downloads/waterfall-1.21-BUILD.jar"
sudo chown -R waterfall:waterfall /opt/waterfall

First run to generate config:

sudo -u waterfall java -jar /opt/waterfall/waterfall.jar

Stop it with Ctrl+C after it finishes starting.

config.yml

The main config lives at /opt/waterfall/config.yml. The important parts:

player_limit: -1
ip_forward: true

listeners:
- query_port: 25577
  motd: '&bMy Network'
  tab_list: GLOBAL_PING
  query_enabled: false
  proxy_protocol: false
  forced_hosts: {}
  ping_passthrough: false
  priorities:
  - lobby
  bind_local_address: true
  host: 0.0.0.0:25565
  max_players: 200
  tab_size: 60
  force_default_server: false

servers:
  lobby:
    motd: '&bLobby'
    address: 127.0.0.1:25566
    restricted: false
  survival:
    motd: '&2Survival'
    address: 127.0.0.1:25567
    restricted: false

groups: {}

permissions:
  default:
  - bungeecord.command.server
  - bungeecord.command.list
  admin:
  - bungeecord.command.alert
  - bungeecord.command.end
  - bungeecord.command.ip
  - bungeecord.command.reload

Key settings:

  • ip_forward: true — must match bungeecord: true in spigot.yml on backends
  • priorities — first server in the list is where new players land
  • host: 0.0.0.0:25565 — the public-facing port
  • Backend addresses use 127.0.0.1 — they're local only

systemd service for Waterfall

[Unit]
Description=Waterfall Minecraft Proxy
After=network.target

[Service]
Type=simple
User=waterfall
WorkingDirectory=/opt/waterfall
ExecStart=/usr/bin/java -Xms256M -Xmx512M -jar /opt/waterfall/waterfall.jar
Restart=on-failure
RestartSec=10s
StartLimitBurst=3
StartLimitIntervalSec=60s

[Install]
WantedBy=multi-user.target

Waterfall doesn't need much memory — 256-512MB is plenty. The backends need the RAM.

sudo systemctl daemon-reload
sudo systemctl enable waterfall
sudo systemctl start waterfall
sudo journalctl -u waterfall -f

Firewall — this part matters

Only the proxy port should be public. Backend ports must be blocked from the internet:

# Allow proxy port
sudo ufw allow 25565/tcp comment "Minecraft proxy"

# Block backend ports from external access
# (if backends are on the same host, they're already on loopback — fine)
# If backends are on separate hosts, use private network IPs and block public access:
sudo ufw deny 25566/tcp
sudo ufw deny 25567/tcp
sudo ufw deny 25568/tcp

If backends are on separate machines, put them on a private network and bind server-ip= in server.properties to the private interface only, not 0.0.0.0.

Test the connection

sudo journalctl -u waterfall -f

Connect with a Minecraft client to your server IP. You should land on the lobby. Use /server survival to switch servers (if the default permissions are set). Players with op can always use /server.

Common issues

"Your account is not premium" — backend has online-mode=true, should be false.

All players show as the same IP in backend logsbungeecord: true not set in spigot.yml, or ip_forward: false in Waterfall config.

Can't connect to backend directly but proxy works — correct, that's expected. Backends don't need to be reachable directly.

Proxy connects but immediately drops — check that the backend is actually running and listening on the configured port: ss -tlnp | grep 25566

Plugins

Waterfall has its own plugin folder (/opt/waterfall/plugins/) for proxy-level plugins. These run on the proxy, not on the backends. Common ones: LuckPerms (network-wide permissions), ChatBridge (cross-server chat). Most game plugins still go on the individual backend servers.


Related posts