Minecraft 26.2 Fabric server stack with CraftDeck management panel

- docker-compose: itzg/minecraft-server (Fabric, offline mode, whitelist
  enforced, Modrinth-managed mods), itzg/mc-backup sidecar, custom dashboard
- Gated auth for offline mode: approve username -> whitelisted with a short
  registration window -> EasyAuth /register + /login; auto-revoke on expiry
- CraftDeck panel (Next.js 16, zero native deps): live console (SSE), players
  & stats, lag diagnostics (server TPS vs player ping via spark), performance
  graphs, Modrinth mod search/manager, backups & restore, sandboxed file
  browser/editor, .env config editor with compose apply, cron schedules,
  Discord alerts, scrypt-hashed panel login
- scripts/setup.sh generates secrets; docs/ covers auth, mods, updating,
  backups, networking (Tailscale), troubleshooting

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Aryan SinghandClaude Fable 5 committed 2026-07-10 11:07:07 -05:00
commit 279d137ce5
98 files changed
+9925

No files matched your search

+64
View File
@@ -0,0 +1,64 @@
# Authentication flow
The server runs with `online-mode=false` so offline launchers (TLauncher, etc.) can
join. That disables Mojang's account verification, so this stack layers two gates on
top:
1. **Whitelist** (vanilla) — only approved usernames can connect at all
2. **EasyAuth** (Fabric mod) — every player must `/register` a password once, then
`/login` on every join. Unauthenticated players are frozen, invisible to the world,
and kicked after a timeout.
## The approval lifecycle
```
you approve a name ──► whitelisted + "pending" + countdown starts (default 2 min)
│
├─ player joins and /register-s in time ──► "registered" (permanent access)
│
└─ countdown lapses ──► whitelist entry removed ──► "expired"
(re-approve any time to restart the window)
```
The panel enforces this automatically: a background watcher reconciles the pending list
against EasyAuth's registered accounts every 5 seconds and revokes lapsed windows.
### Why the short window matters
While a name is whitelisted but unregistered, anyone who knows the name could join and
claim it. The window shrinks that exposure to a couple of minutes, and you control
exactly when it opens — approve while your friend is sitting in the launcher. If someone
does sneak in and register a name that isn't theirs, just **Revoke** it (removes the
account + whitelist entry) and approve again.
## In-game commands (players)
| Command | What it does |
|---|---|
| `/register <pass> <pass>` | Create your password (first join only) |
| `/login <pass>` | Log in (every join) |
| `/account` | Change password, manage sessions |
| `/logout` | Log out manually |
## Panel actions (Access & Auth page)
| Action | Effect |
|---|---|
| Approve | Whitelist + open registration window |
| Re-open window | Same, for an expired/revoked name |
| Reset password | Sets a temporary password (shown once) and kicks them to re-login |
| Revoke | Whitelist entry + EasyAuth account deleted, player kicked |
| Import whitelist | Adopt names whitelisted outside the panel |
## Storage
EasyAuth stores password hashes in its own database under
`data/mc/mods/EasyAuth/` (included in backups). The panel's approval state lives in
`data/dashboard/craftdeck.db`. The two are reconciled via RCON (`auth list`,
`auth remove`, `auth update`), so there's no fragile file coupling.
## Tuning
- **Window length**: Settings → "Player registration window" (0.5–120 minutes)
- **EasyAuth options** (kick timeout, session lifetime, password rules):
Files → `config/EasyAuth/` — edit and restart the server
+57
View File
@@ -0,0 +1,57 @@
# Backups & restore
## What runs automatically
The `backup` sidecar (itzg/mc-backup) tars the entire server data directory to
`data/backups/world-<timestamp>.tgz`:
- flushes the world to disk first (`save-all`), safe while players are online
- interval: `BACKUP_INTERVAL` in `.env` (default `24h`)
- retention: `BACKUP_RETENTION_DAYS` (default `7`) — older tars are pruned
- excludes caches and logs
Safety backups are also taken automatically before config applies (when enabled) and
world resets.
## Manual backup
- Panel: Backups page → **Back up now**
- CLI: `docker exec mc-backup backup now`
## Restoring
Panel: Backups page → **Restore** → type `restore`. The panel stops the server,
extracts the tar over the data directory, and starts the server again. Everything since
that backup is lost.
CLI fallback (panel down):
```bash
./scripts/restore.sh data/backups/world-20260710-050000.tgz
```
## Off-site copies (recommended)
`data/backups/` is just files — sync it anywhere:
```bash
# example: nightly rsync to another machine (add to the host's crontab)
0 6 * * * rsync -a --delete /path/to/minecraft/data/backups/ backup-host:/srv/mc-backups/
```
You can also download any backup from the panel (⬇ button) before risky experiments.
## Disaster recovery (new machine)
```bash
git clone <repo> minecraft && cd minecraft
./scripts/setup.sh # fresh secrets — friends keep their passwords,
# those live in the world data, not in .env
mkdir -p data/backups && cp <your-backup>.tgz data/backups/
docker compose up -d # let it initialize once
./scripts/restore.sh data/backups/<your-backup>.tgz
```
The backup contains the world, mods, configs, and the EasyAuth database, so player
registrations survive. Panel state (approval history, metrics) lives in
`data/dashboard/` — include it in your off-site sync if you want it back too.
+66
View File
@@ -0,0 +1,66 @@
# Mods & modpacks
## Why Fabric
- First loader to support new Minecraft versions (26.x drops land on Fabric within days)
- The best server-side performance ecosystem (Lithium, FerriteCore, spark)
- EasyAuth (the auth system) is Fabric-only
- Server-side-only mods are invisible to players — vanilla and TLauncher clients join
without installing anything
NeoForge/Forge are supported by the same stack (`SERVER_TYPE` in `.env`) but trail new
versions and would require replacing the auth mod, so Fabric is the default.
## The default mod set
| Mod | Job | Players need it? |
|---|---|---|
| fabric-api | required by most Fabric mods | no |
| easyauth | password authentication | no |
| spark | TPS/ping profiling (powers the Lag page) | no |
| lithium | game-logic optimization | no |
| ferrite-core | memory-usage reduction | no |
## Adding mods
**Panel way (recommended):** Mods page → search Modrinth → **Add** → **Apply changes**.
The search is pre-filtered to Fabric mods compatible with your exact server version.
Watch the **"needs client install"** badge — server-side mods are safe for everyone;
client-required mods mean every friend must install the same mod or they can't join.
**Manual way:** drop a `.jar` into `data/mc/mods/` (Files page → mods → Upload) and
restart the server.
## How auto-update works
Managed mods are pinned by *project slug*, not version, in `.env`:
```
MODRINTH_PROJECTS=fabric-api,easyauth,spark,lithium,ferrite-core
```
Every time the server container (re)starts, the itzg image resolves each slug to the
**newest release compatible with your Minecraft version** and downloads it. So:
- "Update all mods" = restart the server (Mods page → Update all & restart)
- Upgrading Minecraft automatically brings the matching mod builds with it
Manually uploaded jars are yours to update — the resolver doesn't touch them.
## Switching to a modpack later
1. Pick a **Modrinth** modpack (e.g. Adrenaline, Simply Optimized) and note its slug
2. Configuration page → set `MODRINTH_MODPACK` to the slug → Save → Apply
(take the safety backup — modpacks change world generation!)
3. Every player installs the same modpack client-side (Modrinth App → install pack →
play). Offline-launcher users need a launcher that supports Modrinth packs, or
manual mod installation.
To go back: clear `MODRINTH_MODPACK`, Apply. Keep `MODRINTH_PROJECTS` as your
server-side base either way. CurseForge packs also work (`CF_SLUG` env — needs a
CurseForge API key; see the itzg image docs), but Modrinth is smoother.
## Removing a mod
Mods page → managed list → 🗑 (or delete the jar) → Apply. `fabric-api` and `easyauth`
are locked — the auth system depends on them.
+56
View File
@@ -0,0 +1,56 @@
# Networking
## Ports
| Port | Service | Exposure |
|---|---|---|
| 25565/tcp | Minecraft | Internet (port-forward this on your router) |
| 8080/tcp | Dashboard | **LAN/VPN only — never port-forward** |
| 25575/tcp | RCON | Docker-internal only (not published at all) |
## Letting friends connect
1. Give the server machine a static LAN IP (router → DHCP reservation)
2. Router → port forwarding → external `25565` → `<server-lan-ip>:25565`
3. Friends connect to your public IP (`curl ifconfig.me`), or set up free dynamic DNS
(DuckDNS et al.) so they can use a stable name like `yourname.duckdns.org`
Whitelist + EasyAuth handle the "random people found my server" problem — that's what
they're for.
## Keeping the dashboard private
The dashboard is deliberately not hardened for the open internet (it controls Docker).
Reach it from outside your LAN with **Tailscale** (free, ~5 minutes):
```bash
# on the server
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
```
Install Tailscale on your phone/laptop, log into the same account, and the panel is at
`http://<server-tailscale-ip>:8080` from anywhere — encrypted, invisible to everyone
else. (WireGuard works just as well if you prefer to self-host the VPN.)
Optionally pin the port binding to specific interfaces in `.env`/compose, and firewall
the host:
```bash
sudo ufw allow 25565/tcp
sudo ufw allow from 192.168.0.0/16 to any port 8080 proto tcp # LAN only
sudo ufw enable
```
## If you later want the panel on a domain
Put Caddy (or Traefik/nginx) in front for TLS + an extra auth layer, and keep 8080
unpublished. This repo doesn't ship that by default — open an issue with your domain
setup and add a `caddy` service; the panel works fine behind a reverse proxy
(plain HTTP upstream, cookie auth).
## "Is it my internet or the server?"
Performance & Lag page. Server-side lag shows as low TPS / high MSPT (affects everyone
equally); player-side lag shows as high ping for that player only. The page gives a
plain-English verdict and per-player ping history so you can settle the argument.
+72
View File
@@ -0,0 +1,72 @@
# Troubleshooting
## Server won't start
**Check the Console page first** (or `docker logs mc --tail 100`).
| Symptom in logs | Fix |
|---|---|
| `EULA` complaints | `EULA=TRUE` is set by compose — make sure you didn't override it |
| A mod failed to resolve for your version | Remove that slug on the Mods page, Apply. (New MC version + mod hasn't updated yet.) |
| `OutOfMemoryError` | Lower `MC_VIEW_DISTANCE`, or raise `MC_MEMORY`/`MC_CONTAINER_MEM_LIMIT` if the host has headroom |
| Port already in use | Something else on 25565 — change `MC_PORT` in `.env` |
| Crash right after "Done" | A mod conflict — disable recently added jars (Mods page → power icon) |
## A friend can't join
Work down this list:
1. **"Not whitelisted"** → their username isn't approved (or the window expired and
auto-revoked them). Access & Auth → approve/re-open. Username must match **exactly**
(case matters in TLauncher).
2. **Joins but immediately kicked** → they didn't `/register` or `/login` in time —
EasyAuth kicks unauthenticated players after a timeout. Tell them to type faster or
raise the timeout in `config/EasyAuth/`.
3. **"Connection timed out"** → networking: is 25565 port-forwarded? Are they using
your *public* IP? Does `docker compose ps` show `mc` healthy?
4. **Wrong password loop** → Access & Auth → **Reset password**, share the temporary
one privately.
## Panel shows "Offline" but the server is up
The dashboard container can't reach the Docker socket or RCON:
```bash
docker compose ps # is mc-dashboard running?
docker logs mc-dashboard --tail 50
docker compose up -d --force-recreate dashboard
```
If you changed `RCON_PASSWORD` in `.env`, Apply/recreate **both** `mc` and `dashboard`
so they agree.
## Forgot the panel password
```bash
docker compose stop dashboard
rm data/dashboard/craftdeck.db # wipes panel users/settings/history — NOT the world
# set PANEL_ADMIN_PASSWORD in .env to a fresh value
docker compose up -d dashboard
```
Player approvals are re-importable afterwards (Access & Auth → Import whitelist).
## Lag
Performance & Lag page tells you *who* is lagging (server vs a player's connection).
If it's the server: check the CPU/memory charts, run the spark health report, lower
view/simulation distance, or add more RAM. If it's one player: it's their network —
nothing to fix server-side.
## Disk filling up
Backups page shows total backup size; lower `BACKUP_RETENTION_DAYS`. The panel also
raises a low-disk alert below 2 GB free.
## Nuke everything and start over (keeps nothing)
```bash
docker compose down
rm -rf data
./scripts/setup.sh && docker compose up -d
```
+39
View File
@@ -0,0 +1,39 @@
# Updating
## Upgrading Minecraft (e.g. 26.2 → 26.3)
1. **Configuration** page → change `Minecraft version` → **Save**
2. **Apply** with *"Take a safety backup first"* enabled
3. The server container is recreated: the new server jar, a matching Fabric loader, and
fresh builds of every managed mod are downloaded automatically
If a managed mod has no build for the new version yet, the server won't start — check
the Console page for which one, remove it from the Mods page (or wait a few days), and
Apply again. Worlds upgrade forward automatically; **there is no downgrade** — that's
what the safety backup is for.
Command-line equivalent:
```bash
sed -i 's/^MC_VERSION=.*/MC_VERSION=26.3/' .env
docker exec mc-backup backup now
docker compose up -d mc
```
## Updating mods only
Mods page → **Update all & restart**. (Any server restart re-resolves managed mods.)
## Updating the panel / images
```bash
git pull
docker compose pull # new itzg server + backup images
docker compose build dashboard
docker compose up -d
```
## Snapshots / release candidates
`MC_VERSION` accepts any version the launcher knows (`26.3-rc-1`, `26.3-snapshot-3`…).
Mods rarely support snapshots — expect to trim the mod list if you try them.