Not every Minecraft mod ever got ported forward. When the mod you actually want only exists for a Java Forge build from 2014, running it means a dedicated legacy server, not an update to whatever you're already hosting — see the Bedrock and modern Fabric guides for the other two flavors this same homelab runs side by side. Here's what actually broke getting a 1.7.10 Forge server running in Docker, and a real rescue mission when a player got stuck somewhere the mod never expected anyone to survive.
Bedrock and Java Edition are different games under the hood — different engine, different codebase, no shared mod ecosystem. A mod built for Java Forge simply cannot run on a Bedrock server, no matter how the Bedrock side is configured. If the mod you want only exists for Java, and your existing server is Bedrock, the only real option is standing up a second, dedicated Java server just for it.
Using itzg/minecraft-server (a well-known Docker image for running Minecraft servers) without
pinning a version tag pulls in whatever JDK the image currently defaults to — which for a modern pull is far
newer than Java 8. Forge's LaunchWrapper for 1.7.10 hard-casts the JVM's system classloader to
java.net.URLClassLoader:
java.lang.ClassCastException: class jdk.internal.loader.ClassLoaders$AppClassLoader
cannot be cast to class java.net.URLClassLoader
That cast only works on Java 8 — the classloader model changed with the Java 9+ module system, and nothing
about that is Forge-version-specific fixable; it's a hard incompatibility. Fix: pin the image tag
explicitly to java8, not the default/latest tag. For any Forge version below roughly
1.18, this tag is required regardless of what the base image currently defaults to — and that default has a
way of quietly moving forward over time, so don't assume last year's compose file still pulls Java 8 today.
services:
mc:
image: itzg/minecraft-server:java8
container_name: mc-java
ports:
- "25566:25565"
environment:
EULA: "TRUE"
TYPE: "FORGE"
VERSION: "1.7.10"
MEMORY: "4G"
MOTD: "Homelab Java Server"
volumes:
- ./data:/data
restart: unless-stopped
stdin_open: true
tty: true
The image's own auto-installer (TYPE: FORGE, VERSION: 1.7.10) handled the actual
Forge install cleanly on this run. Worth knowing going in: legacy Forge installers on this image have a
documented history of flaking on old vanilla-jar downloads (HTTP 403s from Mojang's legacy asset hosting,
install-time NullPointerExceptions) — not something hit here, but a plausible failure mode for other old
Forge versions, worth a retry before assuming something's fundamentally broken.
CurseForge now requires an API key for their official API and blocks straightforward scraping of a mod's
download page — hitting it directly returns an HTML page, not the jar file, even with a normal browser
user-agent. The CDN's direct file path still works without any authentication, though, once you know a
mod file's numeric file ID (visible on the mod's CurseForge page). Split the ID as
{fileID // 1000}/{fileID % 1000}:
https://edge.forgecdn.net/files/5737/543/ExampleMod-1.7.10-2.4.0.jar
(file ID 5737543 → 5737/543). Download that directly and drop it into the mods
folder bound to the container's /data/mods, then restart the container. Confirmed in the
startup log that the mod loaded correctly (Forge Mod Loader lists every mod it identified on boot) before
calling it done — a silently-missing mod jar looks identical to a successfully-started vanilla server until
someone actually checks the log or tries to use the mod's content in-game.
A Forge mod's own custom dimension can be a one-way trip if the mod's author never built an exit — this happened for real, with a player dropped into a mod-added pocket dimension by one of the mod's own abilities, and no in-mod portal, item, or command to get back. Getting them out took several real wrong turns on a Minecraft version old enough that some assumptions about how commands behave simply don't hold:
gamerule keepInventory true before forcing any death. It defaults to
false, so a careless kill would drop the player's entire inventory somewhere they can't get
back to retrieve it. Set it back to false immediately after — it's easy to forget the second
half of this step./kill <player> from the console silently fails on 1.7.10 — no error,
no exception logged, the RCON connection just resets before the command executes. The real cause:
1.7.10's /kill command takes no arguments at all; multiplayer-target support for
/kill was added in a later Minecraft version. Passing an argument a command doesn't expect
broke the RCON round-trip silently rather than erroring cleanly — worth remembering as a class of failure,
not just a one-off./kill themselves (no argument needed) failed
with a permission error, since /kill defaults to operator level 2 and the player wasn't
opped./effect (Instant Damage) failed too — silently,
twice, even at a very high amplifier. The server's default game mode was Creative, and Creative
mode is fully immune to potion-effect damage regardless of amount — no error, the effect just does
nothing to a Creative-mode player./op the player, have them run bare
/kill on themselves once opped, then immediately /deop them. Confirmed via the
server log's own death message ("fell out of the world" — the vanilla message for the special
world-boundary damage source /kill applies internally). That's also why self-/kill
succeeds in Creative mode when a normal damage effect never can — it isn't routed through the normal damage
system at all. Restore keepInventory to its original value immediately after, and confirm the
gamerule actually reads back the value expected.
The general lesson generalizes past this one mod: on an old, legacy modded server, don't assume a command
that "should" work on a modern server behaves the same way here. Check /help <command>
for the real argument signature this specific version actually supports, and treat a command that silently
resets an RCON connection with no logged error as a sign the command itself is malformed for this version —
not a transient connectivity problem worth retrying blindly.