Dockerized Minecraft server with automatic RAM detection and Aikar's optimized JVM flags.
Features Eclipse Temurin 21 JRE on Alpine Linux with intelligent memory management.
Most Minecraft Docker solutions require manual memory configuration:
# itzg/docker-minecraft-server
docker run -e MEMORY=8G ...
# marctv/minecraft-papermc-server
docker run -e MAX_RAM=8G ...But how do you calculate the right value? Too little causes crashes. Too much causes garbage collection lag.
secondfry/docker-minecraft solves this through:
- ✅ Automatic RAM detection - reads system memory, calculates optimal heap
- ✅ Intelligent overhead - reserves 1-2GB for OS based on total RAM
- ✅ Reasonable defaults - caps heap at 12GB to avoid GC issues on large systems
- ✅ Adaptive GC tuning - adjusts G1GC parameters based on allocated heap size
- ✅ Trivial migration - copy existing servers without file preservation
Just run docker-compose up -d and it works.
| Feature | secondfry | itzg | marctv | Pterodactyl |
|---|---|---|---|---|
| RAM Configuration | ✅ Automatic | ❌ Manual | ❌ Manual | ❌ Web UI |
| GC Tuning | ✅ Adaptive | ❌ Static | ❌ Static | ❌ Static |
| Server Migration | ✅ Trivial | |||
| Image Size | 100MB Alpine | 793MB Ubuntu | 200MB | N/A |
| Setup Complexity | 3 commands | Moderate | Simple | Complex |
| Auto Downloads | ❌ Manual JAR | ✅ Auto | ✅ Auto | ✅ Auto |
| Web Interface | ❌ None | ❌ None | ❌ None | ✅ Full UI |
| Best For | Dev UX | Features | ARM/Pi | Enterprise |
Use itzg/docker-minecraft-server if you need:
- Automatic mod/plugin downloads
- 100+ configuration options
- Multi-version server management
- Established community (10k+ stars)
Use Pterodactyl if you need:
- Multi-server management
- User access controls
- Web-based administration
- Enterprise features
Use marctv/minecraft-papermc-server if you:
- Only run PaperMC servers
- Prefer simplicity over flexibility
- Run on ARM/Raspberry Pi
Use secondfry/docker-minecraft if you value:
- Zero-configuration memory optimization
- Clean architecture and minimal footprint
- Easy migration of existing servers
- Smart defaults over config files
- Docker and Docker Compose (installation instructions)
- Your Minecraft server JAR file (Fabric, Paper, Vanilla, etc.)
- Minimum 6GB RAM recommended (8GB+ preferred)
-
Clone or download this repository:
git clone https://github.com/secondfry/docker-minecraft.git cd docker-minecraft # Or download and unzip the repository
-
Place your server JAR in the
server/directory:cd server # Example for Fabric: wget https://meta.fabricmc.net/v2/versions/loader/1.21.8/0.17.0/1.1.0/server/jar \ -O fabric-server-mc.1.21.8-loader.0.17.0-launcher.1.1.0.jar cd ..
-
Start the server:
docker-compose up -d
-
View logs:
docker-compose logs -f
-
Clone or download this repository:
git clone https://github.com/secondfry/docker-minecraft.git cd docker-minecraft -
Move or copy your existing server into the
server/directory:# Move your server directory (replaces empty ./server/) mv /path/to/your/existing/server/ ./server/ # Or copy if you want to keep the original cp -a /path/to/your/existing/server/. ./server/
-
Start the server:
docker-compose up -d
-
Clone or download this repository:
git clone https://github.com/secondfry/docker-minecraft.git cd docker-minecraft -
Extract your server tarball into the
server/directory:# Move tarball to repo root, then extract mv /path/to/server-backup.tar.gz ./ tar -xzf ./server-backup.tar.gz -C ./server/ -
Start the server:
docker-compose up -d
That's it! The server automatically detects available RAM and configures itself with Aikar's optimized flags.
Note: The secondfry-start.sh script is automatically injected into the server at runtime. You never need to worry about preserving it when copying or extracting server files - it's always provided fresh from the repository!
The server automatically detects available RAM and configures JVM heap size with proper overhead:
| Total RAM | Heap Allocation | OS Overhead | Notes |
|---|---|---|---|
| 6-7 GB | Total - 1 GB | 1 GB | Minimum viable |
| 8-15 GB | Total - 2 GB | 2 GB | Recommended |
| 16+ GB | Total - 2 GB | 2 GB | Capped at 12GB |
Example: On an 8GB system → 6GB heap. On a 32GB system → 12GB heap (capped).
Why the 12GB cap? Aikar recommends 6-10GB for most servers. Research shows that allocating more than 12GB rarely improves performance and can cause severe garbage collection pauses (2-5 second freezes). See the official Aikar's flags documentation for details.
Create a .env file from the example:
cp .env.example .envEdit .env and uncomment/set MEMORY_GB:
MEMORY_GB=6 # Force specific heap size
TZ=UTCThen restart: docker-compose up -d
Why .env file? Best practice for configuration management - keeps sensitive values out of docker-compose.yml and version control.
Edit docker-compose.yml and uncomment the MEMORY_GB line:
environment:
- TZ=UTC
- MEMORY_GB=6 # Force specific heap size (in GB, without 'G' suffix)Then restart: docker-compose up -d
If you have a heavily modded server that genuinely needs more than 12GB, you can override the cap:
environment:
- TZ=UTC
- MAX_MEMORY_GB=16 # Increase cap from 12GB to 16GBOr in .env:
MAX_MEMORY_GB=16Warning: Only increase this if you have a specific need (e.g., 200+ mods). Aikar recommends 6-10GB for most servers. Over-allocation causes performance issues, not improvements.
By default, Docker containers can use ALL host RAM without limits. This is the recommended configuration for dedicated Minecraft servers, as the startup script will automatically detect all available RAM and configure itself optimally.
The memory limits in docker-compose.yml are commented out by default.
Only uncomment and set memory limits if you need to:
- Multi-tenant hosts - Prevent one container from using all RAM and starving other services
- Shared hosting - Enforce resource quotas per customer/container
- Testing - Simulate lower-memory environments
# Uncomment to enable limits:
# deploy:
# resources:
# limits:
# memory: 8G # Hard limit - container killed if exceeded
# reservations:
# memory: 6G # Soft limit - guaranteed minimumImportant: If you set a memory limit, the auto-detection script will detect ONLY that limit, not the host's total RAM.
Example:
- Host has 16GB RAM
- Set
memory: 8Glimit - Script detects 8GB and allocates ~6GB heap (leaving 2GB overhead)
- Container cannot use the other 8GB even though it's available on the host
Recommendation: Leave limits commented out unless you have a specific need.
.
├── docker/ # Docker build context (isolated from server data)
│ ├── Dockerfile # Container image definition
│ └── .dockerignore # Files to exclude from build
├── server/ # YOUR MINECRAFT SERVER GOES HERE
│ ├── *.jar # Your server JAR (place here)
│ ├── world/ # World data (generated)
│ ├── mods/ # Your mods (if using Fabric/Forge)
│ ├── plugins/ # Your plugins (if using Paper/Spigot)
│ ├── server.properties # Server config (generated)
│ └── ... # All other server files
├── secondfry-start.sh # Startup script (auto-injected into container)
├── docker-compose.yml # Service orchestration
├── .env.example # Environment variable template
└── README.md # This file
-
server/directory: All your Minecraft files in one place- Easy to understand: "put your server here"
- Clean separation from Docker infrastructure
- Easy to backup: just tar the
server/directory - Easy to migrate: copy/move existing servers without worry
-
secondfry-start.shoverlay: Automatically injected at runtime- Stored at repository root, not in server/
- Never gets overwritten by your server files
- Always up-to-date from repository
- You can destructively copy/move/extract servers safely
-
docker/subdirectory: Docker build files isolated- Faster builds: only sends Docker files to build context
- No pollution: world data and logs stay out of Docker layers
- Clean: infrastructure separate from application data
This structure makes it trivial to dockerize existing servers - no file preservation needed!
Default configuration:
25565- Minecraft server (exposed)25575- RCON remote console (disabled by default for security)
RCON (Remote Console) is disabled by default as it can be a security risk if exposed unintentionally.
To enable RCON:
-
Uncomment the RCON port in
docker-compose.yml:ports: - "25565:25565" - "25575:25575" # Uncomment this line
-
Configure RCON in
server.properties:enable-rcon=true rcon.port=25575 rcon.password=your-secure-password-here
-
Restart:
docker-compose restart
Security Warning: Only expose RCON if you need it, and always use a strong password. Consider using SSH tunneling instead of exposing RCON publicly.
The startup script automatically finds your server JAR in this priority order:
fabric-server-mc.*.jarpaper-*.jarserver.jar- First
*.jarfile found
No manual configuration needed! Just drop your JAR in the root directory.
The container runs as user minecraft (UID 1000, GID 1000) for security. Since the current directory is mounted as a volume, file permissions are inherited from your host system.
Files in the mounted directory must be readable by UID 1000. There are three approaches:
Option 1: Match Host User to Container (Recommended)
If your host user is already UID 1000, no action needed:
id -u # Check your UID
# If 1000, you're good!Option 2: Fix Permissions on Host
Make files readable/writable by UID 1000:
# Change ownership (if you have sudo)
sudo chown -R 1000:1000 .
# Or make files world-readable (less secure)
chmod -R 755 .Option 3: Run Container as Your Host User
Add to docker-compose.yml:
services:
minecraft:
user: "${UID}:${GID}" # Runs as your host user
# ... rest of configThen start with:
UID=$(id -u) GID=$(id -g) docker-compose up -dIf you see permission denied errors:
# Check file ownership
ls -la
# Fix ownership
sudo chown -R 1000:1000 .
# Or run as your user (Option 3 above)Note: The Dockerfile intentionally does NOT include chown commands because volume mounts override image layers. Permissions must be handled on the host or via runtime configuration.
This setup uses Aikar's flags, the industry-standard JVM optimizations for Minecraft servers.
- G1GC garbage collector for low-latency pauses (targets <200ms)
- Adaptive G1 tuning based on heap size:
- ≤12GB: 30-40% new generation
-
12GB: 40-50% new generation + larger heap regions
- MaxTenuringThreshold=1: Prevents short-lived objects from aging unnecessarily
- DisableExplicitGC: Blocks plugins from triggering full GC lag spikes
- Optimized for Minecraft's ~800MB/s memory allocation rate
Garbage collection logs are automatically saved to logs/gc.log with rotation (5 files × 1MB each).
Reference: PaperMC - Aikar's Flags
# Start server (detached)
docker-compose up -d
# Stop server gracefully
docker-compose down
# Restart server
docker-compose restart
# View logs (follow mode)
docker-compose logs -f
# View logs (last 100 lines)
docker-compose logs --tail=100
# Access console (interactive)
docker attach minecraft-server
# Press Ctrl+P, Ctrl+Q to detach without stopping
# Rebuild after Dockerfile changes
docker-compose up -d --build
# View detected RAM allocation
docker-compose logs | grep "Detected"| Component | Value |
|---|---|
| Base Image | Eclipse Temurin 21 JRE on Alpine Linux |
| Java Distribution | Eclipse Temurin (recommended for MC servers) |
| User | Non-root minecraft user (UID 1000) |
| Volume Mount | Current directory → /server (read/write) |
| Auto-restart | Enabled (unless manually stopped) |
| Healthcheck | TCP port 25565 check every 30s |
| EULA | Auto-accepted on first run |
If you need to customize JVM flags beyond Aikar's recommendations, edit secondfry-start.sh lines 103-128.
Warning: Only modify if you understand JVM tuning. Aikar's flags are optimal for 99% of servers.
To run multiple servers on one host:
- Create separate directories for each server
- Modify
container_nameandportsin eachdocker-compose.yml:container_name: minecraft-server-survival ports: - "25565:25565" # Server 1 container_name: minecraft-server-creative ports: - "25566:25565" # Server 2 (different host port)
If using a plugin that requires a database (like CoreProtect), add a database service to docker-compose.yml:
services:
minecraft:
# ... existing config ...
depends_on:
- database
database:
image: mariadb:latest
environment:
MYSQL_ROOT_PASSWORD: changeme
MYSQL_DATABASE: minecraft
volumes:
- ./data/mysql:/var/lib/mysqlCheck logs first:
docker-compose logsCommon issues:
- No JAR file found → Place server JAR in root directory
- Port already in use → Change port in
docker-compose.yml - Permission denied → Run
chmod +x secondfry-start.sh
Symptoms: OutOfMemoryError in logs, server crashes
Solutions:
- Check detected RAM:
docker-compose logs | grep "Detected" - Increase Docker memory limits in
docker-compose.yml - Ensure you're leaving 1-2GB overhead for the OS
- For 8GB host, use:
limits: memory: 8G(will allocate ~6GB heap)
Check GC logs:
cat logs/gc.logIf GC pauses are >200ms:
- Increase heap size (more RAM)
- Reduce view distance
- Use server optimization plugins (Lithium, Starlight, FerriteCore)
- Consider upgrading to 16GB+ RAM
The container runs as UID 1000. If you get permission errors:
# Fix ownership
sudo chown -R 1000:1000 .
# Or make files readable
chmod -R 755 .- Check server is running:
docker ps - Check port mapping:
docker-compose logs | grep "25565" - Check firewall: Allow port 25565 TCP
- Check server.properties:
server-ip=0.0.0.0(binds to all interfaces)
- Preallocate world: Use a world pregeneration plugin to avoid generation lag
- Optimize view distance: 6-8 chunks is usually sufficient
- Use Paper/Fabric: Better performance than Vanilla
- Install optimization mods: Lithium, Starlight, Sodium (client)
- Monitor with GC logs: Check
logs/gc.logfor pause times
Create a backup script:
#!/bin/bash
# backup.sh
docker-compose exec minecraft rcon-cli save-off
docker-compose exec minecraft rcon-cli save-all
tar -czf "backup-$(date +%Y%m%d-%H%M%S).tar.gz" world world_nether world_the_end
docker-compose exec minecraft rcon-cli save-onOr use a backup plugin like DiscordSRV Backup.
- Stop server:
docker-compose down - Backup world:
tar -czf backup.tar.gz world/ - Replace JAR: Download new server JAR
- Start server:
docker-compose up -d - Check logs:
docker-compose logs -f
- Container runs as non-root user (UID 1000)
- Alpine Linux base for minimal attack surface
- No unnecessary packages installed
- Automatic EULA acceptance (ensure you agree to Minecraft EULA)
- Consider using a firewall (iptables/ufw) to restrict port access
Found an issue or have a suggestion? Please open an issue or pull request.
This Docker configuration is provided as-is under the MIT License. Minecraft server software has its own licenses and EULA.