minecraft-server/docs/ITZG-CUSTOM-JAR-PERSISTENCE.md
s8n 4116d67eaf feat(shop): stage ExcellentShop+CoinsEngine migration bundle
Replaces EZShop 1.0-SNAPSHOT (bespoke, sell-only, 27-slot) and Kiranhart
AuctionHouse 1.4.6 (ARR no-LICENSE, dupe history) with a single GPL-3
stack: ExcellentShop 5.0.1 + CoinsEngine 2.7.0 + nightcore 2.15.3.

Per SHOP-SYSTEM-DECISION.md (commit 9565f0b), Stack A wins on three
counts: GPL-3 source (vs ARR/proprietary), unified theme across shop
and AH, single-vendor support story under NightExpress.

Jars sourced from upstream Reposilite repo.nightexpressdev.com — same
artefacts a local mvn package would produce, just reproducible without
the alex9849 integration that breaks on TLS handshake. SHA256SUMS
committed for receipt; never re-fetched at swap time.

Bundle:
  build/    — three jars + SHA256SUMS (~2.1MB total)
  configs/  — post-first-run overrides (chest module off, /shop alias,
              AH 1% tax + BIN+bid + 10 listings matches Kiranhart)
  scripts/  — swap.sh, rollback.sh, lp-shop-migration.sh,
              lp-shop-rollback.sh, docker-compose.patch.yml

itzg integration: COPY_PLUGINS_SRC=/plugins-custom mount per
ITZG-CUSTOM-JAR-PERSISTENCE.md, plus REMOVE_OLD_MODS_EXCLUDE expanded
with the three new globs.

LP migration grants default-tier excellentshop.* + coinsengine.*
nodes; staff tier gets *.admin equivalents to legacy
auctionhouse.moderator. Group prefixes/suffixes untouched per
feedback_lp_prefixes_locked.md.

DOES NOT touch live /data/plugins/EZShop or /data/plugins/AuctionHouse
— staging only. Operator runs swap.sh in scheduled maintenance window.

Refs: SHOP-SYSTEM-DECISION.md, AUDIT-2026-05-07.md F-11,
ITZG-CUSTOM-JAR-PERSISTENCE.md, MIGRATION-PLAN-EXCELLENTSHOP.md.
2026-05-08 00:01:53 +01:00

5.1 KiB

itzg/minecraft-server — Custom Plugin Jar Persistence

Date: 2026-05-07 Context: ChatChat-1.0.0-SNAPSHOT-racked-1.jar was dropped manually into /data/plugins/ and disappeared after the next container restart.


1. Why the ChatChat jar disappeared

itzg's entrypoint runs this sequence on every start (when REMOVE_OLD_MODS=TRUE):

  1. Wipe — every file in /data/plugins/ matching REMOVE_OLD_MODS_INCLUDE and not matching REMOVE_OLD_MODS_EXCLUDE is deleted.
  2. Download — every URL in PLUGINS and every Modrinth project in MODRINTH_PROJECTS is fetched into /data/plugins/.
  3. Copy — anything listed in COPY_PLUGINS_SRC (or container paths inside PLUGINS) is rsynced in.

Our current compose has:

REMOVE_OLD_MODS: "true"
REMOVE_OLD_MODS_INCLUDE: "*.jar"
REMOVE_OLD_MODS_EXCLUDE: "AuthLimbo*.jar"

So step 1 deletes every *.jar except AuthLimbo*.jar. The hand-placed ChatChat jar was not in any download list and not in the exclude glob, so it was nuked and never re-downloaded. AuthLimbo survives only because we explicitly excluded it.

This is documented behaviour, not a bug — itzg's design assumes plugins are declarative, sourced from URLs/Modrinth/COPY_PLUGINS_SRC, never hand-dropped.

2. Three mechanisms to make custom jars persist

