If you are building a raspberry pi minecraft server in 2026, the Raspberry Pi 5 (8GB variant) is the undisputed king of ARM-based Java servers. It handles Minecraft 1.21+ world generation and Java 21 garbage collection without breaking a sweat—provided you feed it fast storage and adequate cooling. However, running a headless server via SSH becomes a hassle when you need to monitor ticks-per-second (TPS) or safely shut it down without corrupting the world save.
This guide bridges the gap between IT administration and embedded hardware. We will build a PaperMC server on an NVMe drive and wire up a physical I2C OLED dashboard and a hardware safe-shutdown button. This gives you at-a-glance server telemetry and a physical way to kill the power safely, without touching a keyboard.
Project Spec Sheet & Parts List
Target Board: Raspberry Pi 5 (8GB RAM) running Raspberry Pi OS Bookworm (64-bit)
Estimated Cost: $115 - $145 USD
Do not attempt this build with a microSD card. Modern Minecraft servers perform thousands of small random I/O writes per second during chunk generation; a standard SD card will throttle your server and degrade within weeks. Furthermore, the 4GB Pi 5 variant will bottleneck during heavy Java garbage collection cycles when multiple players are exploring.
| Component | Exact Model / Variant | Why This Specific Part? |
|---|---|---|
| Compute Board | Raspberry Pi 5 (8GB) | PCIe 2.0 interface for NVMe; 8GB RAM prevents Java OOM crashes. |
| Case & Cooling | Argon ONE V3 M.2 NVMe Case | Integrated 30mm fan, routes NVMe to the PCIe lane, acts as a heatsink. |
| Storage | 1TB WD Blue SN580 NVMe (M.2 2242) | High random read/write IOPS; 2242 size fits the Argon case perfectly. |
| Display | 0.96" I2C OLED (SSD1306 driver) | Low power draw, high contrast, uses only 4 wires (VCC, GND, SDA, SCL). |
| Switch | 12mm Tactile Pushbutton (Normally Open) | Physical safe-shutdown trigger with satisfying tactile feedback. |
| Power Supply | Official 27W USB-C PD Power Supply | Pi 5 requires 5V/5A to prevent USB peripheral brownouts. |
Hardware Wiring & Pin Mapping
We are using the primary I2C bus for the OLED display and a standard GPIO pin with an internal pull-up resistor for the shutdown button. The Argon ONE case exposes the Pi 5 GPIO header through its top plate, making wiring straightforward.
| Component Pin | Raspberry Pi 5 GPIO / Physical Pin | Function |
|---|---|---|
| OLED VCC | Pin 1 (3.3V Power) | Power for SSD1306 logic (Do not use 5V, it will fry the I2C level shifters). |
| OLED GND | Pin 6 (Ground) | Common ground reference. |
| OLED SDA | Pin 3 (GPIO 2 / I2C1 SDA) | I2C Data line. |
| OLED SCL | Pin 5 (GPIO 3 / I2C1 SCL) | I2C Clock line. |
| Button Leg 1 | Pin 11 (GPIO 17) | Signal line (configured with internal pull-up in software). |
| Button Leg 2 | Pin 9 (Ground) | Completes the circuit to ground when pressed. |
Server Setup & Python Dashboard Code
Before running the dashboard code, install the PaperMC server. Follow the official PaperMC installation guide to download the jar, accept the EULA, and configure your server.properties. Ensure you enable RCON and set a password in server.properties if you plan to expand this script later, though the script below relies on system-level metrics and socket polling for maximum reliability.
Install the required Python libraries for the hardware interface:
sudo apt update
sudo apt install python3-pip i2c-tools python3-venv
python3 -m venv ~/mc-dashboard
source ~/mc-dashboard/bin/activate
pip install gpiozero luma.oled psutil RPi.GPIO
Below is the complete, compilable Python script. It polls the Minecraft port to verify the server is online, reads system RAM and CPU temperature, renders it to the OLED, and listens for the physical button press to execute a safe systemctl shutdown.
import time
import socket
import psutil
import os
import subprocess
from gpiozero import Button
from luma.core.interface.serial import i2c
from luma.oled.device import ssd1306
from PIL import ImageFont, ImageDraw, Image
# --- PIN & HARDWARE DEFINITIONS ---
SHUTDOWN_PIN = 17
I2C_PORT = 1
I2C_ADDRESS = 0x3C
MINECRAFT_PORT = 25565
SHUTDOWN_HOLD_TIME = 2.0 # Seconds to hold button to prevent accidental presses
# Initialize Hardware
try:
shutdown_button = Button(SHUTDOWN_PIN, pull_up=True, bounce_time=0.1, hold_time=SHUTDOWN_HOLD_TIME)
serial_interface = i2c(port=I2C_PORT, address=I2C_ADDRESS)
oled_device = ssd1306(serial_interface)
except Exception as e:
print(f"Hardware initialization failed: {e}")
exit(1)
# Load default font (fallback if custom fonts fail)
try:
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 12)
font_small = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 10)
except IOError:
font = ImageFont.load_default()
font_small = font
def is_server_online():
"""Checks if the Minecraft server is accepting TCP connections."""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1.5)
try:
result = sock.connect_ex(('127.0.0.1', MINECRAFT_PORT))
return result == 0
except socket.error:
return False
finally:
sock.close()
def get_cpu_temp():
"""Reads the BCM2712 thermal zone."""
try:
temps = psutil.sensors_temperatures()
if 'cpu_thermal' in temps:
return temps['cpu_thermal'][0].current
elif 'coretemp' in temps:
return temps['coretemp'][0].current
except Exception:
pass
return 0.0
def draw_dashboard():
"""Renders system and server stats to the OLED."""
image = Image.new('1', (oled_device.width, oled_device.height))
draw = ImageDraw.Draw(image)
# Server Status
status = "ONLINE" if is_server_online() else "OFFLINE"
draw.text((0, 0), f"MC Server: {status}", font=font, fill=255)
# System Metrics
ram = psutil.virtual_memory()
ram_used_gb = ram.used / (1024**3)
ram_total_gb = ram.total / (1024**3)
cpu_temp = get_cpu_temp()
draw.text((0, 18), f"RAM: {ram_used_gb:.1f}/{ram_total_gb:.1f} GB", font=font_small, fill=255)
draw.text((0, 32), f"CPU Temp: {cpu_temp:.1f} C", font=font_small, fill=255)
draw.text((0, 46), f"Hold Btn to Shutdown", font=font_small, fill=255)
oled_device.display(image)
def safe_shutdown():
"""Triggers graceful OS shutdown to prevent NVMe corruption."""
draw = ImageDraw.Draw(Image.new('1', (oled_device.width, oled_device.height)))
# Clear screen and show shutdown message
image = Image.new('1', (oled_device.width, oled_device.height))
draw = ImageDraw.Draw(image)
draw.text((10, 20), "SHUTTING DOWN...", font=font, fill=255)
oled_device.display(image)
# Execute system shutdown
subprocess.run(['sudo', 'systemctl', 'poweroff'])
# Bind button hold event
shutdown_button.when_held = safe_shutdown
if __name__ == "__main__":
print("Dashboard running. Hold button for 2s to shutdown.")
try:
while True:
draw_dashboard()
time.sleep(3) # Update every 3 seconds to reduce I2C bus spam
except KeyboardInterrupt:
oled_device.cleanup()
print("Dashboard stopped.")
Debugging: First Three Things to Check When It Fails
Embedded server projects fail at the intersection of hardware, OS, and Java. If your setup crashes, check these three specific failure modes in order.
1. The Java Heap Space Crash
Exact Error String: [Server thread/ERROR]: java.lang.OutOfMemoryError: Java heap space
Ranked Causes:
- JVM Startup Flags Missing: You ran
java -jar paper.jarwithout allocating memory. Java defaults to a tiny fraction of system RAM. - Allocating Too Much RAM: You set
-Xmx7Gon an 8GB board, leaving no RAM for the Linux kernel and NVMe cache, triggering the OOM killer.
The Fix: Always use explicit flags. For an 8GB Pi 5, use: java -Xms4G -Xmx5G -XX:+UseG1GC -jar paper.jar. This leaves 3GB for the OS and file system caching.
2. The I2C Bus Disconnect
Exact Error String: OSError: [Errno 121] Remote I/O error
Ranked Causes:
- Loose Dupont Connectors: The female-to-female jumpers on the SDA/SCL pins have backed out slightly due to thermal expansion from the Pi's heat.
- Missing Pull-up Resistors: Cheap SSD1306 modules sometimes omit the 4.7kΩ I2C pull-up resistors, causing signal degradation on the Pi 5's faster I2C bus.
The Fix: Run i2cdetect -y 1 in the terminal. If the grid is empty or shows UU, reseat the wires. If the display is still flaky, solder 4.7kΩ resistors between VCC and the SDA/SCL lines directly on the OLED PCB.
3. The Python Socket Timeout
Exact Error String: socket.timeout: timed out (followed by the dashboard showing OFFLINE despite the server running).
Ranked Causes:
- Server Still Booting: PaperMC takes 30-60 seconds to generate the
spawnchunks on first run. Port 25565 isn't open yet. - Firewall Blocking Localhost: UFW or iptables is configured to drop traffic on 25565, even on the loopback interface.
The Fix: Check sudo ufw status. If active, allow local traffic: sudo ufw allow from 127.0.0.1 to any port 25565.
Extending or Simplifying the Build
Not every build needs a screen, and some makers want full rack-mount telemetry. Here is how to adjust the scope of this project based on your bench time.
To Simplify: Strip out the luma.oled and PIL libraries entirely. Keep only the gpiozero button logic. A physical safe-shutdown button is arguably the most critical hardware feature for a headless server, preventing NVMe file system corruption during sudden power loss. You can mount the button directly to the Argon ONE case using a 12mm drill bit.
To Extend:
- Add RCON Integration: Use the
rconPython library to send thetpscommand directly to the PaperMC console and parse the output, displaying exact server TPS on the OLED instead of just CPU temp. - Add a Power Relay: Wire a 5V relay module to GPIO 26. Modify the
safe_shutdown()function to trigger the relay 60 seconds after thesystemctl poweroffcommand is sent, physically cutting power to the Pi once it halts, saving idle electricity. - External Antenna: If you are hosting over WiFi (not recommended, but common), the Pi 5's onboard Bluetooth/WiFi module can suffer from thermal throttling. Add a USB WiFi 6E adapter with an external antenna for stable ping.
Frequently Asked Questions
Can a Raspberry Pi 4 run a modded Minecraft server?
Technically yes, but practically no for modern modpacks. The Pi 4 maxes out at 8GB RAM and uses a slower Cortex-A72 CPU. Heavy modpacks (like ATM9 or RLCraft) require Java 17/21 and massive amounts of RAM for chunk pre-generation. The Pi 4 will stutter heavily during garbage collection cycles. If you must use a Pi 4, stick to lightweight plugins (Paper/Purpur) and limit the world border to 2000 blocks to reduce RAM overhead.
How many players can a Raspberry Pi Minecraft server handle in 2026?
On a Raspberry Pi 5 (8GB) running PaperMC with optimized startup flags and NVMe storage, you can comfortably host 8 to 12 players in a standard survival world. The bottleneck is rarely CPU single-core speed anymore; it is network latency and the I/O throughput of chunk generation when players fly in different directions using Elytras. Using the Pi 5's native PCIe lane with an NVMe drive eliminates the I/O bottleneck, pushing the limit closer to 15 players before TPS drops below 18.
Why does my server lag when chunks generate, and how do I fix it?
Chunk generation is a single-threaded, CPU-intensive mathematical process. When a player explores new terrain, the server halts other tasks to calculate terrain noise, biome placement, and structure spawning. To fix this, pre-generate your world. Install the Chunky plugin and run chunky radius 5000 followed by chunky start while no players are online. This forces the server to generate and save all chunks to the NVMe drive ahead of time, reducing in-game generation lag to zero.






