Sign inSign up

b3n1254/minecraft-srv-java-fix

By b3n1254

Updated about 2 months ago

Java error fixed minecraft docker image. run and done!

Image
Web servers
0

2.5K

b3n1254/minecraft-srv-java-fix repository overview

minecraft-srv-java-fix

A production-ready Minecraft Java Edition server Docker image. Built on Ubuntu 24.04 with OpenJDK 21, pre-configured with Aikar's optimized JVM flags, full environment-variable configuration for server.properties, RCON support, health checks, graceful shutdown handling, and multi-architecture support for both amd64 and arm64.

Docker Hub: hub.docker.com/r/b3n1254/minecraft-srv-java-fix

docker.io/b3n1254/minecraft-srv-java-fix:<tag>
TagMC_VERSION baked inUse when
1.21.111.21.11Production pin matching an existing 1.21.11 world
26.1.226.1.2Newer stable; run scripts/upgrade-server.sh 26.1.2 after backup
latestMojang latest at build time (currently 26.1.2)Avoid for production — pin a version tag

Important: The image tag must match the MC_VERSION build arg. Local builds via Compose pass MC_VERSION automatically; when building manually, always set --build-arg MC_VERSION=<tag>.


Table of Contents


Quick Start

The fastest way to get a server running with sensible defaults:

docker run -d \
  --name minecraft-java \
  -p 25565:25565 \
  -v minecraft-data:/minecraft \
  -e MEMORY_MIN=4096M \
  -e MEMORY_MAX=4096M \
  --restart unless-stopped \
  b3n1254/minecraft-srv-java-fix:1.21.11

The server will:

  1. Copy server.jar into the /minecraft volume on first run
  2. Automatically write and accept eula.txt for you
  3. Generate server.properties with your environment variable settings
  4. Start the Minecraft server with Aikar's optimized JVM flags

Connect to your server at <your-host-ip>:25565.


Using Docker Compose is the recommended way to run this image. It keeps all your settings in one place and makes upgrades and restarts easy.

Create a docker-compose.yml:

services:
  minecraft:
    build:
      context: .
      args:
        MC_VERSION: "1.21.11"
    image: b3n1254/minecraft-srv-java-fix:1.21.11
    container_name: minecraft-java
    restart: unless-stopped
    ports:
      - "25565:25565"
      - "25575:25575"   # RCON — remove this line if you're not using RCON
    volumes:
      - minecraft-data:/minecraft
    environment:
      MEMORY_MIN: "1024M"
      MEMORY_MAX: "4096M"       # ~70-80% of your available system RAM
      MAX_PLAYERS: "20"
      MOTD: "My Minecraft Server"
      DIFFICULTY: "normal"
      GAMEMODE: "survival"
      VIEW_DISTANCE: "10"
      ONLINE_MODE: "true"
      PVP: "true"
      WHITELIST: "false"
      ENABLE_RCON: "false"
      # RCON_PASSWORD: set in .env when using docker-compose.rcon.yml
    stop_grace_period: 60s
    stdin_open: true
    tty: true

volumes:
  minecraft-data:

Then run:

docker compose up -d

View server logs:

docker compose logs -f

Stop the server gracefully:

docker compose stop

Image Details

PropertyValue
Base imageubuntu:24.04
Java versionOpenJDK 21 (latest from Ubuntu noble)
Minecraft versionTag-dependent: latest follows Mojang's newest stable release at build time; pinned tags (for example 1.21.11 or 26.1.2) keep a specific server version.
Runs asminecraft user (UID/GID 1001, non-root)
Architectureslinux/amd64, linux/arm64
Exposed ports25565 (game), 25575 (RCON)
Persistent volume/minecraft
Health checkTCP check on port 25565 every 30s
What's inside /minecraft (the volume)

Everything the server generates at runtime lives in the /minecraft directory, which is declared as a Docker volume. This means your data persists across container restarts, upgrades, and re-deploys. Here's what ends up there:

