Minecraft VM runbook (VM 103)¶
Operating guide for the Bedrock server VM (minecraft, VM 103, 192.168.1.224, WireGuard 10.10.0.2).
Three native Bedrock Dedicated Server (BDS) instances run as systemd services under the
minecraft user. Players reach them through the VPS (mc.byers.cc + per-server UDP port) —
see VPS and WireGuard.
Golden rules
- Back up before you change anything. Before touching any world or config, make a
timestamped tarball in
~/manual-backups/and confirm it exists. - Never delete anything under
bedrock_backups/,dbbackups/, or anyworlds/directory — add or replace only, deliberately. - Don't restart all three servers at once — orphaned
bedrock_serverprocesses steal each other's ports (see Port race). - Never print secrets: WireGuard private keys,
~/.cloudflared/credentials, anything in.ssh/. Locations only.
Access¶
| From | Command |
|---|---|
| LAN / tailnet (ops is subnet router) | ssh minecraft |
| Anywhere, via the VPS jump host | ssh -J vps minecraft@10.10.0.2 |
Key-only auth everywhere. The Proxmox host is root@192.168.1.138 if you need the hypervisor.
How players and backups flow¶
flowchart LR
players["Players"] -- "mc.byers.cc<br/>UDP 19132 / 19134 / 19136" --> npm["VPS · NPM<br/>stream passthrough"]
npm -- "WireGuard<br/>10.10.0.1 → 10.10.0.2" --> bds["VM 103<br/>three BDS instances"]
flowchart LR
cron["03:00<br/>root cron"] --> a["autoupdate.sh:<br/>stop servers"] --> t["tarball worlds<br/>→ ~/bedrock_backups"] --> u["update BDS<br/>if release"] --> r["restart<br/>servers"] --> p["prune<br/>14d + 8w"]
p --> k["03:30 kopia<br/>backup@mc → NAS<br/>30d + 52w"]
The 02:30–03:30 window is a no-touch zone — the chain above owns the servers during it.
The three worlds¶
| Directory | systemd service | In-game name | Ports (v4·v6) | Active world | Mode |
|---|---|---|---|---|---|
creative-world |
minecraft-creative-world |
"Rory and Elliott Creative World" | 19132·19133 | Creative level |
Creative, open |
rorys-friends-creative |
minecraft-rorys-friends-creative |
"Rorys Friends Creative" | 19134·19135 | ipad-world |
Creative, allowlist on |
survival-classic |
minecraft-survival-classic |
"Survival World" | 19136·19137 | Survival level |
Survival, allowlist on |
- 19132 is the default Bedrock port — players who don't type a port land on the creative world.
- Every
server.propertiesneedsenable-lan-visibility=true.falseblanks the MOTD and nobody can join (see troubleshooting). - History note:
rorys-friends-creativeis the ipad-world clone (renamed from the oldsurvival-worldslot 2026-06-13, keeping port 19134);survival-classicis the original survival world, resurrected the same day. - Services run with
StandardInput=null— there is no interactive console. All three areenabled(start on boot).
Allowlist changes: prefer the mc-admin panel; manual fallback is editing
allowlist.json in the server dir (validate the JSON) then
sudo systemctl restart minecraft-<dir>. Adding a whole new server: see
New world.
The 03:00 no-touch window¶
Do not touch services or worlds between roughly 02:30 and 03:30
/home/minecraft/autoupdate.sh fires at 03:00 daily as a root cron. It stops every
minecraft-* service, rsyncs new BDS binaries, restarts them, takes the nightly world
tarballs, and prunes old ones. Service operations or world edits during this window race
the updater and are how worlds get corrupted. The nightly vzdump (02:00) and the kopia
snapshot (03:30) bracket it — treat 02:00–03:30 as maintenance-owned time.
What autoupdate.sh preserves: worlds/, server.properties, allowlist.json,
permissions.json — it replaces only binaries and vanilla packs, and since 2026-06-17 it
re-asserts enable-lan-visibility=true after each update. Its SERVER_DIRECTORIES=(...)
array must list every server directory — keep it in sync when servers change.
Cleanup candidate: update_log.txt
autoupdate.sh appends to update_log.txt with no rotation — it is ~64 MB and
growing. Safe to truncate or put under logrotate; nothing reads it programmatically.
Do NOT touch¶
/home/minecraft/autoupdate.sh— root cron, 03:00. Edit only theSERVER_DIRECTORIESarray when adding/removing a server, and take a timestamped.bakfirst./home/minecraft/create_mc_world.sh— provisions new instances + systemd units./home/minecraft/addon_install.py— addon installer pipeline./etc/systemd/system/minecraft-*.service— service definitions.- Anything under
bedrock_backups/,dbbackups/, orworlds/. - cron itself (
crontab,/etc/cron.*).
Backup chain — as of 2026-08-12¶
The old chain is dead — do not trust older docs
PBS is decommissioned (LXC 101 stopped; destroy after 9 Sep 2026) and the VPS
rsync leg was fully retired 2026-08-11 — no push, no VPS retention, old tarballs
deleted. Any doc mentioning pbs-nas or minecraft_backups/ on the VPS is stale.
The chain in words, innermost to outermost:
- On the VM —
autoupdate.sh(03:00) tars each server's world intobedrock_backups/<server>/backup_*.tar.gz(root-owned) and prunes nightly to 14 daily + 8 weekly. Ad-hoc backups you make by hand go in~/manual-backups/(owned byminecraft). The mc-admin restore menu reads these VM-local tarballs only — nothing off-VM. - Off-VM history (kopia) —
backup@mcsnapshots/home/minecraft/bedrock_backupsdaily at 03:30 to the kopia repo on the NAS, via the kopia server on ops (192.168.1.190:51515, TLS, per-machine users). Retention 30 daily + 52 weekly — this is where you go for anything older than two weeks. Details in Backups overview. - Whole-VM image (vzdump) — nightly 02:00 Proxmox vzdump of VMs 100/102/103 to the NAS over NFS, zstd, keep 7 daily + 4 weekly. Disaster recovery for the whole VM, not for a single world.
- Offsite — the latest vzdump set only syncs to Google Drive at 04:00 (rclone,
--drive-use-trash=false --delete-before). See Offsite.
The kopia world history has no cloud copy
Only the vzdump images reach Google Drive. The kopia repo (including 52 weeks of world tarballs) lives solely on the NAS — owner's accepted risk; B2 at ~$0.40/mo is the standing fix. If the NAS burns, world history beyond the latest vzdump is gone.
Restore procedures live in Restores; the quick path for "roll a
world back a few days" is the mc-admin Backups tab (it tars the current world to
manual-backups/ before swapping, and never touches configs or allowlists).
Troubleshooting¶
Servers ping but nobody can join ("Kelp")¶
Clients spin then time out, disconnect codeword "Kelp"; RakNet pong is 33 bytes with a blank
MOTD. Cause: something reset enable-lan-visibility=false in server.properties (a BDS
auto-update did exactly this in June 2026). Fix: set it back to true in each
server.properties and restart. Healthy pong is ~140 bytes starting MCPE;<name>;….
Diagnose UDP reach from the VPS:
sudo timeout 30 tcpdump -ni any "udp and portrange 19132-19137" while a client joins.
A port answers as the wrong server, or "server full"¶
Restarting several BDS instances at once leaves orphaned processes holding ports.
Check: ss -ulnp | grep -E ':1913[2-7]' — every port must map to a distinct pid.
Fix: stop all three services, pkill -9 -u minecraft -f bedrock_server, then start them
one at a time ~8 s apart, creative (19132) first. Never restart them together.
Auto-update outran the clients¶
autoupdate.sh pulls the newest BDS nightly; lagging client apps may silently fail to
join. Option: pin LATEST_FILENAME in autoupdate.sh until clients catch up (with
approval — see do-not-touch list).