logologo
01Home02Skills03Career04Portfolio05Blog06Contacts
Resume
01Home02Skills03Career04Portfolio05Blog06Contacts
Language
Resume

MinePanel

Self-hosted Minecraft management, secure and cloud-independent. ⛏️

NestJSDockerWebSocketsBun
post_image
Source Code
18/08/2026
0
25
Source Code

The Story

Running a Minecraft server is easy.

Running one properly is not.

The moment you add multiple users, controlled access, authentication, resource limits, monitoring, backups, server lifecycle management, and remote administration, the problem stops looking like a game-server script and starts looking a lot more like infrastructure.

I wanted a management experience that felt as convenient as a hosted panel without giving a hosted platform ownership of the infrastructure underneath it. The servers should run on hardware I control. The data should live where I decide. The Docker daemon, database, authentication system, and Minecraft instances should remain under the operator's control.

That became MinePanel.

It started as a relatively simple idea: build a clean interface around Minecraft servers running in Docker.

Then I kept asking what would happen if I treated every part of that idea seriously.

What happens when two lifecycle operations race each other? What happens when Docker and the database disagree? How should a moderator be allowed to manage one server without implicitly becoming an administrator of everything? How do you offer a hosted web application without turning the project into another centralized cloud service? How should cross-origin authentication work when credentials belong to somebody else's self-hosted backend? How do you ship container updates without silently turning latest into an uncontrolled deployment mechanism?

Those questions transformed MinePanel from a panel into one of the most technically ambitious projects I have built.

Today it consists of three deliberately separated parts:

  • an operator-owned backend that runs alongside Docker, PostgreSQL, Caddy, and the Minecraft workloads;
  • a hosted React PWA that connects directly from the browser to whichever MinePanel backend the user chooses;
  • a public SvelteKit site that documents the project and exposes its live roadmap.

There is still no mandatory MinePanel cloud sitting between the user and their infrastructure.

That constraint shapes almost everything else.


What Makes It Special

The central idea behind MinePanel is still simple:

the convenience can be hosted without the infrastructure becoming hosted.

The dashboard at app.minepanel.xyz is a static application. It does not proxy API requests through MinePanel servers, store operator credentials, relay sessions, or maintain a centralized database of connected panels.

Instead, the architecture looks roughly like this:

Code
app.minepanel.xyz
   static React PWA
          │
          │ HTTPS / WebSocket
          ▼
 operator-owned domain
          │
        Caddy
          │
        NestJS ───── PostgreSQL
          │
          │ Docker socket
          ▼
   mc-{serverId}
 Minecraft containers

The browser talks directly to the operator's backend.

A user can save multiple MinePanel installations in the PWA, but the local registry contains only metadata such as their canonical origins and optional labels. Authentication credentials, backend data, TOTP material, setup secrets, WebSocket data, and session tokens are not persisted there.

That separation lets MinePanel provide something that initially sounded contradictory: a single polished web application for managing completely independent self-hosted installations without putting a MinePanel-controlled API in the middle.

It also forced me to think much more carefully about browser security, protocol compatibility, capability discovery, cross-origin authentication, local state isolation, and what information the frontend should be trusted with.

The result feels simple from the outside because the complexity is being handled deliberately underneath it.


The Technical Craft

