← Back to Wiki
Game Servers / Minecraft

Self-Hosting Minecraft Java with a Modern Fabric Modpack

Running Minecraft Java on a current version with an actively-maintained superhero-themed modpack, as a companion to an older server frozen on a legacy Forge modpack that can never be updated (see that guide for why, and the Bedrock guide if you're running both editions). Fabric on 1.20.1 needed real dependency-resolution work, and produced one crash with two completely unrelated causes that happened to share the exact same error string.

Share on X

Why a second server instead of upgrading an existing one

A legacy Forge modpack frozen on Minecraft 1.7.10 has no upgrade path. The mod that makes it worth running was never ported past that version, confirmed against the mod's own CurseForge and Modrinth project pages. Third-party mod-listing sites do claim broader version support than is true. Rather than lose the theme entirely, this is a from-scratch build on a current version with the closest actively maintained equivalent. Not an attempted in-place upgrade.

Storage: put I/O-heavy new builds on NFS, not local disk

This server's LXC was deliberately built on NFS-backed storage over a fast wired link to a NAS, not the local ZFS pool other VMs and containers on the same hypervisor share. The reason was not theoretical. The same night, heavy sustained writes from an unrelated large-file export job on another VM sharing that local pool caused enough I/O contention to crash a completely different game server also sharing it. Connected players got kicked with a generic "server down" error, and the process itself never restarted.

BE WARNED: shared local storage is a real, easy-to-miss failure mode. Two VMs with zero logical relationship take each other down when they share the same physical disk pool and one does a sustained heavy write. If you are about to build a VM or LXC that will do meaningful sustained I/O, put it on separate storage from your existing always-on workloads rather than the default local pool. A game server's world saves, a model export, anything writing gigabytes continuously.

The server itself

Runs on the itzg/minecraft-server Docker image with no special Java-version pinning. 1.20.1 requires Java 17+ anyway, which the image's default tag bundles:

services:
  mc:
    image: itzg/minecraft-server
    container_name: mc-modern
    ports:
      - "25565:25565"
    environment:
      EULA: "TRUE"
      TYPE: "FABRIC"
      VERSION: "1.20.1"
      MEMORY: "4G"
    volumes:
      - ./data:/data
    restart: unless-stopped
    stdin_open: true
    tty: true

Resolving a modpack's full dependency tree via the Modrinth API

Modrinth's CDN allows direct, unauthenticated downloads, unlike CurseForge, which blocks straightforward scraping. The project's version API resolves the latest compatible release and its declared dependencies:

curl -s "https://api.modrinth.com/v2/project/<slug>/version" \
  -H "User-Agent: your-app-name/1.0"
BE WARNED: a mod's own platform dependency can have a confusingly generic name. One dependency in this pack's tree was literally the base name of the mod family. Distinct from the addon mod itself, and easy to confuse with it. Resolve every dependency ID individually via /v2/project/<id> rather than assuming the version list's dependency names are self-evident. It is an easy way to end up one jar short.

The crash with two unrelated causes: Unknown recipe serializer

A player hit a decoder crash on connect, referencing an unknown recipe serializer from a weapons-mod dependency. It looked like a client-side mod mismatch. The first cause was server-side.

Cause 1: a missing "extensions" directory, distinct from the mods folder

The weapons mod expects an optional third-party content-pack directory separate from the normal mods folder, and it did not exist. The pack's own item tags reference dozens of specific weapon IDs that only exist when the matching content packs are installed. The directory was missing, so none of those items registered. That broke enough of the recipe and tag graph that syncing recipes to a connecting client threw a decoder error.

BE WARNED: an ERROR-level log line at boot, not a crash, hid the real cause. The missing-directory error appeared only as a non-fatal stack trace during startup, easy to scroll past. It surfaced later as an unrelated-looking client-side decode error. For any mod with an optional "extensions" or "content pack" system separate from the main mods folder, check the server log for this class of error before you assume a client-side mismatch.

Fix: create the expected directory and drop the missing content-pack archives into it as zips. Some loaders scan the archives directly rather than expecting them unpacked. Then restart the container. The server log went from dozens of missing-reference errors to a clean boot.

Cause 2: the same error string, from a genuinely separate root cause

After the extensions fix, a different player hit the exact same error message. This was a second, independent cause producing identical output. A modded Fabric server syncs its recipe data to every connecting client automatically. A custom recipe serializer, the code that knows how to parse a given recipe type, only exists if the client also has the mod that registers it. The extensions fix made the server's recipe graph more complete, so more valid recipes reached clients than before. That made a pre-existing gap in the client-side setup instructions show up far more reliably.

Lesson: do not assume "modern Minecraft version" means "no client-side setup". Whether a server needs matching client-side mods depends on whether it is modded, not on how recent the Minecraft version is. Two separate questions. Conflating them produced genuinely wrong setup instructions that stayed wrong until a second player hit the same crash for a different reason.

The real fix was not a server change at all. It was writing correct client setup documentation. Bundle the exact mod jars the server runs into one distributable zip, matched to the exact Fabric loader version running server-side. Pull that version off the server, do not guess it from an installer version number.

Benign warnings that aren't bugs

Expect @Mixin target ... was not found warnings on boot, for client-only rendering classes that do not exist in a headless dedicated server. Mods built for both client and server skip those mixins server-side. Not a sign of a broken install. Only worth investigating if the server fails to reach a clean Done (...) boot line.