A self-hosted Necesse dedicated game server, plus a small password-protected web dashboard to manage it: start/stop the server, edit its settings (world, slots, password, port, MOTD, ...), watch who's online, kick/ban players, and browse logs and world saves — all without touching the command line after initial setup.
The Necesse dedicated server Docker image (Dockerfile) that this project runs on top of is BrammyS/necesse-docker-server — all credit for the server containerization itself goes there. This repo adds a web UI (ui/ and control/) alongside it; none of that would exist without BrammyS's original work. If you just want the game server without a UI, use their repo directly.
The Necesse dedicated server has no RCON, no HTTP API, and no config file you can hot-reload — every setting is baked into the launch command from environment variables, and changing anything means recreating the container. This UI exists to make that whole cycle (and the day-to-day "who's playing, is it up, show me the logs" questions) a few clicks instead of a docker-compose edit + recreate + tail -f.
- Password-protected — a single admin password (bcrypt-hashed), session cookies, CSRF protection, rate-limited login.
- Start / stop / restart the game server container.
- Edit server config — world name, slots, owner, MOTD, password, pause-when-empty, and the host port — staged to a
.envfile and applied via an explicit "Apply" step that recreates the container. - Live player list — real online/max count straight from the server's own console, plus kick and ban/unban.
- Log-derived "online now" + event history — join/leave events parsed from the server's session logs.
- World saves browser — lists save files with their parsed
worldSettings.cfg.
Three containers, one docker-compose.yaml:
necesse — the game server itself (BrammyS's image, unmodified)
control — small internal sidecar; the ONLY thing holding /var/run/docker.sock,
exposes a fixed set of operations (start/stop/status/config/kick/ban/...)
over an internal Docker network — never reachable from outside
ui — Express + React, password login, talks to `control` over HTTP
control is split out from ui deliberately: the backend needs Docker-socket access to manage the game server (which is effectively root on the host), and the UI is meant to be reachable from the internet if you want it to be. Keeping the socket out of the internet-facing process means a bug or vulnerability in the web app can't be used to take over the host.
-
Set your admin password.
cd ui/server node scripts/hash-password.js "your-real-password"
Copy the printed hash into
.envasUI_PASSWORD_HASH— double every$in it (Docker Compose's own variable interpolation mangles single$characters, e.g.$2a$12$abc→$$2a$$12$$abc). -
Copy
.env.exampleto.env(if you haven't already) and fill in the rest —SESSION_SECRET(any long random string), and whateverPASSWORD/SLOTS/WORLD/etc. you want the game server to start with. -
Start everything:
docker compose up -d --build
-
Open
http://<your-host>:3000(defaultUI_PORT=3000) and log in.
All settings live in the root .env file (see .env.example for the full list with defaults):
| Variable | Purpose |
|---|---|
WORLD, SLOTS, OWNER, MOTD, PASSWORD, PAUSE, GIVE_CLIENTS_POWER, LOGGING, ZIP |
Passed straight through to the Necesse server (same as BrammyS's image) |
HOST_PORT |
The host-side UDP port players connect to (container-internal port is always 14159) |
UI_PASSWORD_HASH |
bcrypt hash of the admin password (see Quick start above) |
SESSION_SECRET |
Random secret for signing session cookies |
UI_PORT |
Host port the web UI listens on |
TRUST_PROXY |
Set to 1 only if running behind a reverse proxy (e.g. Cloudflare Tunnel) that terminates TLS |
Changing the game-server settings through the UI's Config page stages them into .env and only takes effect once you click Apply, which recreates the necesse container (disconnecting any connected players).
By default the UI is only as exposed as UI_PORT is (i.e. your LAN, unless you port-forward it). If you want to reach it from outside your network, put it behind something like Cloudflare Tunnel with Cloudflare Access in front of it, rather than forwarding a port directly — the backend has Docker-level control over the game server, so it's worth keeping it off the open internet even with a password gate.
- Player identifiers used for kick/ban are always the game's own client id, validated server-side — never a raw shell/console command.
JVMARGSis intentionally not editable from the UI (arbitrary JVM flags are a code-execution vector).- The
controlservice is the only container with Docker socket access, and its HTTP API only exposes a fixed set of operations — no endpoint accepts an image, bind mount, or command from the caller.
MIT — see LICENSE. The base Dockerfile is © BrammyS (necesse-docker-server); the ui/ and control/ additions are new work built on top of it.




