𝔩𝔢𝔩𝕠𝔭𝔢𝔷
Theme

Homelab

Minecraft Server Management

Managing Your Minecraft Kubernetes Server: RCON, Whitelist, and Backups

Overview

Quick reference for managing your Minecraft Kubernetes server. Covers log viewing, RCON console commands for player and whitelist management, world data operations, backup procedures, plugin administration, and common diagnostics.

Before You Begin

Prerequisites

View Logs

Live Logs

kubectl logs -n minecraft -l app=minecraft -c minecraft -f

Recent Logs

kubectl logs -n minecraft -l app=minecraft -c minecraft --tail=50
kubectl logs -n minecraft -l app=minecraft -c minecraft | grep -iE "(error|exception|failed)"

Access RCON Console

Minecraft commands1 for server administration. See Paper documentation2 for Paper-specific administration.

Interactive Session

kubectl exec -it -n minecraft deploy/minecraft -- rcon-cli

Single Command

kubectl exec -n minecraft deploy/minecraft -- rcon-cli "<command>"
Note

If rcon-cli isn't available, attach to the container directly for server console access.

Player Management

# Make player an operator
rcon-cli "op <player>"

# Remove operator status
rcon-cli "deop <player>"

# List online players
rcon-cli "list"

# Kick player
rcon-cli "kick <player> <reason>"

# Ban player
rcon-cli "ban <player> <reason>"

Whitelist

# Enable whitelist
rcon-cli "whitelist on"

# Disable whitelist
rcon-cli "whitelist off"

# Add player to whitelist
rcon-cli "whitelist add <player>"

# Remove player from whitelist
rcon-cli "whitelist remove <player>"

# List whitelisted players
rcon-cli "whitelist list"

# Reload whitelist from file
rcon-cli "whitelist reload"

Find Players Who Tried to Join

kubectl logs -n minecraft -l app=minecraft -c minecraft --tail=100 | grep -i "not whitelisted"
Note

Bedrock players join the Java server with a . prefix (e.g., .BedrockPlayer). Vanilla whitelist add .BedrockPlayer fails because Paper validates the name against the Mojang API. See Whitelist Bedrock Players for the working procedure.

Teleportation

# Teleport player to you
rcon-cli "tp <player> <your-username>"

# Teleport to coordinates
rcon-cli "tp <player> <x> <y> <z>"

# Teleport to another player
rcon-cli "tp <player1> <player2>"

Game Settings

# Change gamemode
rcon-cli "gamemode survival <player>"
rcon-cli "gamemode creative <player>"
rcon-cli "gamemode spectator <player>"

# Set time
rcon-cli "time set day"
rcon-cli "time set night"

# Toggle weather
rcon-cli "weather clear"
rcon-cli "weather rain"

Spawn Points

# Set world spawn at your location
rcon-cli "setworldspawn"

# Set world spawn at coordinates
rcon-cli "setworldspawn <x> <y> <z>"

# Set world spawn at a player's current location
rcon-cli "execute at <player> run setworldspawn"

# Set individual player's spawn point
rcon-cli "spawnpoint <player>"

# Set player spawn at another player's location
rcon-cli "execute at <player1> run spawnpoint <player2> ~ ~ ~"

Announcements

# Simple broadcast (shows as [Server] in chat)
rcon-cli "say Server restarting in 5 minutes!"

# Formatted chat message
rcon-cli 'tellraw @a {"text":"Important announcement","color":"red"}'

# Big title on screen (set subtitle first if needed)
rcon-cli 'title @a subtitle {"text":"Enjoy your stay","color":"yellow"}'
rcon-cli 'title @a title {"text":"Welcome!","color":"gold"}'

Server Control

# Save world
rcon-cli "save-all"

# Stop server (use with caution)
rcon-cli "stop"

Whitelist Bedrock Players

