Advanced Game Server Hosting: Mods, Performance Tuning, and Multi-Game Management
You've got a basic game server running. Friends can connect, the world generates, and everything seems fine. Then someone adds a few mods, the player count grows, the server starts lagging during peak hours, and you realize that running a game server well is different from just running one.
This guide covers the next level: mod management, performance tuning, automated maintenance, multi-game setups, and the operational practices that separate a server people tolerate from one people love playing on.

Mod Management
Mods are where game servers get interesting — and where they get complicated. A modded Minecraft server with 200+ mods is a different beast from vanilla. Managing mods across updates, compatibility issues, and player expectations requires a system.
Minecraft: Mod Loaders and Management
The itzg/minecraft-server Docker image supports all major mod loaders:
services:
minecraft-modded:
image: itzg/minecraft-server
container_name: minecraft-modded
restart: unless-stopped
ports:
- "25565:25565"
volumes:
- ./minecraft-data:/data
environment:
EULA: "TRUE"
MEMORY: "8G"
TYPE: "FABRIC" # or FORGE, NEOFORGE, QUILT
VERSION: "1.21.4"
MODRINTH_PROJECTS: |
sodium
lithium
fabric-api
starlight
iris
MODRINTH_ALLOWED_VERSION_TYPE: "release"
The MODRINTH_PROJECTS environment variable automatically downloads and updates mods from Modrinth. For CurseForge mods, use CF_API_KEY and CURSEFORGE_FILES.
Modpack Deployment
For curated modpacks, pin the exact version to prevent unexpected updates:
environment:
TYPE: "AUTO_CURSEFORGE"
CF_API_KEY: "${CF_API_KEY}"
CF_PAGE_URL: "https://www.curseforge.com/minecraft/modpacks/all-the-mods-10"
CF_FILE_ID: "5432100" # Pin to specific file version
Valheim: BepInEx and Mods
Valheim mods use the BepInEx framework. The lloesche/valheim-server image supports it natively:
services:
valheim:
image: lloesche/valheim-server
container_name: valheim
restart: unless-stopped
ports:
- "2456-2457:2456-2457/udp"
volumes:
- ./valheim-config:/config
- ./valheim-data:/opt/valheim
environment:
SERVER_NAME: "My Valheim Server"
WORLD_NAME: "MyWorld"
SERVER_PASS: "secretpassword"
VALHEIM_PLUS: "true" # Install Valheim Plus mod
# Or for BepInEx mods:
BEPINEX: "true"
cap_add:
- sys_nice
Place BepInEx plugins in ./valheim-config/bepinex/plugins/.
Terraria: tModLoader
For modded Terraria, use tModLoader:
services:
terraria-modded:
image: jacobsmile/tmodloader1.4:latest
container_name: terraria-modded
restart: unless-stopped
ports:
- "7777:7777"
volumes:
- ./terraria-data:/root/.local/share/Terraria
environment:
WORLD_NAME: "ModdedWorld"
DIFFICULTY: "2"
MAX_PLAYERS: "16"
# Mods are managed through the tModLoader mod browser
# or placed in the Mods directory
Palworld: Server Configuration
Palworld's dedicated server has extensive configuration:
services:
palworld:
image: thijsvanloef/palworld-server-docker:latest
container_name: palworld
restart: unless-stopped
ports:
- "8211:8211/udp"
- "27015:27015/udp"
volumes:
- ./palworld-data:/palworld
environment:
PUID: 1000
PGID: 1000
COMMUNITY_SERVER: "true"
SERVER_NAME: "My Palworld Server"
SERVER_PASSWORD: "secretpassword"
ADMIN_PASSWORD: "adminpassword"
MAX_PLAYERS: 16
# Performance tuning
PAL_EGG_DEFAULT_HATCHING_TIME: "1.000000"
DEATH_PENALTY: "None"
ENABLE_PLAYER_TO_PLAYER_DAMAGE: "false"
Performance Tuning
JVM Tuning for Minecraft
Minecraft's biggest performance bottleneck is the Java Virtual Machine. The default JVM flags are conservative. These Aikar flags are the community standard for optimized Minecraft servers:
environment:
JVM_XX_OPTS: >-
-XX:+UseG1GC
-XX:+ParallelRefProcEnabled
-XX:MaxGCPauseMillis=200
-XX:+UnlockExperimentalVMOptions
-XX:+DisableExplicitGC
-XX:+AlwaysPreTouch
-XX:G1NewSizePercent=30
-XX:G1MaxNewSizePercent=40
-XX:G1HeapRegionSize=8M
-XX:G1ReservePercent=20
-XX:G1HeapWastePercent=5
-XX:G1MixedGCCountTarget=4
-XX:InitiatingHeapOccupancyPercent=15
-XX:G1MixedGCLiveThresholdPercent=90
-XX:G1RSetUpdatingPauseTimePercent=5
-XX:SurvivorRatio=32
-XX:+PerfDisableSharedMem
-XX:MaxTenuringThreshold=1
Memory allocation guidelines:
| Players | Vanilla | Light Mods (20-50) | Heavy Mods (100+) |
|---|---|---|---|
| 1-5 | 2-3GB | 4-5GB | 6-8GB |
| 5-10 | 3-4GB | 5-6GB | 8-10GB |
| 10-20 | 4-6GB | 6-8GB | 10-12GB |
| 20+ | 6-8GB | 8-12GB | 12-16GB |
Don't over-allocate memory. Giving Minecraft 16GB when it only needs 8GB causes longer garbage collection pauses.
Performance Mods for Minecraft
Server-side performance mods make a dramatic difference:
| Mod | Loader | Effect |
|---|---|---|
| Lithium | Fabric | Optimizes game physics, AI, block ticking |
| Starlight | Fabric/Forge | Rewrites the lighting engine (massive improvement) |
| FerriteCore | Fabric/Forge | Reduces memory usage |
| Chunky | Fabric/Forge | Pre-generates chunks (eliminates lag from exploration) |
| Krypton | Fabric | Optimizes networking code |
| C2ME | Fabric | Parallelizes chunk generation |
A Fabric server with Lithium + Starlight + FerriteCore performs dramatically better than vanilla, especially with 10+ players.
Pre-generating the World
Chunk generation is the biggest source of lag spikes. Pre-generate your world before players connect:
# In-game command (with Chunky mod)
/chunky radius 5000
/chunky start
# This generates all chunks within 5000 blocks of spawn
# Takes 30-60 minutes depending on hardware
# After completion, exploration within this area causes zero generation lag
CPU and I/O Optimization
Game servers benefit from specific host optimizations:
# Set CPU governor to performance (prevents frequency scaling lag spikes)
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
# Or make it persistent
sudo apt install cpufrequtils
echo 'GOVERNOR="performance"' | sudo tee /etc/default/cpufrequtils
# For Docker: pin game server containers to specific CPU cores
docker update --cpuset-cpus="0-3" minecraft
Store game server data on SSDs. World data involves many small random reads and writes — exactly the workload where SSDs shine and HDDs struggle. A Minecraft server on an NVMe drive loads chunks noticeably faster than one on a hard drive.
Network Optimization
# Increase network buffer sizes (helps with many simultaneous players)
sudo sysctl -w net.core.rmem_max=16777216
sudo sysctl -w net.core.wmem_max=16777216
# Make persistent in /etc/sysctl.d/99-gameserver.conf
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.netdev_max_backlog = 5000
Automated Backups
Game world data is irreplaceable. Automated backups are non-negotiable.
Minecraft Backup Script
#!/bin/bash
# minecraft-backup.sh — Backup Minecraft world with server notification
CONTAINER="minecraft"
BACKUP_DIR="/mnt/backup/minecraft"
MAX_BACKUPS=14
DATE=$(date +%Y-%m-%d_%H-%M)
mkdir -p "$BACKUP_DIR"
# Notify players
docker exec "$CONTAINER" rcon-cli "say Server backup starting in 10 seconds..."
sleep 10
# Disable auto-save and force save
docker exec "$CONTAINER" rcon-cli "save-off"
docker exec "$CONTAINER" rcon-cli "save-all"
sleep 5
# Create backup
tar -czf "$BACKUP_DIR/world-$DATE.tar.gz" -C /path/to/minecraft-data world world_nether world_the_end
# Re-enable auto-save
docker exec "$CONTAINER" rcon-cli "save-on"
docker exec "$CONTAINER" rcon-cli "say Backup complete!"
# Rotate old backups
ls -t "$BACKUP_DIR"/world-*.tar.gz | tail -n +"$((MAX_BACKUPS + 1))" | xargs -r rm
echo "Backup complete: world-$DATE.tar.gz ($(du -sh "$BACKUP_DIR/world-$DATE.tar.gz" | cut -f1))"
Systemd Timer for Automated Backups
# /etc/systemd/system/minecraft-backup.timer
[Unit]
Description=Minecraft World Backup
[Timer]
OnCalendar=*-*-* 04:00:00
OnCalendar=*-*-* 16:00:00
Persistent=true
[Install]
WantedBy=timers.target
# /etc/systemd/system/minecraft-backup.service
[Unit]
Description=Minecraft World Backup
After=docker.service
[Service]
Type=oneshot
ExecStart=/opt/scripts/minecraft-backup.sh
User=root
sudo systemctl enable --now minecraft-backup.timer
Valheim Automatic Backups
The lloesche/valheim-server image has built-in backup support:
environment:
BACKUPS: "true"
BACKUPS_CRON: "0 */6 * * *" # Every 6 hours
BACKUPS_DIRECTORY: "/config/backups"
BACKUPS_MAX_AGE: 7 # Keep 7 days
BACKUPS_MAX_COUNT: 28 # Keep max 28 backups
BACKUPS_IF_IDLE: "false" # Don't backup if no players are on
Multi-Game Server Management
Running multiple game servers efficiently requires planning around resource contention.
Resource Allocation Strategy
# docker-compose.yml — Multi-game setup with resource limits
services:
minecraft:
image: itzg/minecraft-server
container_name: minecraft
restart: unless-stopped
ports:
- "25565:25565"
volumes:
- ./minecraft:/data
environment:
EULA: "TRUE"
MEMORY: "6G"
deploy:
resources:
limits:
cpus: "4.0"
memory: 8G
reservations:
cpus: "2.0"
memory: 6G
valheim:
image: lloesche/valheim-server
container_name: valheim
restart: unless-stopped
ports:
- "2456-2457:2456-2457/udp"
volumes:
- ./valheim-config:/config
- ./valheim-data:/opt/valheim
environment:
SERVER_NAME: "My Valheim Server"
WORLD_NAME: "MyWorld"
SERVER_PASS: "secretpassword"
deploy:
resources:
limits:
cpus: "2.0"
memory: 4G
reservations:
cpus: "1.0"
memory: 2G
terraria:
image: ryshe/terraria:latest
container_name: terraria
restart: unless-stopped
ports:
- "7777:7777"
volumes:
- ./terraria:/root/.local/share/Terraria
stdin_open: true
tty: true
deploy:
resources:
limits:
cpus: "1.0"
memory: 1G
reservations:
cpus: "0.5"
memory: 512M
palworld:
image: thijsvanloef/palworld-server-docker:latest
container_name: palworld
restart: unless-stopped
ports:
- "8211:8211/udp"
- "27015:27015/udp"
volumes:
- ./palworld:/palworld
environment:
SERVER_NAME: "My Palworld Server"
SERVER_PASSWORD: "secretpassword"
ADMIN_PASSWORD: "adminpassword"
MAX_PLAYERS: 16
deploy:
resources:
limits:
cpus: "4.0"
memory: 16G
reservations:
cpus: "2.0"
memory: 8G
Server Status Dashboard
Use Crafty Controller or AMP (Application Management Panel) for a unified web interface to manage multiple game servers:
crafty:
image: registry.gitlab.com/crafty-controller/crafty-4:latest
container_name: crafty
restart: unless-stopped
ports:
- "8443:8443" # Web UI
- "25565:25565" # Minecraft
- "19132:19132/udp" # Bedrock
volumes:
- ./crafty/backups:/crafty/backups
- ./crafty/logs:/crafty/logs
- ./crafty/servers:/crafty/servers
- ./crafty/config:/crafty/app/config
- ./crafty/import:/crafty/import
Crafty provides:
- Start/stop/restart servers from a web UI
- Console access without SSH
- Player management and whitelisting
- Scheduled tasks (backups, restarts)
- Resource monitoring per server
Scheduled Restarts
Game servers accumulate memory leaks and performance degradation over time. Scheduled restarts keep things fresh.
Minecraft Restart with Warning
#!/bin/bash
# minecraft-restart.sh — Graceful restart with player warnings
CONTAINER="minecraft"
# Warn players
docker exec "$CONTAINER" rcon-cli "say Server restarting in 5 minutes for maintenance"
sleep 240 # 4 minutes
docker exec "$CONTAINER" rcon-cli "say Server restarting in 1 minute!"
sleep 50
docker exec "$CONTAINER" rcon-cli "say Server restarting in 10 seconds!"
sleep 10
# Save and stop
docker exec "$CONTAINER" rcon-cli "save-all"
sleep 5
docker restart "$CONTAINER"
Schedule it for low-activity hours:
# /etc/systemd/system/minecraft-restart.timer
[Timer]
OnCalendar=*-*-* 06:00:00
Persistent=true
Monitoring and Alerting
Server Status Checks
A simple health check script:
#!/bin/bash
# check-game-servers.sh
check_port() {
local name="$1" host="$2" port="$3" proto="${4:-tcp}"
if [ "$proto" = "udp" ]; then
nc -zu "$host" "$port" -w 3 2>/dev/null
else
nc -z "$host" "$port" -w 3 2>/dev/null
fi
if [ $? -eq 0 ]; then
echo "OK: $name ($host:$port/$proto)"
else
echo "FAIL: $name ($host:$port/$proto)"
# Send notification (webhook, email, etc.)
fi
}
check_port "Minecraft" localhost 25565
check_port "Valheim" localhost 2456 udp
check_port "Terraria" localhost 7777
check_port "Palworld" localhost 8211 udp
Minecraft-Specific Monitoring
The itzg/minecraft-server image includes a health check. Monitor it with Docker's built-in health check:
healthcheck:
test: mc-health
start_period: 1m
interval: 5s
retries: 20
For TPS (ticks per second) monitoring — the key Minecraft performance metric:
# Check TPS via RCON
docker exec minecraft rcon-cli "tps"
# Expected output: TPS from last 1m, 5m, 15m: 20.0, 20.0, 20.0
# Below 18 TPS = noticeable lag. Below 15 TPS = unplayable.
Networking and Remote Access
Port Forwarding
Each game needs specific ports forwarded on your router:
| Game | Port(s) | Protocol |
|---|---|---|
| Minecraft Java | 25565 | TCP |
| Minecraft Bedrock | 19132 | UDP |
| Valheim | 2456-2457 | UDP |
| Terraria | 7777 | TCP |
| Palworld | 8211 | UDP |
VPN Alternative
Instead of port forwarding (which exposes your home IP), use a VPN:
- Tailscale: Zero-config mesh VPN. Install on your server and each player's machine. Everyone connects via Tailscale IPs. No port forwarding needed.
- Playit.gg: Free tunneling service specifically designed for game servers. Creates a public address that tunnels to your local server.
# Playit.gg setup (no Docker needed on the host)
curl -SsL https://playit.gg/downloads/playit-linux_64-signed | bash
# Follow the interactive setup to create tunnels for your game ports
Hardware Recommendations
Budget Game Server (1-2 games, 5-10 players)
- CPU: Intel i5 (any recent gen) or Ryzen 5
- RAM: 16GB DDR4
- Storage: 256GB NVMe SSD
- Estimated cost: $200-300 (used mini PC)
- Power: 20-40W idle
Mid-Range Game Server (3-4 games, 10-20 players)
- CPU: Intel i7/i9 or Ryzen 7
- RAM: 32GB DDR4
- Storage: 512GB NVMe SSD
- Estimated cost: $400-600 (used desktop or workstation)
- Power: 40-80W under load
Heavy Game Server (5+ games, 20+ players, heavy mods)
- CPU: Xeon or Ryzen 9 with high single-thread performance
- RAM: 64GB DDR4/DDR5
- Storage: 1TB NVMe SSD
- Estimated cost: $600-1000
- Power: 80-150W under load
Important: Game servers (especially Minecraft) benefit more from high single-thread CPU performance than from many cores. An Intel i5-13600K outperforms a dual-Xeon server for Minecraft because most game server logic is single-threaded.
Final Thoughts
The difference between a game server that's "fine" and one that's genuinely good comes down to operational care: automated backups, scheduled restarts, performance monitoring, and proactive mod management. None of it is hard, but it does require setting up the automation and then leaving it alone to do its job.
Start with one game, get it running well with backups and scheduled restarts, then expand. Your friends won't care about your fancy container orchestration setup — they'll care that the server is fast, stable, and their builds are safe. Focus on those outcomes and the rest is just implementation details.