/minecraft/
├── server.jar          # copied from image on first run
├── eula.txt            # auto-accepted on first run
├── server.properties   # generated/patched from env vars on every start
├── world/              # overworld dimension data
│   ├── region/         # .mca chunk files
│   ├── entities/
│   └── ...
├── world_nether/       # nether dimension
├── world_the_end/      # end dimension
├── logs/
│   ├── latest.log      # current session log
│   └── *.log.gz        # rotated logs
├── ops.json            # operator list
├── whitelist.json      # whitelist
├── banned-players.json
├── banned-ips.json
└── usercache.json

Environment Variables

All configuration is done through environment variables passed to the container. No need to exec into the container or edit files manually.

Memory
VariableDefaultDescription
MEMORY_MIN1024MJVM minimum heap size (-Xms). Keep this equal to MEMORY_MAX to avoid heap resize pauses.
MEMORY_MAX2048MJVM maximum heap size (-Xmx). Set this to 70–80% of your available system RAM.

Sizing guide:

PlayersRecommended MEMORY_MAX
1–52048M
5–154096M
15–306144M
30–508192M
50+12288M+

These are rough guides. Heavy modpacks, large view distances, and lots of chunk generation all increase memory requirements.

-e MEMORY_MIN=2048M -e MEMORY_MAX=6144M