Paper's whitelist add does a Mojang API lookup, which fails for Bedrock-only gamertags (they have no Mojang account). Paper also checks the whitelist by UUID at connection time, and Floodgate-bridged Bedrock players join with a UUID derived from the player's Xbox XUID — not a Mojang UUID. So Bedrock entries are appended to whitelist.json directly with the computed Floodgate UUID, then the in-game whitelist is reloaded.

Two name transforms happen between the user's Xbox gamertag and the value stored in whitelist.json:

  • URL encoding for the XUID lookup — Geyser's API takes the gamertag as a URL path component, so spaces (and other reserved characters) must be percent-encoded.
  • Space-to-underscore for the server-side name — Floodgate replaces spaces with underscores when presenting the player to the server. The whitelist entry must use the underscored, dot-prefixed form (e.g. raw basic killer 20.basic_killer_20).

Java players with the same gamertag are unaffected — whitelist add <player> works for them regardless of whether a Bedrock entry exists for the same name.

Capture the Gamertag

RAW_GAMERTAG="<gamertag>"
SERVER_NAME=".$(echo "$RAW_GAMERTAG" | tr ' ' '_')"
echo "Server name: $SERVER_NAME"

Expected: Server name: .<underscored-gamertag> — e.g. raw basic killer 20 becomes .basic_killer_20. For single-word gamertags the tr pass is a no-op and you get .<gamertag> unchanged.

Look Up the XUID

ENCODED=$(printf '%s' "$RAW_GAMERTAG" | jq -sRr @uri)
curl -sSL "https://api.geysermc.org/v2/xbox/xuid/${ENCODED}"

Expected: {"xuid":"<number>"} if the gamertag is in Geyser's cache.

Note

A {"message":"Unable to find user in our cache..."} response means the gamertag isn't cached yet. Geyser's cache populates on connection attempts — have the player try connecting on Bedrock once, then re-run the lookup.

Compute the Floodgate UUID

XUID=$(curl -sSL "https://api.geysermc.org/v2/xbox/xuid/${ENCODED}" | jq -r '.xuid')
FLOODGATE_UUID=$(printf "00000000-0000-0000-%04x-%012x" $(( XUID >> 48 )) $(( XUID & 0xFFFFFFFFFFFF )))
echo "$FLOODGATE_UUID"

The Floodgate UUID is the XUID rendered as 16 hex chars (high 16 bits then low 48 bits) prepended with 16 zeros. The all-zero first half distinguishes Floodgate UUIDs from Mojang UUIDs.

Patch whitelist.json

POD=$(kubectl get pod -n minecraft -l app=minecraft -o jsonpath='{.items[0].metadata.name}')
kubectl cp -c minecraft minecraft/$POD:/data/whitelist.json /tmp/whitelist.json
jq --arg uuid "$FLOODGATE_UUID" --arg name "$SERVER_NAME" \
  '. += [{uuid: $uuid, name: $name}]' \
  /tmp/whitelist.json > /tmp/whitelist.new.json
cat /tmp/whitelist.new.json
kubectl cp -c minecraft /tmp/whitelist.new.json minecraft/$POD:/data/whitelist.json

The cat between the jq and the push-back lets you eyeball the appended entry before committing.

Reload the Whitelist

kubectl exec -n minecraft deploy/minecraft -- rcon-cli "whitelist reload"
kubectl exec -n minecraft deploy/minecraft -- rcon-cli "whitelist list"

Expected: the $SERVER_NAME value (e.g. .basic_killer_20) appears in the list with a 00000000-0000-0000-xxxx-xxxxxxxxxxxx UUID. The Bedrock player can now connect.

Script Sketch

The full procedure composes into a single script. Planned location: ~/xo/lelopez-io/homelab/scripts/whitelist-bedrock.sh, invoked as ./scripts/whitelist-bedrock.sh "<gamertag>". Reference implementation:

