Running a Minecraft server in an LXC container is tidier than a bare VM: near-native performance, isolated from the rest of the cluster, snapshotable, and easy to migrate. This guide covers Fabric 1.20.1 with the performance mod stack that makes a single-node CPU cluster playable for small groups.
Performance target
On a Ryzen 5 2400G with 4 GB RAM allocated to the container and 3 GB to the JVM, expect:
| Metric | Idle | 4 players | 8 players |
|---|---|---|---|
| TPS | 20.0 | 19.8 | 18.5–20.0 |
| MSPT | 4–6ms | 8–14ms | 14–22ms |
| RAM used | 800 MB | 1.4 GB | 2.1 GB |
| CPU % | 5–8% | 20–35% | 45–65% |
The mods below get you from stock’s 12–18 MSPT at 4 players down to 8–14ms.
Mod stack
| Mod | What it fixes |
|---|---|
| Fabric API | Required loader for all Fabric mods |
| Lithium | Server-side optimizations: physics, collision, chunk loading |
| Starlight | Complete rewrite of the lighting engine (~30% faster chunk load) |
| Carpet | Admin utilities: tick control, spawn tracking, /log profiling |
| Carpet-Extra | Additional Carpet features: block update optimizations |
Skip Sodium/Iris/Indium — those are client-side rendering mods. Server performance mods are Lithium and Starlight. Clients can use any mods they want regardless of what’s on the server.
Task 1: Create the container
| Field | Value |
|---|---|
| VMID | 104 |
| Hostname | minecraft |
| OS | Ubuntu 24.04 LXC (unprivileged) |
| Host | pvelab03 |
| IP | 10.25.144.76 (or your choice) |
| Cores | 4 |
| RAM | 4096 MB |
| Disk | 20 GB |
4 GB RAM is fine for up to 10 players. For larger servers or many loaded chunks, increase to 8 GB and set JVM to -Xmx6G.
Task 2: Install Java and create the server
Minecraft 1.20.1 requires Java 17+. Java 21 performs better with virtual threads and the Shenandoah GC:
apt-get update && apt-get install -y curl wget jq
apt-get install -y openjdk-21-jre-headless
java -version
# → openjdk version "21.0.x" ...
useradd -m -s /bin/bash minecraft
mkdir -p /srv/minecraft/{server,mods,backups}
chown -R minecraft:minecraft /srv/minecraft
cd /srv/minecraft/server
wget -q "https://meta.fabricmc.net/v2/versions/installer" -O - | \
jq -r '.[0].url' | xargs wget -q -O fabric-installer.jar
# Bootstrap the server jar (Fabric loader 0.16.0, Minecraft 1.20.1)
su - minecraft -c "java -jar /srv/minecraft/server/fabric-installer.jar server \
-mcversion 1.20.1 \
-loader 0.16.0 \
-downloadMinecraft \
-dir /srv/minecraft/server"
echo "eula=true" > /srv/minecraft/server/eula.txt
chown minecraft:minecraft /srv/minecraft/server/eula.txt
Task 3: Install performance mods
Download the 1.20.1-compatible versions from Modrinth:
MODS_DIR="/srv/minecraft/server/mods"
# Lithium — check Modrinth for current 1.20.1 build
wget -q "https://cdn.modrinth.com/data/gvQqBUqZ/versions/FT.../lithium-fabric-mc1.20.1-0.11.2.jar" \
-O "$MODS_DIR/lithium.jar"
# Starlight — Fabric 1.20.1
wget -q "https://cdn.modrinth.com/data/H8CaAYZC/versions/.../starlight-1.1.2+fabric.dce1c55.jar" \
-O "$MODS_DIR/starlight.jar"
# Carpet
wget -q "https://github.com/gnembon/fabric-carpet/releases/download/1.4.112/fabric-carpet-1.20.1-1.4.112+v230628.jar" \
-O "$MODS_DIR/carpet.jar"
chown minecraft:minecraft "$MODS_DIR"/*.jar
Mod downloads change URLs as new versions release. Before running: check the Modrinth project page for each mod, filter by Minecraft 1.20.1 + Fabric, and grab the direct download URL from the latest compatible release.
Task 4: Tune the JVM and create the systemd service
The flags below use Shenandoah (low-pause GC), pin threads to avoid cross-NUMA latency, and pre-touch memory:
cat > /srv/minecraft/server/start.sh {'<<'} 'EOF'
#!/bin/bash
cd /srv/minecraft/server
exec java \
-Xms2G -Xmx3G \
-XX:+UseShenandoahGC \
-XX:ShenandoahGCHeuristics=adaptive \
-XX:+DisableExplicitGC \
-XX:+AlwaysPreTouch \
-XX:+UseNUMA \
-XX:+PerfDisableSharedMem \
-Dfml.ignorePatchDiscrepancies=true \
-jar fabric-server-launch.jar --nogui
EOF
chmod +x /srv/minecraft/server/start.sh
chown minecraft:minecraft /srv/minecraft/server/start.sh
cat > /etc/systemd/system/minecraft.service {'<<'} 'EOF'
[Unit]
Description=Minecraft Fabric Server 1.20.1
After=network.target
[Service]
User=minecraft
WorkingDirectory=/srv/minecraft/server
ExecStart=/srv/minecraft/server/start.sh
ExecStop=/bin/kill -s SIGTERM $MAINPID
Restart=on-failure
RestartSec=10
StandardInput=null
[Install]
WantedBy=multi-user.target
EOF
systemctl enable --now minecraft
journalctl -u minecraft -f
# Wait for: [Server thread/INFO]: Done (15.234s)! For help, type "help"
Once live, connect with Minecraft client to 10.25.144.76:25565. In-game, verify TPS with Carpet:
/carpet setDefault commandLog true
/log tps
# → TPS: 20.0, MSPT: 6.24
Task 5: Automatic backups
cat > /srv/minecraft/backups/backup.sh {'<<'} 'EOF'
#!/bin/bash
BACKUP_DIR="/srv/minecraft/backups"
WORLD_DIR="/srv/minecraft/server/world"
DATE=$(date +%Y%m%d_%H%M)
TAR="/srv/minecraft/backups/world_$(date +%Y%m%d_%H%M).tar.gz"
# Tell players about the backup
# (works if minecraft-send-command tool is installed, skip otherwise)
tar -czf "$TAR" -C /srv/minecraft/server world
echo "Backup: $TAR ($(du -sh $TAR | cut -f1))"
# Keep last 7 daily backups
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -delete
EOF
chmod +x /srv/minecraft/backups/backup.sh
Add to root crontab:
echo "0 3 * * * /srv/minecraft/backups/backup.sh >> /var/log/minecraft-backup.log 2>&1" | crontab -
Proxmox snapshot for pre-update safety
Before any Minecraft or mod update:
pct snapshot 104 pre-update-$(date +%Y%m%d) \
--description "Before Minecraft/mod update"
# To roll back if the update breaks things:
pct rollback 104 pre-update-20260705
The snapshot includes the entire container filesystem — world data, mods, configs. Rollback takes under 30 seconds.
Server management reference
# View live log
journalctl -u minecraft -f
# Restart (saves world first via SIGTERM → ExecStop)
pct exec 104 -- systemctl restart minecraft
# Check memory usage
pct exec 104 -- ps aux | grep java
# Emergency stop (only if systemctl fails)
pct exec 104 -- kill -9 $(pgrep -f fabric-server)
# View backup list
pct exec 104 -- ls -lh /srv/minecraft/backups/
The complete Proxmox cluster setup supporting this server — including Tailscale for remote access and Grafana alerts for monitoring the container’s RAM and CPU — is covered in the other posts in this series.