How to Set Up a Minecraft Server with Docker Compose in 10 Minutes
A step-by-step guide to deploying a self-updating, mod-ready Minecraft server using Docker Compose. Master container orchestration, persistent volumes, automatic mod loading, and JVM performance tuning.

Want to host a Minecraft server to play with friends but feel overwhelmed by server administration?
I've been there: manually installing JDK, downloading the server jar from Mojang, configuring eula.txt, wrestling with NAT/port forwarding, tweaking JVM parameters, and manually dropping mods into folders. Hours later, and the server still wouldn't start. Then I discovered containerization. It turns out, running a Minecraft server can be as simple as starting a web service.
Today, I'll show you how to use the itzg/docker-minecraft-server image (14k+ stars on GitHub) to spin up a scalable, auto-updatable, and mod-ready Minecraft server in just 10 minutes. By the end of this guide, you'll not only be gaming with friends but also master practical skills in Docker Compose orchestration, persistent volumes, environment variable configuration, and performance tuning.
Prerequisites
- A Linux server (Ubuntu 20.04+ / CentOS 8+ works fine; a local VM is okay too)
- Docker and Docker Compose V2 installed
- Basic understanding of Docker concepts (images, containers, port mapping, volumes)
- A Java Edition Minecraft client (version must match the server)
If you haven't installed Docker yet, the official one-liner script is the easiest way:
bash
curl -fsSL https://get.docker.com | sh
sudo systemctl enable docker && sudo systemctl start docker
Quick Start: Launch in 3 Steps
Step 1: Create docker-compose.yml
Skip docker run and use Compose for declarative management. It saves you from countless headaches later. Create a docker-compose.yml in your project directory:
yaml
version: '3.8'
services:
mc:
image: itzg/minecraft-server:latest
container_name: minecraft-server
ports:
- "25565:25565"
environment:
EULA: "TRUE"
TYPE: PAPER
VERSION: "1.20.4"
MEMORY: 2G
volumes:
- ./data:/data
restart: unless-stopped
Why configure it this way?
EULA: "TRUE": Mojang requires you to accept the End User License Agreement, otherwise the server will exit immediately. Mandatory.TYPE: PAPER: The vanilla server lacks optimization. Paper is a highly optimized fork that supports plugins and maintains stable TPS, making it the top choice for hosting.VERSION: Specifies the Minecraft version. The image automatically downloads the corresponding server jar on startup.MEMORY: 2G: Sets the JVM heap memory limit. 2G is sufficient for a small server with 5-10 players. Increase it for larger groups or modpacks.volumes:/datais the internal directory for world saves, configs, and plugins. Mapping it to the host ensures your world persists even if the container is deleted.restart: unless-stopped: Automatically restarts the container if the server reboots or the process crashes. No need for midnight reboots.
Step 2: Start the Service
bash
docker compose up -d
On the first run, the image downloads the server jar and generates initial configurations. The console will output something like:
[init] Resolved version 1.20.4 to 1.20.4
[init] Downloading PaperMC server jar...
[init] Running server: java -Xms2G -Xmx2G -jar paper-1.20.4.jar
[Server thread/INFO] Starting minecraft server version 1.20.4
Look for Done or Server started to confirm successful launch.
Step 3: Join the Game
Open your Minecraft client, select the matching server version, go to "Multiplayer" → "Add Server":
Server Address: Your-Server-IP:25565
Click "Done" and join. If it fails, check your firewall to ensure port 25565 is open.
Practical Example: Deploying a Modded Server
The basic setup works, but many players prefer modded gameplay. Manually downloading mods and resolving dependency conflicts is painful. Fortunately, this image has built-in mod management.
Scenario: Running a Lightweight Tech Modpack with Forge
Modify the environment section in your docker-compose.yml:
yaml
environment:
EULA: "TRUE"
TYPE: FORGE
VERSION: "1.20.1"
MEMORY: 4G
MODS: |
https://cdn.example.com/mods/jei-1.20.1.jar
https://cdn.example.com/mods/mekanism-1.20.1.jar
REMOVE_OLD_MODS: "TRUE"
Key Configuration Breakdown:
TYPE: FORGE: Automatically downloads and installs the Forge mod loader.MODS: Provide direct download links to mod.jarfiles. The image will automatically fetch and place them in themodsdirectory upon startup. Supports multiple links separated by newlines.REMOVE_OLD_MODS: "TRUE": Cleans up old mods on startup to prevent version conflicts and bloat.
Start it the same way:
bash
docker compose up -d
Logs will show:
[init] Downloading mod: jei-1.20.1.jar
[init] Downloading mod: mekanism-1.20.1.jar
[init] Starting Forge server...
To update mods later, just change the URLs or versions in MODS and run docker compose up -d again. No need to SSH into the server and swap files manually.
Advanced: One-Click CurseForge / Modrinth Integration
The project also supports pulling complete modpacks directly from CurseForge or Modrinth. Simply add these environment variables:
yaml
CF_SLUG: "my-modpack"
CF_FILE_ID: "1234567"
# Or for Modrinth:
MODRINTH_PROJECT: "my-project"
MODRINTH_VERSION: "1.2.3"
The image will automatically extract, install dependencies, and configure mods/configs, truly achieving "configuration-as-deployment".
Troubleshooting & Common Pitfalls
1. Insufficient Memory Causing Frequent Crashes
Symptoms: Logs show java.lang.OutOfMemoryError: GC overhead limit exceeded or TPS drops to single digits.
Fix: Increase MEMORY and enable Aikar's JVM optimization flags:
yaml
MEMORY: 6G
USE_AIKAR_FLAGS: "TRUE"
Aikar Flags are widely validated GC optimization parameters in the MC community, significantly reducing lag.
2. World Data Missing After Container Restart
Cause: Incorrect volume mapping or accidentally deleting the host directory.
Fix: Ensure the volumes mapping is correct and avoid arbitrarily deleting the data directory on the host. Verify before each start:
bash
ls -la ./data/world/
Look for level.dat to confirm world data is intact.
3. Plugin Incompatibility After Version Switch
Cause: Moving from 1.20.1 to 1.20.4 might change APIs, breaking older plugins.
Fix: When switching versions, it's recommended to clear the data/plugins directory (backup first) and download version-matched plugins. Alternatively, set:
yaml
FORCE_REDOWNLOAD: "FALSE"
to prevent the container from overwriting existing files on every startup.
Summary
Here's a quick recap of today's steps:
- Write Compose File → Declare image, ports, environment variables, and volumes.
- One-Click Start → Automatically downloads the server, initializes the world, and goes live.
- Load Mods on Demand → Use
MODSvariables or modpack configs to eliminate manual deployment. - Performance Tuning → Aikar Flags + proper memory allocation for rock-solid stability.
This image encapsulates the most tedious parts of server hosting—downloading binaries, handling dependencies, version control, and mod loading—into simple environment variables. You no longer need custom scripts, documentation deep-dives, or manual debugging. Just update the config and restart the container.
Next Steps:
- Try reverse-proxying a Web UI (like
rcon-cli+ a dashboard) with Nginx. - Explore
BACKUPandRESTOREenvironment variables for automated backups. - Orchestrate high-availability MC clusters across multiple nodes using Docker Swarm or Kubernetes.
Running a server isn't hard; the hardest part is starting. Grab a Compose file now, and you'll be exploring new worlds with friends tonight. Drop your questions in the comments, and I'll write an advanced guide based on common issues!