Putting my son's PlayStation on a Java Minecraft server
My son plays Minecraft on a PlayStation; I wanted him on a Java world I host and control. Here's the whole Bedrock-to-Java bridge — and the DNS trap that eats an afternoon.
My son plays Minecraft on a PlayStation. I wanted him on a world I run — one where I choose the datapacks, keep the map for years, invite exactly the people we want, and where “the server” is a box in my own flat rather than someone else’s business model.
There’s one problem, and it’s a deep one. PlayStation runs Bedrock Edition. A self-hosted, plugin-and-datapack server runs Java Edition. The two speak completely different network protocols — and, to make it worse, a console won’t even let you type a server IP. So a cosy father-son project quietly turned into a small networking exercise.
It works now. He’s in, building next to school friends on a map I pre-generated for him. This is the whole thing written down — including every gotcha that cost me an afternoon, so it doesn’t cost you one.
The build this is based on: PaperMC on Ubuntu, self-hosted, a PS5 joining over the home LAN. Everything runs in a normal user account — no root except two clearly-marked steps.
What you’re actually building
Because the console is Bedrock and the server is Java, three pieces have to sit in between and translate:
PS5 (Bedrock)
│ 1. DNS redirect: a "featured server" hostname → your box
▼
BedrockConnect (UDP 19132) ── shows a menu so the console can pick your server
│ 2. transfer to your server's IP:port
▼
Geyser (UDP 19133) ── translates Bedrock ⇄ Java protocol
│ 3. connects as a Java client on localhost
▼
ViaVersion + ViaBackwards ── only if your Java version is newer than Geyser supports
│
▼
PaperMC (Java) (TCP 25565) ── your actual world
Java players (a laptop, a friend’s PC) connect directly to TCP 25565 and never touch any of the Bedrock plumbing. All of that machinery exists purely so a console — which can’t add a server by IP — can find its way to your world.
Read this first: the version-compatibility trap ⚠️
This is the single biggest source of pain, so it goes before anything else. The Bedrock-bridging tools — Geyser and ViaVersion — always lag a few weeks behind a brand-new Minecraft release.
- If you run the absolute newest Minecraft version, Geyser may not speak it yet.
- The fix: run Geyser at the newest version it supports, and add ViaVersion + ViaBackwards on the Java server so that older-protocol client (Geyser) can still connect to your newer server.
- On a bleeding-edge Paper build, the Geyser plugin can fail to load — it hooks Bukkit APIs that changed underneath it. Running Geyser standalone, as a separate process, sidesteps that entirely.
Rule of thumb: decide your Minecraft version by what the bridges support, not the other way around. I learned this backwards, of course.
Prerequisites
- A Linux host on your LAN (this uses Ubuntu; the commands are generic).
- SSH access to it.
- Your server’s LAN IP — I’ll call it
SERVER_IP(example:192.168.1.153). - Control of your router/DNS. This is where consoles get stuck; see Part 4.
- ~2 GB disk, 4 GB+ RAM free for a small world.
Throughout, replace SERVER_IP with your server’s actual LAN IP.
Part 1 — The Java server (PaperMC)
Paper is an optimized, plugin-capable Java server. Everything below installs into ~/minecraft with no root.
Install a matching JDK (no root)
Modern Minecraft needs a recent Java (1.20.5+ wants Java 21; some 2026 builds want Java 25). Grab a self-contained Temurin build from Adoptium rather than fighting system packages:
mkdir -p ~/minecraft/{server,jdk}
cd ~/minecraft
# Pick the Java version your Paper build requires (21 or 25). Example: Java 21:
wget -O jdk.tar.gz "https://api.adoptium.net/v3/binary/latest/21/ga/linux/x64/jre/hotspot/normal/eclipse"
tar -xzf jdk.tar.gz -C jdk --strip-components=1
~/minecraft/jdk/bin/java -version
Download Paper
Paper’s current API lives at fill.papermc.io. This grabs the latest build for a version:
VER="1.21.11" # <-- set to your target Minecraft version
BUILD=$(curl -s "https://fill.papermc.io/v3/projects/paper/versions/$VER/builds/latest")
URL=$(echo "$BUILD" | python3 -c "import sys,json;print(json.load(sys.stdin)['downloads']['server:default']['url'])")
curl -s -o ~/minecraft/server/paper.jar "$URL"
Accept the EULA and configure
cd ~/minecraft/server
echo "eula=true" > eula.txt
cat > server.properties <<'PROPS'
motd=My Server
server-port=25565
online-mode=false
enforce-secure-profile=false
white-list=false
level-name=world
difficulty=normal
gamemode=survival
max-players=10
view-distance=10
simulation-distance=8
spawn-protection=0
PROPS
online-mode=false(offline mode) is what lets Bedrock players — and any unauthenticated client — join without a paid Java account. Only do this on a private LAN, and never port-forward it to the internet: anyone who can reach the port could join as any name.enforce-secure-profile=falseavoids chat-signing kicks in offline mode.
A nicer world (optional but recommended)
Terralith is a datapack — server-side only, no client mods — that dramatically improves terrain generation. Download the zip matching your version from Modrinth and drop it in before first launch so the whole world uses it:
mkdir -p ~/minecraft/server/world/datapacks
# download Terralith_<version>.zip into that folder (keep it zipped)
Launch script + auto-start (no root)
Aikar’s JVM flags in a detached screen session, with a boot entry via your user crontab:
cat > ~/minecraft/server/start.sh <<'SCRIPT'
#!/usr/bin/env bash
set -euo pipefail
BASE="$HOME/minecraft"; JAVA="$BASE/jdk/bin/java"
cd "$BASE/server"
screen -list 2>/dev/null | grep -qE '\.mc[[:space:]]' && { echo "already running"; exit 0; }
screen -dmS mc "$JAVA" -Xms4G -Xmx4G \
-XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 \
-XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch \
-XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M \
-XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 \
-XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 \
-XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem \
-XX:MaxTenuringThreshold=1 -jar paper.jar --nogui
echo "started (screen 'mc'). Console: screen -r mc (detach: Ctrl-A then D)"
SCRIPT
chmod +x ~/minecraft/server/start.sh
# start on boot (no root needed)
( crontab -l 2>/dev/null; echo "@reboot /bin/bash $HOME/minecraft/server/start.sh >> $HOME/minecraft/boot.log 2>&1" ) | crontab -
bash ~/minecraft/server/start.sh
Attach the console with screen -r mc, detach with Ctrl-A then D, and stop cleanly by typing stop.
Java players can connect right now at SERVER_IP (port 25565). ✅ If your kid’s world is Java-only, you’re already done — the rest of this is entirely about dragging a console across the protocol gap.
Part 2 — Bridge Bedrock ⇄ Java (Geyser + Via)
ViaVersion + ViaBackwards (only if needed)
You need these only if your Java server is newer than the version Geyser emulates. Check Geyser’s supported versions. If your server matches, skip ahead. Otherwise, download both as plugins from Modrinth into ~/minecraft/server/plugins/ and restart:
mkdir -p ~/minecraft/server/plugins
# ViaVersion — lets newer clients join older servers
# ViaBackwards — lets OLDER clients (i.e. Geyser) join your NEWER server <-- the one you need
After restart the log should show ViaVersion detected server version: <yours>.
Geyser — standalone, not plugin
On a bleeding-edge Paper build, run Geyser as a standalone process to avoid the plugin load crash. Download the standalone build:
mkdir -p ~/minecraft/geyser
cd ~/minecraft/geyser
GV=$(curl -s https://download.geysermc.org/v2/projects/geyser | python3 -c "import sys,json;print(json.load(sys.stdin)['versions'][-1])")
GB=$(curl -s "https://download.geysermc.org/v2/projects/geyser/versions/$GV/builds/latest" | python3 -c "import sys,json;print(json.load(sys.stdin)['build'])")
curl -s -o Geyser.jar "https://download.geysermc.org/v2/projects/geyser/versions/$GV/builds/$GB/downloads/standalone"
# first run generates config.yml, then Ctrl-C
~/minecraft/jdk/bin/java -jar Geyser.jar
Configure Geyser
Edit ~/minecraft/geyser/config.yml:
bedrock:
port: 19133 # NOT 19132 — BedrockConnect needs 19132 (see Part 3)
remote:
address: 127.0.0.1 # your Paper server, same box
port: 25565
auth-type: offline # matches online-mode=false; Bedrock players join with their gamertag
Run it in its own screen session (make a start-geyser.sh like the server’s, using screen -dmS geyser ..., plus a matching @reboot crontab line).
Stopping Geyser: its console ignores
screen-injected text (it uses JLine). Stop it withscreen -S geyser -X quit, not by typingstop. I wasted ten minutes on this one.
At this point a Bedrock client that can enter a custom IP — a phone, a Windows PC — can already join at SERVER_IP:19133. Consoles still can’t. That’s the next two parts.
Part 3 — Let the console reach a custom server (BedrockConnect)
The console problem: PlayStation, Xbox, and Switch only let you join servers from their built-in Featured Servers list. There is no “add server by IP” box. BedrockConnect works around it: you point the console at a featured server (via DNS, Part 4), it lands on BedrockConnect instead, and BedrockConnect shows a menu where your server appears — then transfers the console straight to it.
BedrockConnect must listen on UDP 19132 (the port featured servers use), which is exactly why Geyser got moved to 19133.
mkdir -p ~/minecraft/bedrockconnect
cd ~/minecraft/bedrockconnect
curl -sL -o BedrockConnect.jar \
"https://github.com/Pugmatt/BedrockConnect/releases/latest/download/BedrockConnect-1.0-SNAPSHOT.jar"
# your server as a menu entry (points at Geyser on 19133)
cat > custom_servers.json <<JSON
[ { "name": "My World", "address": "SERVER_IP", "port": 19133 } ]
JSON
# run it (own screen session + @reboot, like the others)
~/minecraft/jdk/bin/java -DlogFile=true -jar BedrockConnect.jar \
custom_servers=custom_servers.json featured_servers=false user_servers=true
The log should say Loaded 1 custom servers and Server is now running: 0.0.0.0:19132.
Port map so far:
| Port | Service | Protocol |
|---|---|---|
| 25565 | Paper (Java) | TCP |
| 19132 | BedrockConnect | UDP |
| 19133 | Geyser | UDP |
Part 4 — The DNS redirect (the part that actually trips everyone up)
Here’s the crux. The console needs a featured server’s hostname to resolve to your box so it lands on BedrockConnect. You do that with a DNS server that answers those specific hostnames with SERVER_IP and forwards everything else normally.
The featured-server domains to redirect (these map to the console’s Featured list):
hivebedrock.network inpvp.net lbsg.net galaxite.net enchanted.gg
The Hive (geo.hivebedrock.network) is the reliable one to test with.
The catch: DNS lives on port 53 (needs root)
Binding port 53 requires root. There are three ways to do this; the third is the one that always works.
Option A — run your own DNS (dnsmasq)
Install dnsmasq and bind it to your LAN IP so it coexists with the system resolver:
sudo apt install -y dnsmasq
sudo tee /etc/dnsmasq.d/bedrock.conf >/dev/null <<'EOF'
listen-address=SERVER_IP
bind-interfaces
address=/hivebedrock.network/SERVER_IP
address=/inpvp.net/SERVER_IP
address=/lbsg.net/SERVER_IP
address=/galaxite.net/SERVER_IP
address=/enchanted.gg/SERVER_IP
server=8.8.8.8
server=1.1.1.1
EOF
sudo systemctl enable --now dnsmasq && sudo systemctl restart dnsmasq
dnsmasq’s address=/domain/IP is a wildcard — it covers geo.hivebedrock.network and every other subdomain. That robustness matters more than it looks. Then set the console’s Primary DNS to SERVER_IP.
Option B — do it on the router (and the gotchas)
If your router forces its own DNS, you can’t just point devices elsewhere. Two very common traps:
- Transparent DNS interception / “content filtering.” Many routers — UniFi with Content Filtering or Ad Blocking, most ISP gateways, Pi-hole setups — hijack all port-53 traffic and answer it themselves. So a query you explicitly aimed at your box never arrives, and you get the real server IP back. Symptom:
nslookup geo.hivebedrock.network SERVER_IPreturns the real Hive IPs. Fix: turn off content filtering / forced DNS on the network the console is on. - Router “local DNS records” often DON’T override public domains. On UniFi specifically, adding a Local DNS Record for
geo.hivebedrock.network→ your box does not work — the gateway keeps forwarding public domains upstream. Don’t rely on it.
Option C — the reliable rule: put the console on the same subnet as the server ✅
This is the one that always works. If the console and the server sit on the same subnet/VLAN, their traffic is switched directly and never passes through the router’s DNS interception or its inter-subnet firewall. So:
- Put the console on the same network as the server.
- Set that network’s DNS (or the console’s Primary DNS) to
SERVER_IP(your dnsmasq). - Done — no interception to fight, no cross-subnet UDP to get blocked.
Why cross-subnet fails: DNS and the Bedrock game both ride UDP. Routers frequently pass TCP and ping between subnets while silently dropping (or hijacking) UDP. “It can ping the server” is not proof the path works — a lesson I re-learned the hard way.
Verify the redirect actually works
From a device on the console’s network — not the server itself:
nslookup geo.hivebedrock.network SERVER_IP
# EXPECT: Address: SERVER_IP (redirect works)
# BAD: real Hive IPs (interception, or DNS not reaching your box)
Or fire a fresh, never-cached subdomain — only a working wildcard record can answer it:
nslookup randomxyz123.hivebedrock.network SERVER_IP # EXPECT: SERVER_IP
Part 5 — PlayStation setup
- Settings → Network → Set Up Internet Connection → (your Wi-Fi/LAN) → Custom:
- IP Settings: Automatic
- DHCP Host Name: Do Not Specify
- DNS: Manual → Primary
SERVER_IP, Secondary8.8.8.8 - MTU: Automatic · Proxy: Do Not Use → Save and test.
- Launch Minecraft → Play → Servers → open The Hive.
- Instead of The Hive you’ll get the BedrockConnect menu → pick “My World”.
- You’re in.
After any DNS change, restart the console (or fully close Minecraft) to clear cached DNS. The number of times “it’s broken” was really “the console cached the old lookup” is embarrassing.
Part 6 — Troubleshooting (real failures, in order of likelihood)
| Symptom | Cause | Fix |
|---|---|---|
nslookup ... SERVER_IP returns real IPs | Router is hijacking port 53 (content filtering / forced DNS) | Disable content filtering; or put the console on the server’s subnet |
| Console connects to the real featured server | DNS not reaching your box (cross-subnet UDP dropped, or wrong DNS set) | Same-subnet rule; verify with the nslookup checks above |
| ”You are not whitelisted on this server!” | Server whitelist is on | In the console: whitelist off, and set white-list=false in server.properties |
| Bedrock name has spaces but Java shows underscores | Normal — Geyser converts gamertag spaces → _ in offline mode | Nothing to fix; op them by the underscored name |
| Geyser plugin crashes on enable | Bleeding-edge Paper API changed | Use standalone Geyser (Part 2) |
| Only some featured servers redirect | You only redirected some domains | Add all five domains (Part 4) |
Diagnostic gold: if you run your own dnsmasq, temporarily turn on query logging to see whether the console’s lookups even reach you:
echo 'log-queries' | sudo tee /etc/dnsmasq.d/log.conf && sudo systemctl restart dnsmasq
journalctl -u dnsmasq -f | grep -iE 'hivebedrock|from '
# No lines from the console's IP = its DNS isn't reaching you at all.
That one command answers the question you actually care about — is the console even talking to me? — and it’s the fastest way to end the guessing.
Part 7 — Performance tuning
- Pre-generate the world. The single biggest win against exploration lag, on Java and Bedrock. Install the Chunky plugin, then in the console:
chunky radius 2000 chunky start chunky progress # check ETA; chunky pause / continue to control - Paper config (
config/paper-world-defaults.yml):max-entity-collisions: 2,update-pathfinding-on-block-update: false,max-auto-save-chunks-per-tick: 8. - spigot.yml
entity-activation-range: loweranimals/monsters/miscfor fewer active mobs. - Geyser
config.yml: on a LAN,compression-level: 1trims CPU and latency to Bedrock clients. - CPU governor (Linux): pin to
performancefor steadier tick times under heavy generation:sudo tee /etc/systemd/system/cpu-performance.service >/dev/null <<'EOF' [Unit] Description=Set CPU governor to performance After=multi-user.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/bin/bash -c 'echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor' [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload && sudo systemctl enable --now cpu-performance.service - On the PlayStation: set Render Distance to 8–10. Consoles are happiest there, and it’s capped by the server’s
view-distanceanyway. (No shaders on console, so render distance is the main lever.)
Quick reference
Four long-running processes, each in its own screen session with a matching @reboot crontab line:
mc → Paper server (TCP 25565)
geyser → Geyser standalone (UDP 19133)
bconnect → BedrockConnect (UDP 19132)
dnsmasq → DNS redirect (UDP 53, system service via sudo)
screen -r <name> attaches · Ctrl-A then D detaches · screen -ls lists.
The rules I’d tattoo on my arm:
- Pick your Minecraft version by what Geyser/Via support, not the newest release.
- Run Geyser standalone on bleeding-edge servers.
- The DNS redirect is where consoles get stuck — same subnet beats every router quirk.
- DNS and the game are UDP; “it can ping” proves nothing.
- Offline mode is LAN-only — never port-forward it.
Was it worth it?
An afternoon of protocol archaeology for a nine-year-old to place the same blocks he could have placed on a random public server — put like that, it sounds absurd. But it isn’t the blocks. It’s that the world is ours: I picked the terrain generator, I pre-generated the map so it never stutters when he sprints somewhere new, and the only people on it are the ones we invited. When school friends come over with a laptop, they join the exact same world from Java. Nothing to pay, nothing to phone home, a map that will still be there in five years.
The honest lesson underneath all the plumbing is smaller than the guide makes it look: decide your version by what the bridges support, and put the console on the same subnet as the server. Almost every dead end I hit traces back to ignoring one of those two.
A security note, because it bears repeating: this whole setup assumes a private LAN. Keep every port — 25565, 19132, 19133, 53 — off the public internet. Offline mode means anyone who reaches the port can log in as anyone. For remote friends, put them on a VPN (Tailscale or WireGuard) rather than port-forwarding an offline-mode server. A world that’s yours is only worth it if it stays that way.