Core Features

  • Self-hosted control plane The backend, PostgreSQL database, Minecraft data, Docker containers, and management API remain on the operator's infrastructure.

  • Hosted multi-panel PWA One installable dashboard can connect directly to multiple independent MinePanel installations, with local IndexedDB metadata keeping those instances separated.

  • Container-per-server isolation Every Minecraft instance runs inside its own managed Docker container on a dedicated network, giving MinePanel a predictable lifecycle and resource model.

  • Full server lifecycle management Servers can be created, started, gracefully stopped, restarted, inspected, and removed through controlled backend operations rather than arbitrary Docker access.

  • Resource admission and reconciliation MinePanel checks host resources before admitting workloads and reconciles persisted state with the infrastructure it actually finds instead of assuming the database is always right.

  • Granular authorization ADMIN, MOD, and USER roles are combined with granular moderator permissions and per-server authorization rather than one global "moderator can do everything" flag.

  • OPEN / REQUEST / PRIVATE access models Servers can be public to authenticated users, discoverable through an approval workflow, or completely private. REQUEST servers have a dedicated discovery path without leaking PRIVATE servers.

  • Session-based authentication Authentication uses short-lived JWT access sessions through HttpOnly cookies, refresh sessions, revocation, logout-all, password recovery semantics, rate limiting, and first-admin bootstrap protections.

  • Two-factor authentication TOTP enrollment, verification, disable flows, and one-time backup codes are integrated into both the backend and PWA.

  • Challenge-bound Google authentication Google login and account linking use backend-issued single-use challenges and explicit linking semantics instead of treating an OAuth credential as sufficient proof for every operation.

  • Capability-negotiated clients Backends expose an explicit protocol version and capability surface. The PWA uses what the backend actually advertises instead of guessing compatibility from a version string.

  • Real-time host telemetry Administrators can monitor CPU, RAM, and disk information through authenticated WebSocket connections.

  • Hardened hosted authentication The current hosted-browser contract uses partitioned HttpOnly cookies and Web Locks to coordinate refresh behavior across browser contexts rather than falling back to unsafe token storage.

  • Strict frontend trust boundaries Production panel registration accepts canonical public HTTPS origins and rejects credentials, paths, literal IPs, local network pseudo-domains, and other ambiguous targets.

  • Deliberately limited offline behavior The PWA can cache its own application shell, but authentication, APIs, WebSockets, mutations, and backend responses never become service-worker application data.

  • Automated release engineering Pull requests and releases pass type checks, linting, automated tests, database migration validation, image smoke tests, security scanning, and deployment-contract checks before container publication.


The Secret Sauce

The backend currently uses:

  • NestJS 11 + TypeScript for explicit modules, dependency injection, guards, validation, WebSockets, and a codebase large enough that architectural boundaries actually matter.
  • Bun 1.3 as the production runtime and package manager.
  • PostgreSQL 16 as the primary stateful dependency.
  • Drizzle ORM for typed database access and explicit SQL migrations.
  • Dockerode for controlled communication with the host Docker daemon.
  • itzg/minecraft-server as the Minecraft workload base.
  • Caddy 2 as the public TLS boundary and reverse proxy.
  • Socket.IO for real-time authenticated communication.

The hosted dashboard uses:

  • React 19
  • Vite 7
  • React Router 7
  • TanStack Query
  • IndexedDB
  • Tailwind CSS 4
  • Socket.IO Client
  • vite-plugin-pwa
  • Cloudflare Pages

The public project site uses:

  • Svelte 5 + SvelteKit 2
  • Cloudflare Pages
  • server-side validated roadmap loading from the implementation repositories;
  • edge caching;
  • locally hosted assets;
  • no behavioral analytics or advertising dependencies.

The repositories are intentionally independent because they have very different responsibilities. The public website should not dictate backend behavior. The PWA should not become part of the operator's trusted backend. And the backend should not know how a particular frontend chooses to present its capabilities.

The API contract is what connects them.


Challenges and Solutions

Building a Hosted Dashboard Without Building a MinePanel Cloud

Challenge: A hosted frontend is convenient, but the obvious implementation is to put a MinePanel API, authentication relay, or proxy between the browser and every self-hosted backend.

That would make the architecture easier.

It would also undermine one of the main reasons MinePanel exists.

Solution: I designed the PWA as a static client that communicates directly with operator-selected HTTPS backends.

That required strict origin validation, exact CORS configuration, capability discovery, careful cookie semantics, partitioned cross-site cookies, coordinated refresh behavior through Web Locks, per-panel query isolation, and aggressive cleanup whenever a panel or authenticated identity changes.

