Java error fixed minecraft docker image. run and done!
2.5K
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>
| Tag | MC_VERSION baked in | Use when |
|---|---|---|
1.21.11 | 1.21.11 | Production pin matching an existing 1.21.11 world |
26.1.2 | 26.1.2 | Newer stable; run scripts/upgrade-server.sh 26.1.2 after backup |
latest | Mojang 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>.
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:
server.jar into the /minecraft volume on first runeula.txt for youserver.properties with your environment variable settingsConnect 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
| Property | Value |
|---|---|
| Base image | ubuntu:24.04 |
| Java version | OpenJDK 21 (latest from Ubuntu noble) |
| Minecraft version | Tag-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 as | minecraft user (UID/GID 1001, non-root) |
| Architectures | linux/amd64, linux/arm64 |
| Exposed ports | 25565 (game), 25575 (RCON) |
| Persistent volume | /minecraft |
| Health check | TCP check on port 25565 every 30s |
/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
All configuration is done through environment variables passed to the container. No need to exec into the container or edit files manually.
| Variable | Default | Description |
|---|---|---|
MEMORY_MIN | 1024M | JVM minimum heap size (-Xms). Keep this equal to MEMORY_MAX to avoid heap resize pauses. |
MEMORY_MAX | 2048M | JVM maximum heap size (-Xmx). Set this to 70–80% of your available system RAM. |
Sizing guide:
| Players | Recommended MEMORY_MAX |
|---|---|
| 1–5 | 2048M |
| 5–15 | 4096M |
| 15–30 | 6144M |
| 30–50 | 8192M |
| 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
| Variable | Default | Description |
|---|---|---|
JVM_OPTS | Aikar'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.
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.
| Variable | Default | server.properties key | Description |
|---|---|---|---|
MAX_PLAYERS | 20 | max-players | Maximum number of players allowed on the server at once. |
MOTD | A Minecraft Server | motd | The message shown in the Minecraft server list below the server name. Supports color codes using \u00A7 (§). |
DIFFICULTY | normal | difficulty | Game difficulty. Options: peaceful, easy, normal, hard. |
GAMEMODE | survival | gamemode | Default gamemode for new players. Options: survival, creative, adventure, spectator. |
VIEW_DISTANCE | 10 | view-distance | How many chunks around each player the server sends. Higher = more RAM and CPU. Range: 2–32. |
SIMULATION_DISTANCE | 10 | simulation-distance | How many chunks around each player are actively simulated (mobs, redstone, etc.). Can be set lower than view distance to save CPU. |
ONLINE_MODE | true | online-mode | When 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. |
PVP | true | pvp | Whether players can damage each other. Set to false for PvE-only servers. |
LEVEL_NAME | world | level-name | The name of the world folder. Changing this loads a different world (or creates a new one if it doesn't exist). |
SEED | (random) | level-seed | World generation seed. Leave blank for a random seed. Only used when the world is first generated. |
WHITELIST | false | white-list | When true, only players listed in whitelist.json can join. Manage the whitelist with /whitelist add <player> in the server console. |
SPAWN_PROTECTION | 16 | spawn-protection | Radius in blocks around the world spawn point that non-operators cannot modify. Set to 0 to disable. |
ALLOW_FLIGHT | false | allow-flight | When 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_TIME | 60000 | max-tick-time | Milliseconds before watchdog stops the server over a single tick. -1 disables (not recommended). |
NETWORK_COMPRESSION_THRESHOLD | 256 | network-compression-threshold | Compress packets larger than N bytes. -1 disables compression. |
ENTITY_BROADCAST_RANGE_PERCENTAGE | 100 | entity-broadcast-range-percentage | Lower to reduce entity sync load (e.g. 50). |
STRICT_VERSION_MATCH | false | (startup only) | Exit if image-bundled MC version ≠ volume server.jar version. |
HEALTHCHECK_MODE | tcp | (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 (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.
| Variable | Default | Description |
|---|---|---|
ENABLE_RCON | false | Set to true to enable the RCON server. |
RCON_PORT | 25575 | The 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"
| Port | Protocol | Purpose |
|---|---|---|
25565 | TCP | Minecraft game traffic. This is what players connect to. |
25575 | TCP | RCON remote console. Only needed if ENABLE_RCON=true. |
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.jaris only copied into/minecrafton first run. If you upgrade the image later, an existing volume keeps its currentserver.jaruntil 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:1001on 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
When BACKUP_ON_START=true (the Compose default), the container creates and verifies two timestamped archives before starting Java:
/minecraft/backups/primary/ inside the persistent Minecraft volume/backups/emergency/ in the separate minecraft-backups Docker volumeEach 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.
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 periodhealthy — server is up and accepting connectionsunhealthy — port is not responding after 3 retriesWhen 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
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"
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.
docker-compose.yml to match your world (e.g. 1.21.11)../scripts/backup-world.sh
server.jar, restarts):
./scripts/upgrade-server.sh 26.1.2
docker compose -f docker-compose.26.yml up -d
# Connect on port 25566
| Script | Purpose |
|---|---|
./scripts/validate.sh | Pre-flight: paths, compose, image tag alignment, health, Hub auth |
./scripts/backup-world.sh | Stop server, tar volume to ~/minecraft-backups/ |
./scripts/upgrade-server.sh <tag> | Backup, replace server.jar from image tag, restart |
./scripts/build-and-push.sh | Multi-arch build/push to b3n1254/minecraft-srv-java-fix on Docker Hub |
Environment overrides: MINECRAFT_VOLUME, MINECRAFT_BACKUP_DIR, DOCKER_REPO, MINECRAFT_TAG.
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
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.
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)1.21.11, 1.20.4, 1.19.4Note: 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.
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:
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.
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
Check the logs:
docker logs minecraft-java
# or with compose:
docker compose logs -f minecraft
Common causes:
MEMORY_MAX. Check docker stats minecraft-java to see actual memory usage.-p 25600:25565.healthy: docker psONLINE_MODE=true, players need a valid Minecraft Java accountMEMORY_MAX — the default 2048M is conservativeVIEW_DISTANCE and SIMULATION_DISTANCEdocker stats minecraft-java — if CPU is constantly at 100%, you may need more coresJVM_OPTS unless you have a specific reasonThis usually happens from a non-graceful shutdown (power loss, docker kill, OOM kill). To recover:
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
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.
See CHANGELOG.md for detailed entries.
-XX:+PessimisticRefProcEnabled that blocked Java 21 startup.build: . so deployments use the corrected local image instead of a stale published one.linux/amd64 and linux/arm64.| Docker Hub tag | Minecraft version | Multi-arch |
|---|---|---|
1.21.11 | 1.21.11 | amd64, arm64 |
26.1.2 | 26.1.2 | amd64, arm64 |
latest | Mojang latest at build time | amd64, 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
Content type
Image
Digest
sha256:eb7a198d9…
Size
174.3 MB
Last updated
about 2 months ago
docker pull b3n1254/minecraft-srv-java-fix