PLAN - Runic Engine Minecraft Server
PLAN: Runic Engine Minecraft Server
Phase 1: Gather Version Information from CurseForge Client
Before building the server, you need to know the exact Minecraft version, Forge version, and modpack version your clients are running.
How to Find Your CurseForge Client Versions
Step 1: Find the Modpack Instance Folder
- Open the CurseForge App
- Go to My Modpacks tab
- Click the Runic Engine modpack icon (not the Play button)
- Click the three dots (⋮) or gear/settings icon on the right side
- Click "Open Folder" — this opens the modpack's install directory in your file explorer
Step 2: Find the Minecraft Version
- On the modpack's CurseForge page (or in the app's modpack details), the Minecraft version is displayed prominently (e.g.,
1.20.1) - Alternatively, in the instance folder, open
minecraftinstance.json— look for thegameVersionfield
Step 3: Find the Forge Version
- In the instance folder, look for a file called
minecraftinstance.jsonor check themanifest.json - The Forge version will appear in the modloader section, e.g.,
forge-47.4.0 - You can also check the CurseForge app: click the modpack → look at the Versions tab — it will list the Forge version required
- Another method: in the instance folder, check the
mods/directory — look forforge-*in file names, or checkversion.jsoninside the.minecraft/versions/subfolder
Step 4: Find the Mods Folder
- From the instance folder opened in Step 1, the mods are in the
mods/subdirectory - Full typical path on Windows:
C:\Users\<username>\curseforge\minecraft\Instances\Runic Engine\mods\ - Full typical path on macOS/Linux:
~/curseforge/minecraft/Instances/Runic Engine/mods/
Step 5: Confirm Modpack Version
- In the CurseForge app, the modpack version number is shown on the modpack tile (e.g.,
1.6.0) - Both players must be on the same modpack version
Phase 2: Build the Docker Compose Configuration
docker-compose.yaml
Create /home/ejm3/docker_storage/runic_engine/docker-compose.yaml with:
- Image:
itzg/minecraft-server:java17(Java 17 is required for Forge 1.20.1) - Type:
FORGE - VERSION: Match the Minecraft version from Phase 1 (e.g.,
1.20.1) - FORGE_VERSION: Match the Forge version from Phase 1 (e.g.,
47.4.0) - Mods bind mount:
/nfs_storage/minecraft_mods/runic→/mods-src(read-only) - COPY_MODS_SRC:
/mods-src— the itzg image will copy mods from here into/data/modsat startup - REMOVE_OLD_MODS:
true— clears old mods before copying - MEMORY:
8G(sufficient for 2 players with 230+ mods) - Resource limits: 10G memory limit, 3 CPUs
Environment Variables Summary
EULA: "TRUE"
TYPE: FORGE
VERSION: "1.20.1" # UPDATE from client
FORGE_VERSION: "47.4.0" # UPDATE from client
MEMORY: 8G
USE_AIKAR_FLAGS: "true"
MOTD: "Runic Engine Server"
DIFFICULTY: normal
MAX_PLAYERS: 5
VIEW_DISTANCE: 10
COPY_MODS_SRC: /mods-src
REMOVE_OLD_MODS: "true"
ENABLE_COMMAND_BLOCK: "true"
SPAWN_PROTECTION: 0
TZ: America/Chicago
Phase 3: Handle Client-Only Mods
When players copy their mods folder to the server, client-only mods must be removed or excluded because they reference client-side classes (rendering, input, audio) that don't exist on a dedicated server, causing NoClassDefFoundError crashes.
Common Client-Only Mods to Exclude
These are well-known client-only mods frequently found in Forge 1.20.1 modpacks. Remove these from the server's mods directory:
| Mod | Why It's Client-Only |
|---|---|
| OptiFine | Rendering optimization, shaders |
| Oculus | Shader support (Iris fork for Forge) |
| Rubidium | Rendering optimization (Sodium fork for Forge) |
| Embeddium | Rendering optimization (another Sodium fork) |
| Iris Shaders | Shader support (Fabric, but sometimes present) |
| Sodium | Rendering optimization (Fabric, but sometimes present) |
| Better Fps | Client-side FPS optimization |
| FancyMenu | Custom main menu screens |
| CustomMainMenu | Custom main menu screens |
| Controlling | Keybinding UI improvements |
| Configured | Client config GUI |
| BetterF3 | Enhanced F3 debug screen |
| Toast Control | Hides client toast notifications |
| Blur (Blur API) | GUI blur effects |
| Ding | Plays a sound when MC finishes loading |
| Sound Physics Remastered | 3D audio / reverb |
| Presence Footsteps | Enhanced footstep sounds |
| Dynamic Surroundings | Client-side ambient sounds/weather |
| ReAuth / OAuth | Re-authentication helpers |
| Mod Menu (Fabric) | Mod list GUI |
| Light Overlay | Renders light level overlays |
| AppleSkin | Client-side HUD for food values |
| Xaero's Minimap | Minimap rendering (has a server companion but the client mod alone crashes) |
| Xaero's World Map | World map rendering |
| JourneyMap | Minimap/world map (client component) |
| Mouse Tweaks | Inventory mouse improvements |
| Inventory HUD+ | HUD overlays |
| ItemPhysic | Dropped item rendering |
| NotEnoughAnimations | Extra player animations |
| Skin Layers 3D | 3D skin layer rendering |
| Entity Culling | Rendering optimization |
| FerriteCore | Memory optimization (has separate client/server versions — use only the server version) |
| Smooth Boot | Client-side startup optimization |
| Shutup Experimental Settings | Suppresses client warning screen |
| Catalogue | Mod list GUI replacement |
| Cloth Config | Config screen API (often client-only) |
| Just Enough Items (JEI) | Recipe viewer — usually works on both sides, but check if your version is client-only |
Note on JEI/REI: These recipe viewers often work on both client and server. They typically won't crash the server, but they're unnecessary there. If in doubt, keep them — they'll just be ignored.
Note on map mods (JourneyMap, Xaero's): Some have server-side companion plugins. The main
.jaris usually client-only. Check the mod's CurseForge page for "server" vs "client" labels.
How to Identify Other Client-Only Mods
If a mod not on this list crashes the server, here's how to identify it:
Method 1: Check the CurseForge Page
- Go to the mod's CurseForge page
- Look at the "Environment" or "Side" field in the mod details sidebar
- It will say:
Client,Server, orBoth - If it says Client — remove it from the server
Method 2: Check the mod's mods.toml
- Open the
.jarfile (it's a zip) with any archive tool - Navigate to
META-INF/mods.toml - Look for a
sidefield:side = "CLIENT"→ client-only, remove from serverside = "BOTH"→ safe on serverside = "SERVER"→ server-only
Method 3: Read the Server Crash Log
- If the server crashes on startup, check
logs/latest.logorcrash-reports/ - Look for errors like:
NoClassDefFoundError: net/minecraft/client/...ClassNotFoundException: ...client...Mod <modname> requires client-side classes
- The mod name will be in the stack trace — remove that mod's
.jarand restart
Method 4: Binary Search
- If you can't identify the offending mod from logs:
- Remove half the mods, try starting the server
- If it works, the problem mod is in the removed half
- Repeat until you find the specific mod
Implementation: Exclusion Script
Rather than manually deleting mods each time, we'll create a small script or use the itzg image's built-in exclusion:
Option A: REMOVE_OLD_MODS_EXCLUDE + manual removal
- Use
COPY_MODS_SRCwithREMOVE_OLD_MODS=true - Add a startup script that deletes known client-only jars after copy
Option B: Curated server mods directory
- Instead of copying the full client mods folder, maintain a separate "server-safe" copy
- Script that copies mods from the NFS share and removes known client-only ones
We will implement Option B with a shell script (filter-mods.sh) that:
- Copies all mods from the NFS bind mount
- Deletes any matching a known exclusion list
- Runs before the server starts
Phase 4: Directory Structure
/home/ejm3/docker_storage/runic_engine/
├── docker-compose.yaml
├── filter-mods.sh # Script to remove client-only mods
├── client-only-mods.txt # List of client-only mod filenames to exclude
├── docs/
│ ├── PRD.md
│ └── PLAN.md
└── server-data/ # (gitignored) server world data, configs, etc.
/nfs_storage/minecraft_mods/runic/ # Players copy their mods here
├── mod1.jar
├── mod2.jar
├── oculus-1.20.1.jar # ← will be filtered out by script
├── rubidium-1.20.1.jar # ← will be filtered out by script
└── ...
Phase 5: Create Files
5.1 docker-compose.yaml
Write the compose file with:
itzg/minecraft-server:java17image- Forge type with version pinning
- NFS mods bind mount (read-only) at
/mods-src - Named volume for server data
filter-mods.shmounted and used as a custom entrypoint wrapper- Resource limits (10G memory, 3 CPUs)
- Standard server settings (MOTD, difficulty, etc.)
5.2 filter-mods.sh
Startup script that:
- Copies all
.jarfiles from/mods-src/to/data/mods/ - Reads
/client-only-mods.txtfor patterns - Removes any matching jars from
/data/mods/ - Logs what was removed
- Executes the original entrypoint
5.3 client-only-mods.txt
A simple text file with one pattern per line (case-insensitive glob patterns):
optifine*
oculus*
rubidium*
embeddium*
fancymenu*
custommainmenu*
controlling*
configured*
betterf3*
toastcontrol*
blur*
ding*
soundphysics*
presencefootsteps*
dynamicsurroundings*
reauth*
lightoverlay*
mousetweaks*
inventoryhud*
itemphysic*
notenoughanimations*
skinlayers3d*
entityculling*
smoothboot*
shutupexperimentalsettings*
catalogue*
5.4 README with player instructions
A brief README in the project root with:
- How to find CurseForge version info (from Phase 1)
- How to copy mods to the NFS share
- How to connect to the server
- Troubleshooting tips
Phase 6: Testing & Validation
- Verify the Forge version and Minecraft version match the clients
- Start the server with
docker compose up -d - Check logs:
docker compose logs -f— confirm Forge loads and mods are accepted - If crash occurs, check the crash log for client-only mod references (Phase 3 methods)
- Have both players connect and verify they can join
- Confirm the MOTD and server settings are correct
Implementation Order
- Get version info from a client (Phase 1) — requires player input
- Create docker-compose.yaml (Phase 5.1)
- Create filter-mods.sh (Phase 5.2)
- Create client-only-mods.txt (Phase 5.3)
- Have a player copy mods to NFS share
- Start server and iterate (Phase 6) — remove additional client-only mods as discovered
- Write README (Phase 5.4) — after everything works