Credentials are not moved into localStorage just to make the architecture easier. The service worker is not allowed to become an invisible API cache. The hosted application does not proxy requests through infrastructure I control.

Result: MinePanel can offer a single hosted management application while the actual control plane remains self-hosted.

That is probably the architectural decision that best represents the project now.


Giving an Application Access to Docker

Challenge: Access to /var/run/docker.sock is effectively privileged host access.

Pretending otherwise because the application happens to have a web UI would be irresponsible.

Solution: I made the Docker boundary explicit.

The NestJS application is reached through Caddy rather than publishing its API port directly. PostgreSQL is not publicly exposed. Minecraft workloads are created through controlled server-side paths. Host data roots are validated. The backend receives the Minecraft data tree read-only. Managed containers run on a dedicated bridge network. Lifecycle operations only target MinePanel-managed resources, and resource rules are checked before workloads are created.

Deployment assumptions are treated as part of the security model rather than as documentation trivia.

Result: MinePanel gets the flexibility of programmatic Docker orchestration while keeping the dangerous part of the architecture visible, constrained, reviewed, and testable.


Making Authorization More Expressive Than Roles

Challenge: ADMIN and USER are enough until the first time somebody needs to moderate one server but should not be allowed to reset accounts, delete another server, or manage the entire installation.

Solution: I separated identity, account status, global roles, granular moderator permissions, server visibility, and server membership into distinct concepts.

Authorization remains authoritative in the backend.

The frontend can hide a button because a user cannot perform an operation, but hiding that button is never the security mechanism.

MinePanel also protects administrative invariants such as accidentally removing the final administrator and gives REQUEST servers an explicit request/approval workflow rather than turning access control into a boolean field.

Result: The permission model can grow with communities instead of collapsing into progressively larger collections of role checks.


Making Docker and Database State Survive Reality

Challenge: Infrastructure is not CRUD.

A database row saying RUNNING does not make a container run. Docker operations can fail halfway through. Processes restart. Two requests can arrive at nearly the same time. Resource availability can change between operations.

Solution: Lifecycle operations use explicit transitions, guarded updates, resource admission checks, startup reconciliation, bounded stop behavior, and failure handling that distinguishes confirmed outcomes from uncertain ones.

The system is designed around the possibility that persisted state and observed infrastructure can diverge.

Result: The backend increasingly behaves like a small infrastructure controller rather than a thin HTTP wrapper around Docker.

That distinction changed a large part of how I designed the project.


Treating CI as Part of the Product

Challenge: Once people can deploy a container directly onto a machine that exposes its Docker daemon, "it builds on my laptop" stops being an acceptable release process.

Database migrations, container contents, architecture support, dependencies, deployment files, and release tags all become part of the product contract.

Solution: MinePanel's CI now validates much more than TypeScript.

The pipeline covers unit tests, PostgreSQL-backed E2E tests, migrations against fresh databases, container builds, degraded-mode image smoke tests, trusted Docker smoke tests for releases, vulnerability scanning, deployment-contract checks, and multi-architecture publication.

Published images include provenance/SBOM information and immutable commit-derived tags.

The release channels are explicit as well:

  • master produces the pre-stable edge channel plus an immutable SHA image;
  • future versioned releases produce matching semantic-version tags and latest;
  • deployment assets and container versions are expected to come from the same release.

Result: Releasing MinePanel is becoming a reproducible engineering process rather than "push a Docker image and hope the README still matches it."


Behind the Scenes

MinePanel has probably changed more in how I engineer software than any other personal project I have worked on.

Every completed layer exposed the next one.

