← Back to Wiki
Self-Hosting / Minecraft

Self-Hosting Minecraft Java With Legacy Forge Mods (1.7.10)

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.

Share on X

Why a Bedrock server can't just add the mod

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.

Gotcha: the default server image ships a JDK too new for a 2014-era Forge build

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.

Minimal working compose file:
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.

Installing an old mod when CurseForge blocks the easy path

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 57375435737/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.

Rescuing a player from a mod dimension with no way back

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:

  1. Set 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.
  2. /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.
  3. Having the player run bare /kill themselves (no argument needed) failed with a permission error, since /kill defaults to operator level 2 and the player wasn't opped.
  4. Console-side lethal damage via /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.
What actually worked: temporarily /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.