#!/usr/bin/env bash
# Whitelist a Bedrock player on the Minecraft Kubernetes server.
set -euo pipefail
[[ $# -eq 1 ]] || { echo "Usage: $0 \"<gamertag>\"" >&2; exit 64; }

RAW_GAMERTAG="$1"
SERVER_NAME=".$(echo "$RAW_GAMERTAG" | tr ' ' '_')"
ENCODED=$(printf '%s' "$RAW_GAMERTAG" | jq -sRr @uri)

RESPONSE=$(curl -sSL "https://api.geysermc.org/v2/xbox/xuid/${ENCODED}")
XUID=$(echo "$RESPONSE" | jq -r '.xuid // empty')
if [[ -z "$XUID" ]]; then
  echo "Lookup failed: $(echo "$RESPONSE" | jq -r '.message')" >&2
  echo "Tip: have the player attempt a Bedrock connection once, then retry." >&2
  exit 1
fi

FLOODGATE_UUID=$(printf "00000000-0000-0000-%04x-%012x" $(( XUID >> 48 )) $(( XUID & 0xFFFFFFFFFFFF )))
POD=$(kubectl get pod -n minecraft -l app=minecraft -o jsonpath='{.items[0].metadata.name}')
TMP=$(mktemp -d) && trap "rm -rf $TMP" EXIT

kubectl cp -c minecraft "minecraft/$POD:/data/whitelist.json" "$TMP/whitelist.json"
jq --arg uuid "$FLOODGATE_UUID" --arg name "$SERVER_NAME" \
  '. += [{uuid: $uuid, name: $name}]' \
  "$TMP/whitelist.json" > "$TMP/whitelist.new.json"
kubectl cp -c minecraft "$TMP/whitelist.new.json" "minecraft/$POD:/data/whitelist.json"
kubectl exec -n minecraft deploy/minecraft -- rcon-cli "whitelist reload" >/dev/null

echo "Added $SERVER_NAME$FLOODGATE_UUID"
kubectl exec -n minecraft deploy/minecraft -- rcon-cli "whitelist list"

The script is sketched here, not yet checked into the homelab repo — drop it under scripts/ when ready and add execute permissions.

Manage World Data

List Contents

POD=$(kubectl get pod -n minecraft -l app=minecraft -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n minecraft $POD -- ls -lh /data/world

Check Disk Usage

kubectl exec -n minecraft deploy/minecraft -- du -sh /data/

Create Backups

Full Server Backup

POD=$(kubectl get pod -n minecraft -l app=minecraft -o jsonpath='{.items[0].metadata.name}')
mkdir -p ~/minecraft-backup/$(date +%Y%m%d)
kubectl cp minecraft/$POD:/data ~/minecraft-backup/$(date +%Y%m%d)/

World Only

POD=$(kubectl get pod -n minecraft -l app=minecraft -o jsonpath='{.items[0].metadata.name}')
kubectl cp minecraft/$POD:/data/world ~/minecraft-backup/world-$(date +%Y%m%d)/

Manage Plugins

List Installed Plugins

kubectl exec -n minecraft deploy/minecraft -- ls /data/plugins/

Check BentoBox Addons

kubectl exec -n minecraft deploy/minecraft -- ls /data/plugins/BentoBox/addons/

View Plugin Configs

kubectl exec -n minecraft deploy/minecraft -- cat /data/plugins/<plugin>/config.yml

Troubleshoot Issues

Pod Not Starting

# Check pod status
kubectl get pods -n minecraft

# Check events
kubectl describe pod -n minecraft -l app=minecraft

# Check HelmRelease
flux get helmreleases -n minecraft

Connection Issues

# Verify services
kubectl get svc -n minecraft

# Check playit.gg agent
kubectl logs -n minecraft -l app=minecraft -c playit-agent

Restart Server

kubectl rollout restart deployment -n minecraft minecraft
kubectl get pods -n minecraft -w

Resources

Footnotes

  1. Minecraft Wiki, "Commands," minecraft.wiki. Accessed: Dec. 30, 2025. [Online]. Available: https://minecraft.wiki/w/Commands

  2. PaperMC, "Paper Administration," docs.papermc.io. Accessed: Dec. 30, 2025. [Online]. Available: https://docs.papermc.io/paper/admin

Previous
Minecraft Bedrock Support