Authentication led to sessions. Sessions led to revocation, recovery, 2FA, browser cookie semantics, and OAuth linking. Roles led to permission-based authorization. Server lifecycle management led to concurrency, resource admission, reconciliation, and graceful shutdown behavior. A hosted dashboard led to CORS, CHIPS, Web Locks, capability negotiation, strict origin handling, IndexedDB isolation, service-worker boundaries, and a completely different trust model.

Even documentation became architectural infrastructure.

The backend has a canonical specification that describes not just what exists, but the invariants the implementation is expected to preserve, what is deliberately deferred, what is optional, and what later phases are allowed to assume.

The backend and PWA own their own machine-readable roadmap state, while the public website consumes that data without becoming its source of truth.

That became particularly important because I use AI coding agents extensively during development.

MinePanel taught me that faster implementation makes architecture more important, not less.

I use agents as bounded implementation workers, reviewers, researchers, and additional pairs of eyes. My job increasingly becomes defining the contracts they have to operate inside: designing the architecture, deciding invariants, separating concerns, reviewing patches, testing assumptions, resolving conflicting approaches, and deciding when something is actually finished.

Without that discipline, AI can produce a large amount of locally reasonable code that collectively describes a bad system extremely quickly.

So I built the project around the opposite idea: make the specification strong, make trust boundaries explicit, make contracts testable, and make CI hostile to accidental regressions.

That process is one of the reasons MinePanel matters so much to me.

On the surface, it is a Minecraft management panel.

Underneath, it has become my practical playground for backend architecture, authentication, browser security, authorization, PostgreSQL, container orchestration, real-time systems, deployment engineering, CI/CD, protocol design, frontend architecture, and long-term product planning.

I did not want to build a polished mockup of an ambitious system.

I wanted to find out what happens when I keep engineering the ambitious system until the difficult parts are real too.


Current Status

MinePanel is currently approaching its first stable backend release.

The original backend foundation is complete: authentication, authorization, Docker lifecycle management, resource admission, host telemetry, deployment, automated migrations, and release automation are implemented.

The core Identity / Onboarding phase is also complete, including Google authentication, account linking, server visibility, access requests, requestable-server discovery, and granular moderator permissions.

On the frontend, the hosted dashboard already covers the current management surface: multi-panel discovery, authentication, account security, server lifecycle controls, access workflows, administrator management, moderator permissions, and host telemetry.

The next backend milestone is Stable-v1 Hardening: freezing more of the public protocol, improving error contracts and abuse protection, tightening workload resource isolation, making the Minecraft image strategy reproducible, and expanding trusted real-Docker lifecycle coverage.

After that, the roadmap moves deeper into actual server operations: audit events, console and logs, backups, scheduled tasks, filesystem management, players, plugins and mods, notifications, presets, networking, and eventually additional product surfaces.


Getting Started

The current pre-stable backend is published through the edge channel.

A deployment needs a Linux machine with Docker Engine and Compose, a domain pointing at it, and ports 80 and 443 available.

The deployment assets can be downloaded directly:

Code
curl -fsSLo docker-compose.yml https://raw.githubusercontent.com/MinePanelProject/minepanel-backend/master/docker-compose.yml
curl -fsSLo .env.example https://raw.githubusercontent.com/MinePanelProject/minepanel-backend/master/.env.example
curl -fsSLo Caddyfile https://raw.githubusercontent.com/MinePanelProject/minepanel-backend/master/Caddyfile

cp .env.example .env

After configuring the domain, credentials, setup token, and Minecraft data location:

Code
docker compose pull
docker compose up -d

Caddy provisions HTTPS automatically, PostgreSQL migrations run before the API starts, and the backend can then be added directly to the hosted PWA at app.minepanel.xyz.

No MinePanel account is required to connect the two.

No MinePanel server proxies the connection.

The infrastructure remains yours.

That is still the end goal I started with:

make serious Minecraft infrastructure on your own hardware feel as convenient as a hosted platform, without giving up ownership to get there.

Made with ❤️ by Okazakee | Source Code
•CMS•Privacy Policy