13 KiB
DirtSimpleP2P
DirtSimpleP2P is a Minecraft networking plugin that lets a public BungeeCord or Waterfall proxy reach backend Paper/Spigot servers that are behind NAT.
It is designed for server owners who want to host a backend Minecraft server at home or on a private network without opening the backend Minecraft port on their router.
The backend server connects outbound to the public proxy. Bungee/Waterfall then connects to a local port on the proxy machine, and DirtSimpleP2P carries the Minecraft TCP connection through the tunnel.
Features
- One jar for both the proxy and backend server
- No home router port forwarding for backend Minecraft servers
- NAT-safe outbound tunnel from backend to proxy
- Multiple simultaneous Minecraft player connections
- Multiplexed framed TCP protocol
- Secret-token authentication
- Automatic token generation
- Optional TLS encryption
- Self-signed TLS certificate generation
- TLS certificate pinning on backend servers
- Fast reconnect with heartbeat checks
- Multiple public proxies can connect to one backend
- One proxy can expose multiple named backend servers
/dsp2p statusand/dsp2p doctorcommands- Temporary browser setup wizard with console-generated login key
- Simple
config.propertiessetup
Requirements
- Java 21
- Maven 3.8+
- BungeeCord or Waterfall for the public proxy side
- Paper or Spigot for the backend server side
How It Works
Normal Minecraft proxy setup expects Bungee/Waterfall to connect directly to a backend server.
That does not work well when the backend server is at home behind NAT, unless you forward ports on the home router.
DirtSimpleP2P avoids that:
- The proxy server runs DirtSimpleP2P in
frontendmode. - The backend server runs DirtSimpleP2P in
backendmode. - The backend opens an outbound tunnel to the public proxy.
- The proxy exposes a local port such as
127.0.0.1:25566. - Bungee/Waterfall sends players to that local port.
- DirtSimpleP2P carries the TCP traffic through the tunnel to the backend server.
The public proxy needs an open tunnel port. The home/backend router does not need a Minecraft port forward.
Build
From the project folder:
mvn clean package
The plugin jar is created at:
plugin/target/DirtSimpleP2P-0.1.0-SNAPSHOT.jar
Use this same jar on both the proxy and backend server.
Basic Setup
You need:
- A public BungeeCord/Waterfall proxy, usually on a VPS
- A backend Paper/Spigot server, which can be at home or on a private network
1. Install On The Proxy
Put the jar in your BungeeCord/Waterfall plugins folder:
DirtSimpleP2P-0.1.0-SNAPSHOT.jar
Start the proxy once, then stop it.
DirtSimpleP2P creates:
plugins/DirtSimpleP2P/config.properties
2. Configure The Proxy
Use this on the BungeeCord/Waterfall side:
role=frontend
node.name=bungee-frontend
tunnel.listenHost=0.0.0.0
tunnel.listenPort=24445
tunnel.authToken=GENERATED_AUTOMATICALLY
tunnel.connectTimeoutMillis=5000
tunnel.heartbeatIntervalMillis=2000
tunnel.heartbeatTimeoutMillis=8000
tunnel.heartbeatMissesBeforeDisconnect=4
tunnel.reconnectInitialMillis=250
tunnel.reconnectMaxMillis=10000
tunnel.tls.enabled=false
tunnel.tls.allowInsecure=true
tunnel.tls.autoGenerate=true
tunnel.tls.keyStore=certs/frontend.p12
tunnel.tls.keyStorePassword=
tunnel.tls.requireClientAuth=false
webwizard.bindHost=127.0.0.1
webwizard.port=8765
routes=minecraft
route.minecraft.frontendBindHost=127.0.0.1
route.minecraft.frontendBindPort=25566
route.minecraft.backendNode=paper-backend
Notes:
tunnel.listenPortis the public tunnel port that the backend connects to.tunnel.authTokenis generated automatically on first start.- Copy the generated token to the backend config.
route.minecraft.frontendBindPortis the local port Bungee/Waterfall should use for this backend.route.minecraft.backendNodemust match the backend server'snode.name.
3. Point Bungee/Waterfall At The Local Tunnel
In your BungeeCord/Waterfall server config, set the backend address to:
127.0.0.1:25566
Do not put your home server IP in the Bungee/Waterfall config. Bungee connects locally, and DirtSimpleP2P carries the traffic through the tunnel.
4. Install On The Backend
Put the same jar in your Paper/Spigot plugins folder:
DirtSimpleP2P-0.1.0-SNAPSHOT.jar
Start the backend server once, then stop it.
DirtSimpleP2P creates:
plugins/DirtSimpleP2P/config.properties
5. Configure The Backend
Use this on the Paper/Spigot side:
role=backend
node.name=paper-backend
frontends=proxy1
frontend.proxy1.connectHost=YOUR_PROXY_IP_OR_DOMAIN
frontend.proxy1.connectPort=24445
frontend.proxy1.tls.pinnedCertificateSha256=
tunnel.authToken=PASTE_THE_PROXY_TOKEN_HERE
tunnel.connectTimeoutMillis=5000
tunnel.heartbeatIntervalMillis=2000
tunnel.heartbeatTimeoutMillis=8000
tunnel.heartbeatMissesBeforeDisconnect=4
tunnel.reconnectInitialMillis=250
tunnel.reconnectMaxMillis=10000
tunnel.tls.enabled=false
tunnel.tls.allowInsecure=true
tunnel.tls.trustOnFirstUse=true
webwizard.bindHost=127.0.0.1
webwizard.port=8765
routes=minecraft
route.minecraft.backendTargetHost=127.0.0.1
route.minecraft.backendTargetPort=25565
Change:
frontend.proxy1.connectHostto your public proxy IP or domainfrontend.proxy1.connectPortto the proxytunnel.listenPorttunnel.authTokento the token from the proxy configroute.minecraft.backendTargetPortif your Paper/Spigot server is not listening on25565
6. Start Everything
Start the public proxy first.
Then start the backend Paper/Spigot server.
If the tunnel connects, the proxy log should show that a backend authenticated. Players connect to your normal public proxy address.
TLS
TLS is optional, but recommended for real deployments.
On the proxy:
tunnel.tls.enabled=true
tunnel.tls.allowInsecure=false
tunnel.tls.autoGenerate=true
When TLS is enabled, the proxy can generate:
plugins/DirtSimpleP2P/certs/frontend.p12
On the backend:
tunnel.tls.enabled=true
tunnel.tls.allowInsecure=false
tunnel.tls.trustOnFirstUse=true
On the first successful TLS connection, the backend saves the proxy certificate fingerprint:
frontend.proxy1.tls.pinnedCertificateSha256=
After that, the backend only trusts that same certificate.
For stricter security, copy the certificate fingerprint from the proxy log and paste it into the backend config before the first connection.
Multi-Server Setups
Two Public Proxies, One Backend
Use this when two public BungeeCord/Waterfall proxies should both reach the same backend server.
Backend config:
role=backend
node.name=survival-backend
frontends=proxy1,proxy2
frontend.proxy1.connectHost=proxy1.example.com
frontend.proxy1.connectPort=24445
frontend.proxy1.tls.pinnedCertificateSha256=
frontend.proxy2.connectHost=proxy2.example.com
frontend.proxy2.connectPort=24445
frontend.proxy2.tls.pinnedCertificateSha256=
routes=minecraft
route.minecraft.backendTargetHost=127.0.0.1
route.minecraft.backendTargetPort=25565
Each proxy config:
role=frontend
node.name=proxy1
routes=minecraft
route.minecraft.frontendBindHost=127.0.0.1
route.minecraft.frontendBindPort=25566
route.minecraft.backendNode=survival-backend
Each proxy listens for tunnel connections. The backend connects outbound to both proxies.
One Proxy, Multiple Backends
Use this when one proxy should expose more than one backend server.
Proxy config:
role=frontend
node.name=main-proxy
routes=survival,lobby
route.survival.frontendBindHost=127.0.0.1
route.survival.frontendBindPort=25566
route.survival.backendNode=survival-backend
route.lobby.frontendBindHost=127.0.0.1
route.lobby.frontendBindPort=25567
route.lobby.backendNode=lobby-backend
Bungee/Waterfall server entries:
survival -> 127.0.0.1:25566
lobby -> 127.0.0.1:25567
Survival backend config:
role=backend
node.name=survival-backend
frontends=main
frontend.main.connectHost=YOUR_PROXY_IP
frontend.main.connectPort=24445
routes=survival
route.survival.backendTargetHost=127.0.0.1
route.survival.backendTargetPort=25565
Lobby backend config:
role=backend
node.name=lobby-backend
frontends=main
frontend.main.connectHost=YOUR_PROXY_IP
frontend.main.connectPort=24445
routes=lobby
route.lobby.backendTargetHost=127.0.0.1
route.lobby.backendTargetPort=25565
All nodes in the same private tunnel group must use the same tunnel.authToken.
Commands
Run from the server console or in game:
/dsp2p status
Shows role, node name, whether the tunnel is connected, connected tunnel count, active streams, TLS state, and the latest event.
/dsp2p doctor
Checks the config and prints setup/security hints.
/dsp2p webwizard
Toggles a temporary browser setup wizard.
When the wizard starts, the server console prints:
Please sign in with this key: <temporary-key>
Open the printed URL in a browser and sign in with the temporary key.
The wizard uses a dark control-center UI. It detects whether it is running on Paper/Spigot or BungeeCord/Waterfall and only shows the setup sections that apply to that server type.
Depending on the server type, it provides guided sections for:
- server mode and node name
- proxy listener settings
- backend proxy connections
- Minecraft routes
- tunnel token and TLS
- heartbeat and reconnect settings
- advanced raw config keys
You can add or remove proxy endpoints, backend routes, and advanced config keys directly from the web UI.
The web UI includes:
Save ConfigSave + Reload DirtSimpleP2PReload Current ConfigGenerate New Token
Reloading from the wizard restarts the DirtSimpleP2P tunnel agent with the latest config. It does not run a full Minecraft server reload.
Run /dsp2p webwizard again to stop the wizard when setup is complete.
By default, the wizard binds to:
webwizard.bindHost=127.0.0.1
webwizard.port=8765
Keep it on 127.0.0.1 unless you understand the risk of exposing a config editor over the network.
Permission:
dirtsimplep2p.command
On Paper/Spigot, the permission defaults to server operators. On BungeeCord/Waterfall, give this permission to staff who should see tunnel diagnostics.
Firewall Notes
On the public proxy/VPS, allow the tunnel port:
24445/tcp
On the home router, you do not need to forward the backend Minecraft port:
25565/tcp
The backend server must be allowed to make outbound TCP connections to the proxy.
Reconnect Behavior
DirtSimpleP2P is tuned to detect real drops quickly while still tolerating short latency spikes.
Default behavior:
- Heartbeat every
2seconds - Status warning if heartbeats are delayed
- Disconnect after about
8seconds without tunnel traffic - First reconnect retry after about
250ms - Exponential backoff up to
10seconds while the proxy is unreachable
Useful config values:
tunnel.heartbeatIntervalMillis=2000
tunnel.heartbeatTimeoutMillis=8000
tunnel.heartbeatMissesBeforeDisconnect=4
tunnel.reconnectInitialMillis=250
tunnel.reconnectMaxMillis=10000
If your network has frequent latency spikes, increase:
tunnel.heartbeatMissesBeforeDisconnect=5
Troubleshooting
Token Problems
The token must match on both sides.
If the plugin generated a token on the proxy, copy that generated value into the backend config.
Backend Says Connection Refused
Check that:
- The proxy server is running
- DirtSimpleP2P started on the proxy
frontend.proxy1.connectHostpoints to the proxy IP or domainfrontend.proxy1.connectPortmatches the proxytunnel.listenPort- The proxy firewall allows the tunnel port
Bungee Cannot Reach The Backend
Make sure the Bungee/Waterfall backend server entry points to the local tunnel port:
127.0.0.1:25566
Also check that the backend Paper/Spigot server is running and the tunnel is connected.
Players Disconnect Or Freeze
Check both server logs for:
- reconnect messages
- heartbeat timeout messages
- stream reset messages
- backend target connection failed
Security Notes
Keep tunnel.authToken private. Anyone with the token can attempt to authenticate to the tunnel.
For local testing, insecure mode is available:
tunnel.tls.enabled=false
tunnel.tls.allowInsecure=true
For real deployments, enable TLS:
tunnel.tls.enabled=true
tunnel.tls.allowInsecure=false
Trust-on-first-use is convenient, but the first TLS connection is the sensitive moment. For the strictest setup, manually copy the proxy certificate fingerprint into the backend config before the first connection.
The Web Wizard is intended as a temporary setup tool. It uses a random key printed to the server console and should be stopped when configuration is complete.
The reload button reloads DirtSimpleP2P itself. It intentionally does not run Paper/Bukkit /reload, because full server reloads are risky on production Minecraft servers.