Packwiz-Abdelpak-hosting-kit/README.md

164 lines
6.1 KiB
Markdown

# mc-packwiz-deploy
Self-hosted NeoForge Minecraft server with a packwiz modpack that clients pull
automatically on every launch. Mods are served from an HTTP mirror of the
server's own mods directory — no Modrinth or CurseForge dependency.
Everything lives on a ZFS dataset mounted at `/minecraft`.
## Quick start
```bash
./deploy.sh -n "Opengames" -a themiro \
-u https://games.abdelbaki.eu/mods \
--accept-eula
```
Add `-p 25599` to run on a non-default port. It's written into
`server.properties`, opened in firewalld, and shown in the player guides — which
print a bare hostname on 25565 and `host:port` on anything else, since Minecraft
assumes the default and a needless `:25565` confuses people.
The NeoForge version is resolved automatically — the newest stable build in the
series matching your Minecraft version, from the NeoForged maven. Pin one with
`-N 21.1.248` if you'd rather not track latest.
Then:
```bash
sudo systemctl start minecraft
journalctl -u minecraft -f
```
Send the players `https://<your-host>/setup-windows-packwiz.html` or
`setup-linux-packwiz.html`. Those are generated with your real pack URL and
server address already filled in.
## Re-running
`deploy.sh` is safe to run repeatedly, and re-running is the intended way to
apply changed settings — a port change, a different heap size, or a run that
failed partway and left things half-applied.
Each run:
1. Snapshots the dataset (`<pool>@predeploy-<timestamp>`)
2. Stops the server if it was running
3. Rewrites `server.properties`, `user_jvm_args.txt` and both unit files
4. Moves any existing pack to `<pack>.bak.<timestamp>` and rebuilds it
5. Regenerates the player guides
6. Starts the server again if it had been running
**The world and `/minecraft/mods` are never touched.** The pack is moved aside
rather than deleted, so hand-edited `.pw.toml` files and any git history in the
pack directory survive.
`--keep-pack` skips the pack rebuild; `--no-snapshot` skips step 1.
## What's in here
| Script | Runs as | Does |
|---|---|---|
| `deploy.sh` | you | Runs the two setup scripts in order |
| `mc-service-setup.sh` | root | `minecraft` user, NeoForge server, systemd units |
| `packwiz-setup.sh` | you | packwiz install, pack init, mod index, player guides |
| `mc-refresh-restart.sh` | you | Re-sync the pack after changing mods, then restart |
| `build.sh` | you | Builds the release zip |
## Pack HTTP server
`packwiz-http.service` serves the pack, the mod jars and the setup guides on
port 18080 (`-H` to change, `-B` to change the bind address, `--no-http` to skip
it). Put a reverse proxy in front of it.
It serves an **allowlist**, not the whole share: `mods/`, `packs/`, and
`setup-*.html`. Everything else 404s. That is deliberate — the same directory
holds `server.properties` (which can carry an rcon password), `ops.json`,
`whitelist.json`, `usercache.json`, logs and the world.
`pack.toml` and `index.toml` are sent with `Cache-Control: no-cache`, so the
proxy can't hand clients a stale pack. Jars are pinned by hash in the index and
cache freely.
If the mirror is only reachable over plain HTTP from the machine building the
pack, but clients need the public HTTPS URL, use `-S https` — it rewrites the
scheme on every generated URL.
## Memory
The heap defaults to 10 GB (`-X`/`-x` to change it). `-XX:+AlwaysPreTouch` means
the JVM commits the whole heap at startup rather than growing into it, so the
number you set is the number actually taken.
On a ZFS host, cap the ARC to go with it — ZFS defaults to using up to half of
RAM for cache, and will otherwise contend with the JVM for the same pages:
```bash
echo "options zfs zfs_arc_max=2147483648" | sudo tee /etc/modprobe.d/zfs.conf
sudo dracut --force && sudo reboot
```
That pins the ARC to 2 GB. On a 16 GB box that leaves roughly 4 GB for the OS
and anything else the host runs, which is comfortable.
## Requirements
- Fedora (or any systemd distro; `dnf` calls need swapping otherwise)
- A ZFS dataset mounted at `/minecraft`
- Java 21
- A web server already publishing `/minecraft``/mods` and `/packs` must be
reachable over HTTP
Every script refuses to run if `/minecraft` isn't a mountpoint. An unmounted
dataset leaves it as an empty directory on the root filesystem, and writing a
world or a mod mirror there fills the root disk instead of the pool.
## Day-to-day
Add or remove a jar in `/minecraft/mods`, then:
```bash
./mc-refresh-restart.sh
```
That registers new mods, re-pins any whose file changed, drops metadata for
deleted ones, takes a ZFS snapshot, restarts the server, and prunes to the
8 most recent snapshots. Players get the change on their next launch.
It refuses to restart if any mod failed to register, so the server never comes
up on a half-synced pack. Edit the config block at the top first — `PACK_DIR`,
`BASE_URL`, and `PACK_URL` need to match your deployment.
## The server unit
`minecraft.service` pairs with `minecraft.socket`, a console FIFO at
`/run/minecraft-console`. That's what makes a clean shutdown possible:
```bash
sudo systemctl stop minecraft # sends "stop", world saves properly
echo "say hello" | sudo tee /run/minecraft-console
journalctl -u minecraft -f # console output
```
Without the FIFO, systemd would SIGTERM the JVM and risk cutting a world save
in half.
## Web server
`/minecraft` needs to be served over HTTP. Serve `pack.toml` and `index.toml`
with `Cache-Control: no-cache` — a caching proxy will otherwise hand clients a
stale pack while the server runs the new one. `mc-refresh-restart.sh` checks for
exactly that and stops before restarting if it finds it.
## Caveats
- `packwiz-setup.sh` probes `packwiz --help` at runtime rather than assuming
flag names, since the NeoForge options have moved between builds. If it bails
with an interface complaint, the installed packwiz differs from what's
expected.
- Mod names come from filenames with the trailing version stripped
(`create-1.21.1-6.0.4.jar` → `create`). It's a heuristic; rename the
generated `.pw.toml` files if you don't like the result.
- Hashes are pinned. Replacing a jar in place under the same filename requires
a re-sync, or clients fail the hash check.