All projects

Self-hosted modded Minecraft server

Personal project · January 2026 – ongoing

Summary

My first real remote Linux administration project. I bought a small headless Ubuntu machine (i5-8500T, 32 GB RAM, 256 GB SSD) and run a modded Minecraft server on it for a friend. I manage everything remotely over SSH, and the server is still running.

The goal was more than just "get a game server up". I wanted to run a service someone else depends on: reachable from the internet, stable, and manageable without me being the only one with the keys.

What I built

  • A NeoForge server running my friend's custom modpack of around 130 mods, managed as a systemd service.
  • A public entry point on a rented VPS (Stato) with a WireGuard tunnel back to my home server, because my ISP uses CGNAT.
  • A web dashboard so my friend can manage mods, restart the server and use the console without SSH access.
  • Basic hardening: SSH key-only login and a ufw firewall. World backups can be made from the dashboard.

How it fits together

Player
VPS (public IPv4)
WireGuard tunnel
Home server (Ubuntu)
Minecraft (NeoForge)
Dashboard

Challenge: no public IP (CGNAT)

My ISP uses CGNAT, so my home connection has no public IPv4 address and port forwarding isn't possible. Players simply couldn't reach the server.

I rented a VPS that has a public IPv4 and built a WireGuard tunnel between it and the home server. Players connect to the VPS, and the traffic goes through the tunnel to the machine at home.

I picked WireGuard over OpenVPN because it's lightweight and minimal. I wanted to learn the fundamentals of how a VPN tunnel works rather than rely on a heavier, more abstracted tool.

Challenge: keeping a modded server stable

With around 130 mods from a custom modpack, crashes were common, especially when the server was first set up and every time the modpack changed. So far that adds up to more than 300 crash reports.

I diagnosed them by reading the server logs and crash reports to track down which mod or config caused the problem.

Crashes could happen at any time, so whenever I was home and not working I put my own things aside to fix the server. I did that on purpose: I treated it like an on-call sysadmin job to learn what that kind of work actually feels like.

The dashboard

To give my friend control without giving him SSH access, I built a web dashboard with AI assistance. It runs on the home server next to Minecraft.

  • Mod management: upload, enable, disable and remove mods.
  • Server control: start, stop and restart the server.
  • Remote console over RCON, limited to an allowlist of commands.
  • Live logs and an issues view that groups repeated warnings and errors, with IP addresses and player UUIDs redacted.
  • Server settings, players and world backups.
  • Login with roles: passwords are hashed with bcrypt, sessions use JWT in an httpOnly cookie, and admin pages are protected.

Challenge: Linux users and permissions

The server and the dashboard run under separate identities, and at first the dashboard didn't have permission to change the Minecraft server's files, so none of its changes worked.

I solved it with a shared minecraft group: the server directories belong to that group, are group-writable and use the setgid bit so new files inherit it. Both services run with that group. For restarting, a narrow sudoers rule lets the dashboard control only the Minecraft service.

That taught me how Linux users, groups and file permissions work in practice, and why it's worth keeping services separated instead of running everything as one all-powerful user.

Why it's interesting to me

It was my first time being fully responsible for a Linux machine I can only reach remotely. Everything from networking to permissions to debugging had to happen over SSH, and if I broke the connection, there was no screen to walk up to.

Working around CGNAT made networking concrete: public vs. private addresses, why port forwarding fails, and how a tunnel to a VPS solves it.

Most of all, it was about running something for someone else: keeping it stable, reading logs to find the cause instead of guessing, and giving them safe, limited access so they don't have to wait for me.

Tech

  • Linux (Ubuntu)
  • SSH
  • systemd
  • WireGuard
  • VPS
  • NeoForge
  • ufw
  • Linux users & permissions
  • Log analysis
  • Next.js
  • TypeScript
  • RCON