JVM Flags (Aikar's Flags)
VariableDefaultDescription
JVM_OPTSAikar's G1GC flags (see below)Full JVM argument string passed before -jar.

By default this image ships with Aikar's flags — the community standard for Minecraft Java server JVM tuning. They are applied automatically with no configuration required.

Aikar's G1GC flags are enabled by default for consistent Minecraft tick performance. Override JVM_OPTS only when you have a specific tuning reason.

Server Properties

These environment variables are written into server.properties every time the container starts. This means you can change any of them and just restart the container — no need to exec in or edit files.

Important: If you manually edit server.properties inside the volume for settings that aren't covered by these env vars, your manual edits will be preserved. These variables only override the specific keys they map to.

VariableDefaultserver.properties keyDescription
MAX_PLAYERS20max-playersMaximum number of players allowed on the server at once.
MOTDA Minecraft ServermotdThe message shown in the Minecraft server list below the server name. Supports color codes using \u00A7 (§).
DIFFICULTYnormaldifficultyGame difficulty. Options: peaceful, easy, normal, hard.
GAMEMODEsurvivalgamemodeDefault gamemode for new players. Options: survival, creative, adventure, spectator.
VIEW_DISTANCE10view-distanceHow many chunks around each player the server sends. Higher = more RAM and CPU. Range: 2–32.
SIMULATION_DISTANCE10simulation-distanceHow many chunks around each player are actively simulated (mobs, redstone, etc.). Can be set lower than view distance to save CPU.
ONLINE_MODEtrueonline-modeWhen true, the server authenticates players with Mojang's servers (requires a legitimate Minecraft account). Set to false for LAN play, offline play, or when using a proxy like Velocity or BungeeCord that handles auth itself.
PVPtruepvpWhether players can damage each other. Set to false for PvE-only servers.
LEVEL_NAMEworldlevel-nameThe name of the world folder. Changing this loads a different world (or creates a new one if it doesn't exist).
SEED(random)level-seedWorld generation seed. Leave blank for a random seed. Only used when the world is first generated.
WHITELISTfalsewhite-listWhen true, only players listed in whitelist.json can join. Manage the whitelist with /whitelist add <player> in the server console.
SPAWN_PROTECTION16spawn-protectionRadius in blocks around the world spawn point that non-operators cannot modify. Set to 0 to disable.
ALLOW_FLIGHTfalseallow-flightWhen false, the server kicks players detected as flying in survival mode. Set to true if you use mods/plugins that allow flight (e.g., Elytra boosters, flight mods).
MAX_TICK_TIME60000max-tick-timeMilliseconds before watchdog stops the server over a single tick. -1 disables (not recommended).
NETWORK_COMPRESSION_THRESHOLD256network-compression-thresholdCompress packets larger than N bytes. -1 disables compression.
ENTITY_BROADCAST_RANGE_PERCENTAGE100entity-broadcast-range-percentageLower to reduce entity sync load (e.g. 50).
STRICT_VERSION_MATCHfalse(startup only)Exit if image-bundled MC version ≠ volume server.jar version.
HEALTHCHECK_MODEtcp(healthcheck only)tcp (port open) or log (also require "Done" in latest.log).

Examples:

# Hard mode creative server, 50 players, custom MOTD
-e DIFFICULTY=hard \
-e GAMEMODE=creative \
-e MAX_PLAYERS=50 \
-e MOTD="Welcome to My Server!"

# Offline/LAN server with a specific seed
-e ONLINE_MODE=false \
-e SEED=8675309

# Low-resource server — reduce simulation load
-e VIEW_DISTANCE=8 \
-e SIMULATION_DISTANCE=6

RCON

RCON (Remote CONsole) lets you send commands to the server from outside the container without needing to attach to it. This is useful for automated backups, admin scripts, server management panels, and tools like mcrcon.

VariableDefaultDescription
ENABLE_RCONfalseSet to true to enable the RCON server.
RCON_PORT25575The port RCON listens on.
RCON_PASSWORD(unset)Required when RCON is enabled. Always set a strong password. Pass at runtime — do not hardcode in a committed compose file.

Enabling RCON:

-e ENABLE_RCON=true \
-e RCON_PASSWORD=your-strong-password-here

And expose the port:

-p 25575:25575

Connecting with mcrcon:

# Install mcrcon (Linux)
# Then connect:
mcrcon -H your-server-ip -P 25575 -p your-strong-password-here

# Run a command directly:
mcrcon -H your-server-ip -P 25575 -p your-password "list"
mcrcon -H your-server-ip -P 25575 -p your-password "op YourUsername"

Security note: If you're running this on a public-facing host, do not expose port 25575 publicly. Either firewall it and access via SSH tunnel, or bind it to localhost only:

ports:
  - "127.0.0.1:25575:25575"

Ports

PortProtocolPurpose
25565TCPMinecraft game traffic. This is what players connect to.
25575TCPRCON remote console. Only needed if ENABLE_RCON=true.

Persistent Data (Volumes)

All server data lives in /minecraft inside the container. This directory is declared as a Docker volume so it persists independently of the container lifecycle.

Named volume (recommended): Docker manages storage location automatically.

volumes:
  - minecraft-data:/minecraft

Upgrade note: server.jar is only copied into /minecraft on first run. If you upgrade the image later, an existing volume keeps its current server.jar until you replace or remove that file yourself. Back up the world first before changing server versions.

Existing-volume permission note: Docker usually creates a fresh named volume with workable permissions. If you attach an old volume or migrated data and see write errors from the startup script, fix ownership to UID/GID 1001:1001 on the host before restarting the container.

Bind mount: You control exactly where on the host the data lives. Useful for easy access to files from the host, or for existing world data.

docker run -v /home/youruser/minecraft-server:/minecraft ...

Bind mount permission note: The server runs as UID/GID 1001. If you use a bind mount, make sure the host directory is owned by UID 1001:

mkdir -p /home/youruser/minecraft-server
chown 1001:1001 /home/youruser/minecraft-server

Backing up your world:

# Stop the server first to ensure a clean backup
docker compose stop

# Archive the volume contents
docker run --rm \
  -v minecraft-data:/data \
  -v $(pwd):/backup \
  ubuntu tar czf /backup/minecraft-backup-$(date +%Y%m%d).tar.gz -C /data .

# Restart
docker compose start

Emergency world backups

When BACKUP_ON_START=true (the Compose default), the container creates and verifies two timestamped archives before starting Java:

  • Primary: /minecraft/backups/primary/ inside the persistent Minecraft volume
  • Secondary: /backups/emergency/ in the separate minecraft-backups Docker volume

Each archive has a SHA-256 sidecar and is tested with tar -tzf. BACKUP_RETENTION limits the number retained per tier; the default is 3. With BACKUP_ON_START_REQUIRED=true, a failed or unverifiable backup prevents the server from starting. Set it to false only when deliberately accepting that risk.

The secondary Docker volume protects against replacement of the Minecraft data volume, but it is still on the same Docker host/storage pool. For protection against disk failure, copy the secondary archives to another physical disk or host as a separate step.

Health Check

The image includes a Docker health check that tests whether the Minecraft port (25565) is accepting TCP connections every 30 seconds. The check allows a 3-minute startup window before it begins — this accounts for first-run world generation which can take 1–2 minutes.

HEALTHCHECK --interval=30s --timeout=10s --start-period=3m --retries=3

You can see the health status with:

docker ps
# or
docker inspect --format='{{.State.Health.Status}}' minecraft-java

Possible statuses:

  • starting — within the 3-minute startup grace period
  • healthy — server is up and accepting connections
  • unhealthy — port is not responding after 3 retries

Graceful Shutdown

When Docker stops the container (via docker stop, docker compose stop, or system shutdown), it sends SIGTERM to the server process. The startup script catches this signal and sends it to the Minecraft JVM, which triggers a clean world save before exit.

Docker Compose is configured with stop_grace_period: 30s, giving the server 30 seconds to finish saving before Docker force-kills it. Do not reduce this — world saves on large servers can take several seconds, and an abrupt kill can corrupt chunk data.

# Always stop gracefully — never use docker kill or docker compose kill
docker compose stop
# or
docker stop minecraft-java

RCON — Remote Console

With RCON enabled you can run any server command without opening a terminal session:

# List online players
mcrcon -H localhost -P 25575 -p yourpassword "list"

# Op a player
mcrcon -H localhost -P 25575 -p yourpassword "op Steve"

# Change difficulty live
mcrcon -H localhost -P 25575 -p yourpassword "difficulty hard"

# Broadcast a message
mcrcon -H localhost -P 25575 -p yourpassword "say Server restarting in 5 minutes!"

# Save the world manually
mcrcon -H localhost -P 25575 -p yourpassword "save-all"

Upgrading Minecraft Version (important)

The image bundles server.jar at build time. On first run, that jar is copied into the /minecraft volume. After that, the volume jar is what runs — pulling or rebuilding the image does not upgrade an existing server.

At startup, the container logs both versions:

==> Image bundles Minecraft 26.1.2
==> Volume server.jar is Minecraft 1.21.11
WARNING: Image version (26.1.2) != volume server.jar (1.21.11).

Set STRICT_VERSION_MATCH=true to refuse startup when they differ.

  1. Pin the image tag in docker-compose.yml to match your world (e.g. 1.21.11).
  2. Backup before any jar change:
    ./scripts/backup-world.sh
    
  3. Upgrade to a new tag (backs up, replaces server.jar, restarts):
    ./scripts/upgrade-server.sh 26.1.2
    
Test 26.1.2 without touching production
docker compose -f docker-compose.26.yml up -d
# Connect on port 25566

Operational Scripts

ScriptPurpose
./scripts/validate.shPre-flight: paths, compose, image tag alignment, health, Hub auth
./scripts/backup-world.shStop server, tar volume to ~/minecraft-backups/
./scripts/upgrade-server.sh <tag>Backup, replace server.jar from image tag, restart
./scripts/build-and-push.shMulti-arch build/push to b3n1254/minecraft-srv-java-fix on Docker Hub

Environment overrides: MINECRAFT_VOLUME, MINECRAFT_BACKUP_DIR, DOCKER_REPO, MINECRAFT_TAG.

Publish to Docker Hub
docker login
cd /path/to/minecraft
./scripts/validate.sh          # local checks first
PUSH=true ./scripts/build-and-push.sh

Pull on any host (multi-arch):

docker pull b3n1254/minecraft-srv-java-fix:1.21.11

RCON (secure setup)

Default compose does not publish port 25575. To enable RCON on localhost only:

cp .env.example .env
# Edit .env: ENABLE_RCON=true and RCON_PASSWORD=...
docker compose -f docker-compose.yml -f docker-compose.rcon.yml up -d

With RCON enabled, docker stop triggers save-all and stop via mcrcon before SIGTERM.


Building a Specific Minecraft Version

The Minecraft version is bundled at build time via the MC_VERSION build argument. By default it fetches the latest stable release from Mojang's API. To build a specific version:

docker build --build-arg MC_VERSION=1.20.4 -t my-minecraft:1.20.4 .

Valid values for MC_VERSION:

  • latest — always fetches the most recent stable release at build time (default)
  • Any specific release version string, e.g. 1.21.11, 1.20.4, 1.19.4

Note: Snapshot versions are not recommended for production servers as they can be unstable and worlds created on snapshots may not be compatible with future releases.


Multi-Architecture Support

This image is built for both linux/amd64 (standard x86-64 PCs and servers) and linux/arm64 (ARM 64-bit). Docker will automatically pull the correct variant for your machine.

This means you can run this image natively — without emulation — on:

  • Standard x86-64 Linux servers and desktops
  • Raspberry Pi 4 / 5 (running a 64-bit OS)
  • Oracle Cloud free tier ARM instances (Ampere A1)
  • AWS Graviton instances
  • Apple Silicon Macs running Linux VMs

Running as Non-Root

The server process runs as the minecraft user (UID 1001, GID 1001) inside the container. It does not run as root. This is a security best practice — if the server process were somehow compromised, the attacker would have limited access to the host system.

The /minecraft volume and /minecraft_core directory are both owned by the minecraft user. No root access is needed at runtime.


Advanced: Custom server.properties

The environment variables cover the most commonly changed settings. For settings not covered by env vars (e.g. spawn-monsters, generate-structures, max-tick-time, network-compression-threshold), you can edit server.properties directly inside the volume.

The startup script only writes the specific keys that have corresponding env vars — it does not overwrite the entire file. So your custom edits to other keys are safe and will persist across restarts.

To edit server.properties directly:

# Find where your named volume is stored
docker volume inspect minecraft-data

# Or exec into the container while it's running
docker exec -it minecraft-java bash
# then: nano /minecraft/server.properties

After editing, restart the container:

docker compose restart

Troubleshooting

Server won't start / crashes immediately

Check the logs:

docker logs minecraft-java
# or with compose:
docker compose logs -f minecraft

Common causes:

  • Out of memory: Increase MEMORY_MAX. Check docker stats minecraft-java to see actual memory usage.
  • Port already in use: Something else is using port 25565. Change the host port mapping: -p 25600:25565.
  • Volume permission issue: If using a bind mount, make sure the host directory is owned by UID 1001.
Players can't connect
  • Confirm the server is healthy: docker ps
  • Check your firewall allows port 25565 TCP inbound
  • Verify the correct IP address — on cloud providers, use the public IP, not the private one
  • If ONLINE_MODE=true, players need a valid Minecraft Java account
Server is laggy / low TPS
  • Increase MEMORY_MAX — the default 2048M is conservative
  • Reduce VIEW_DISTANCE and SIMULATION_DISTANCE
  • Check docker stats minecraft-java — if CPU is constantly at 100%, you may need more cores
  • The default Aikar's flags already handle GC tuning; do not override JVM_OPTS unless you have a specific reason
World got corrupted

This usually happens from a non-graceful shutdown (power loss, docker kill, OOM kill). To recover:

  • Restore from backup
  • Try Minecraft's built-in world repair: the server will attempt to fix corrupted chunks on startup
I want to run multiple servers

Change the host port for each instance:

# Server 1
ports:
  - "25565:25565"

# Server 2
ports:
  - "25566:25565"

Use separate named volumes for each:

volumes:
  - minecraft-server1:/minecraft
  # vs
  - minecraft-server2:/minecraft

EULA

By running this image you are agreeing to the Minecraft End User License Agreement. The image automatically writes eula=true to eula.txt on first run. Do not run this image if you do not agree to the EULA.


Hotfix Update Log

See CHANGELOG.md for detailed entries.

2026-05-26
  • Removed invalid JVM flag -XX:+PessimisticRefProcEnabled that blocked Java 21 startup.
  • Switched the Compose workflow to build: . so deployments use the corrected local image instead of a stale published one.
  • Published fresh multi-arch images for linux/amd64 and linux/arm64.
  • Documented upgrade behavior and old-volume ownership caveats.

Tags

Docker Hub tagMinecraft versionMulti-arch
1.21.111.21.11amd64, arm64
26.1.226.1.2amd64, arm64
latestMojang latest at build timeamd64, arm64

Repository: b3n1254/minecraft-srv-java-fix on Docker Hub.

Pin production to a version tag (not latest):

build:
  args:
    MC_VERSION: "1.21.11"
image: b3n1254/minecraft-srv-java-fix:1.21.11

Tag summary

Content type

Image

Digest

sha256:eb7a198d9

Size

174.3 MB

Last updated

about 2 months ago

docker pull b3n1254/minecraft-srv-java-fix