# Mechanism How
A REMOVE_OLD_MODS_EXCLUDE glob Add ChatChat*.jar to the exclude list. Quick but fragile — depends on filename and only protects already-present files; doesn't handle re-deploy on a fresh volume.
B COPY_PLUGINS_SRC bind-mount Mount a host dir of custom jars read-only at e.g. /plugins-custom, set COPY_PLUGINS_SRC=/plugins-custom. Entrypoint copies them in after the wipe. Survives wipes, version-controllable, declarative.
C PLUGINS URL → Forgejo Release Upload the jar as a Forgejo Release asset, add the download URL to the existing PLUGINS list. Same flow as EssentialsX/spark already use.

Note: PLUGINS also accepts container paths directly (e.g. PLUGINS=/plugins-custom/ChatChat.jar), so mechanism B can collapse into the existing PLUGINS env if preferred.

COPY_PLUGINS_SRC is the cleanest fit:

  • Custom jars live in the repo (or /opt/docker/minecraft-custom-plugins/), so they're under version control / backup.
  • No external host dependency (Forgejo could be down — bind mount can't be).
  • Build artefacts from staging/chatchat/ drop straight into the mounted dir.

docker-compose.yml diff

       MODRINTH_LOADER: paper
       SPIGET_RESOURCES: ""
       REMOVE_OLD_MODS: "true"
       REMOVE_OLD_MODS_INCLUDE: "*.jar"
-      REMOVE_OLD_MODS_EXCLUDE: "AuthLimbo*.jar"
+      REMOVE_OLD_MODS_EXCLUDE: "AuthLimbo*.jar,ChatChat*.jar"
+      COPY_PLUGINS_SRC: "/plugins-custom"
     volumes:
       - /opt/docker/minecraft:/data
+      - /opt/docker/minecraft-custom-plugins:/plugins-custom:ro

Then on the host:

sudo mkdir -p /opt/docker/minecraft-custom-plugins
sudo cp staging/chatchat/ChatChat-1.0.0-SNAPSHOT-racked-1.jar \
        /opt/docker/minecraft-custom-plugins/
sudo cp /opt/docker/minecraft/plugins/AuthLimbo-*.jar \
        /opt/docker/minecraft-custom-plugins/   # optional: source-of-truth
sudo chown -R 1000:1000 /opt/docker/minecraft-custom-plugins
docker compose up -d --force-recreate mc

The EXCLUDE line still lists ChatChat*.jar so that if the bind mount ever vanishes, an existing copy in /data/plugins/ isn't wiped — belt and braces.

4. Bonus — Forgejo Release upload procedure (mechanism C)

If you'd rather host the jar at git.s8n.ru (e.g. for cobblestone or a friend's box without the bind mount):

# 1. Tag and push
cd staging/chatchat
git tag -a chatchat-racked-1 -m "ChatChat racked build 1"
git push origin chatchat-racked-1

# 2. Create release + upload asset (uses Forgejo PAT from ~/.config/veilor-forgejo)
TOKEN=$(cat ~/.config/veilor-forgejo/pat)
curl -X POST \
  -H "Authorization: token $TOKEN" \
  -H "Content-Type: application/json" \
  https://git.s8n.ru/api/v1/repos/s8n/minecraft-server/releases \
  -d '{"tag_name":"chatchat-racked-1","name":"ChatChat racked-1","draft":false}'

RELEASE_ID=$(curl -s -H "Authorization: token $TOKEN" \
  https://git.s8n.ru/api/v1/repos/s8n/minecraft-server/releases/tags/chatchat-racked-1 \
  | jq -r .id)

curl -X POST \
  -H "Authorization: token $TOKEN" \
  -H "Content-Type: application/java-archive" \
  --data-binary @ChatChat-1.0.0-SNAPSHOT-racked-1.jar \
  "https://git.s8n.ru/api/v1/repos/s8n/minecraft-server/releases/$RELEASE_ID/assets?name=ChatChat-1.0.0-SNAPSHOT-racked-1.jar"

Then add to compose:

PLUGINS: |
  ...existing...
  https://git.s8n.ru/s8n/minecraft-server/releases/download/chatchat-racked-1/ChatChat-1.0.0-SNAPSHOT-racked-1.jar  

References