feat(compose): mount mods, config and kubejs writable

Drop :ro from ./server-mods:/mods and the four kubejs source subdirs, and
add a new ./server-config:/data/config bind — mod config used to live only
inside the mc_data volume, unreachable from the host without root.

The config bind needs seeding: a bind mount shadows what is under it, so an
empty ./server-config hides the generated config and every mod falls back to
defaults. tooling/export-server-config.sh copies /data/config out of the
volume first, halting if the target is non-empty, if the container is still
running, or if the mc_data volume name is ambiguous.

Writable mounts also need the server's uid (itzg runs as 1000) to have write
access on the host dir; oxps' host user is 1002, hence MC_UID/MC_GID.

The kubejs ROOT still must not be mounted — BaseProperties.save() NPEs and
kubejs never initialises. That is about mounting the root at all, not about
the mode, so dropping :ro does not change it. It does mean kubejs writes now
land in $DISTRIBUTION_WEB_ROOT as uncommitted changes in that host's checkout.

Also documents how to reach the mc_data volume (exec/cp, resolved host path,
stop-before-write rule) in plan/05-minecraft.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
oierandClaude Opus 5 committed 2026-07-26 00:46:30 +02:00
1 parent dfb8719074
commit 92c6f5fc8f
7 files changed
+252 -8

No files matched your search

+16
View File
@@ -109,6 +109,22 @@ automatically; cosmetic-client mods that declare `BOTH` stay `both` until you
hand-edit `mods-sides.json` to `client`. No Modrinth/packwiz, no network at
classify/sync time. See `plan/04-mods.md`.
### Writable bind mounts — seed `./server-config` first
`/mods`, `/data/config` and the four kubejs source subdirs are mounted **rw**
(2026-07-26). Two consequences:
- `./server-config:/data/config` is a NEW bind — config used to live inside
`mc_data`. A bind mount **shadows** what's under it, so booting with an empty
`./server-config` makes every mod regenerate defaults. Run
`tooling/export-server-config.sh` once per host, server stopped, before that
deploy. The old files survive in `mc_data` under the mount, but the server
stops seeing them.
- itzg runs as **uid 1000**; a rw mount only works if the host dir permits it.
`cochi` is uid 1000, **`oxps` `oier` is uid 1002** → pass `MC_UID`/`MC_GID`.
- kubejs writes now land in `$DISTRIBUTION_WEB_ROOT` — i.e. as uncommitted
changes in that host's distribution checkout.
The kubejs ROOT still must not be mounted (NPE at init) — unchanged by the mode.
Full detail: `plan/05-minecraft.md` → "Writable bind mounts".
### Filestash (upload.) — four traps
Filestash serves at `upload.${BASE_DOMAIN}` (can't live under a subpath — SPA
hardcodes `/api`, `/assets`, `/login` at root). `files.${BASE_DOMAIN}` is a