Skip to content
MC Images

Docker Compose

Run the Minecraft server images with Docker Compose, with a complete compose.yaml, mods and plugins, memory limits, several servers side by side, updates and backups.

Docker Compose keeps the whole server definition (image tag, ports, settings and storage) in one compose.yaml that you can commit and reuse. Every example below works with any of the images; change the image and the selection variable to match your server.

A complete compose.yaml

services:
  minecraft:
    image: ghcr.io/hambn/minecraft-server-paper:26.2
    ports:
      - "25565:25565"
    environment:
      EULA: "TRUE"            # accepts the Minecraft EULA (Java servers only)
      MEMORY: "4G"            # heap size, -Xms and -Xmx
      PLUGINS: "luckperms,essentialsx"
      PROP_MOTD: "My server"  # any server.properties key, see Configuration
      PROP_MAX_PLAYERS: "20"
    volumes:
      - minecraft-data:/data
    stop_grace_period: 2m     # time to save the world on shutdown
    restart: unless-stopped

volumes:
  minecraft-data:

Start it, follow the log, and stop it:

docker compose up -d
docker compose logs -f minecraft
docker compose down

docker compose down sends SIGTERM and waits up to stop_grace_period while the server saves the world. The named volume minecraft-data survives down. Only docker compose down -v deletes it.

Choose the image and tag

Server Image Newest tag Selection variable
Fabric ghcr.io/hambn/minecraft-server-fabric 26.3 MODS
NeoForge ghcr.io/hambn/minecraft-server-neoforge 26.2 MODS
Paper ghcr.io/hambn/minecraft-server-paper 26.2 PLUGINS
Pumpkin ghcr.io/hambn/minecraft-server-pumpkin 26.3 PLUGINS

Use an exact Minecraft version tag rather than latest. A version tag gets every automatic rebuild for that release (loader, base image and mod updates) but never moves your world to a newer Minecraft version. The image pages list the versions each image maintains.

Enable mods or plugins

Everything in the image’s catalog is inactive until you list its ID. The valid IDs for each version are on that version’s page.

services:
  minecraft:
    image: ghcr.io/hambn/minecraft-server-fabric:26.3
    environment:
      EULA: "TRUE"
      MEMORY: "6G"
      MODS: "lithium,ferrite-core,spark"

Required dependencies are added automatically. If an ID is unknown or unsupported, the container prints an error and exits before the server starts. Check the result with docker compose logs minecraft.

Keep settings in an env file

Move the settings out of compose.yaml into a .env-style file that you can keep out of version control:

# minecraft.env
EULA=TRUE
MEMORY=4G
PLUGINS=luckperms,essentialsx,worldedit
PROP_MOTD=My server
PROP_ONLINE_MODE=true
services:
  minecraft:
    image: ghcr.io/hambn/minecraft-server-paper:26.2
    env_file: minecraft.env
    ports:
      - "25565:25565"
    volumes:
      - minecraft-data:/data
    stop_grace_period: 2m
    restart: unless-stopped

volumes:
  minecraft-data:

Limit memory and CPU

MEMORY sets the Java heap. Give the container a limit a little above it, so the JVM has room for its own memory:

services:
  minecraft:
    image: ghcr.io/hambn/minecraft-server-paper:26.2
    environment:
      EULA: "TRUE"
      MEMORY: "4G"
    deploy:
      resources:
        limits:
          memory: 5G
          cpus: "2"

Pumpkin is a native binary, so MEMORY does not apply to it. Use the container limit alone.

Use a host directory instead of a volume

The containers run as UID and GID 1000. Make a bind-mounted directory writable by that user first:

mkdir -p ./data && sudo chown 1000:1000 ./data
services:
  minecraft:
    image: ghcr.io/hambn/minecraft-server-paper:26.2
    volumes:
      - ./data:/data

The world, server.properties, logs and the mods or plugins directory then live in ./data on the host.

Run several servers

Give every server its own host port and its own volume:

services:
  survival:
    image: ghcr.io/hambn/minecraft-server-paper:26.2
    ports:
      - "25565:25565"
    environment:
      EULA: "TRUE"
      PLUGINS: "luckperms,coreprotect"
    volumes:
      - survival-data:/data
    stop_grace_period: 2m
    restart: unless-stopped

  modded:
    image: ghcr.io/hambn/minecraft-server-fabric:26.3
    ports:
      - "25566:25565"
    environment:
      EULA: "TRUE"
      MEMORY: "6G"
      MODS: "lithium,ferrite-core"
    volumes:
      - modded-data:/data
    stop_grace_period: 2m
    restart: unless-stopped

volumes:
  survival-data:
  modded-data:

Players join the second server at your-host:25566.

Use the server console

The Java servers read console commands from standard input. Keep it open, then attach:

services:
  minecraft:
    stdin_open: true
    tty: true
docker attach "$(docker compose ps -q minecraft)"

Type commands such as say hello or op <player>. Detach with Ctrl-P followed by Ctrl-Q. Ctrl-C would stop the server.

Update

Images are rebuilt automatically whenever a component of a maintained version changes. To get the newest build of your tag:

docker compose pull
docker compose up -d

To control exactly when the server changes, pin the digest as well, for example ghcr.io/hambn/minecraft-server-paper:26.2@sha256:<digest>. Each version page shows its current digest. To move to a newer Minecraft release, back up /data first, because a world cannot be downgraded. Then change the tag. See tags and updates.

Back up the world

Stop the server so the world is saved and consistent, archive the volume, and start it again:

docker compose stop minecraft
docker run --rm --volumes-from "$(docker compose ps -aq minecraft)" \
  -v "$PWD:/backup" alpine \
  tar czf "/backup/minecraft-$(date +%F).tar.gz" -C /data .
docker compose start minecraft

To restore, extract the archive into an empty volume or directory with the same command, using tar xzf instead of tar czf.