As we all find ourselves at home these days many of our kids have requested help with gaming. Minecraft is a common game, loved by millions, played by kids of all ages, and a good teaching tool for  how one can optimize the Red Hat Enterprise Linux (RHEL) platform for hosting. 

For those who are not familiar with it, Minecraft is a client-server game, meaning clients can connect to a Minecraft server to share the experience with other gamers.

This post will go through a number of performance optimizations you can configure in RHEL to make your Minecraft Server experience the best it can be. There are a few architectural flavors of Minecraft; I will focus on the Java implementation of the Minecraft server on RHEL 8. Note that this is only compatible with the Minecraft: Java Edition client.

I will break down the performance optimizations into five key areas: Operating system, disk I/O, memory, Java and Kernel security mitigations.

Operating System

RHEL ships with a tuning sub-system that is easy to use, but not installed by default. The tuning sub-system is called Tuned. Install the package with yum:

# yum -y install tuned

This installs and starts the sub-system automatically with its default settings. Run the tuned-adm list command to see all of the pre-defined tuning profiles:

# tuned-adm list

Available profiles: 
- latency-performance
- virtual-guest
- default
- throughput-performance
- spindown-disk
- laptop-battery-powersave
- server-powersave
- desktop-powersave
- laptop-ac-powersave
- virtual-host
- enterprise-storage 

Current active profile: default

Next, select a profile that best suits your RHEL installation. If you are running RHEL as a virtual machine (VM), select the virtual-guest profile. If you are running RHEL on a physical piece of hardware, then select throughput-performance or latency-performance. You can do that with tuned-adm profile and the name of the profile you want to choose:

# tuned-adm profile virtual-guest 
Calling '/etc/ktune.d/tunedadm.sh stop':                   [  OK  ]
Reverting to cfq elevator: dm-0 dm-1 vda                   [  OK  ]
Stopping tuned:                                            [  OK  ]
Switching to profile 'virtual-guest'
Applying deadline elevator: dm-0 dm-1 vda                  [  OK  ]
Applying ktune sysctl settings:
/etc/ktune.d/tunedadm.conf:                                [  OK  ]
Calling '/etc/ktune.d/tunedadm.sh start':                  [  OK  ]
Applying sysctl settings from /etc/sysctl.conf
Starting tuned:                                            [  OK  ]

As you can see, the virtual-guest profile has been assigned, and tuned made a number of changes to the system to optimize it automatically for your configuration. 

More information about tuned can be found in the Red Hat documentation pages "Getting started with tuned" and "Customizing tuned profiles."

Disk I/O

The Minecraft Server application will need faster Disk I/O during initialization and throughout the game. Spinning hard drives are slow and add a lot of latency when compared to SSD or NVMe drives. 

If you do not have access to an SSD or NVMe drive, there is an option to configure faster I/O with ramdisks. The ramdisk is exactly what it sounds like: it is a partition of RAM that is reserved for use by the OS to access like a hard drive. This ramdisk is as fast as RAM (Random Access Memory), but with one huge caveat: if the system crashes or reboots, all the data in the ramdisk is lost, so use caution. We'll walk through the eight steps to set it up:

1. Determine the size you need to reserve in MB (MegaBytes). Remember 1024MB is 1GB (GigaBytes). Keep in mind, you will need to save some memory for the OS to use, so don’t be greedy and reserve too much for your ramdisk. I chose 3072MB, which is 3GB, leaving my OS 21GB since my server had 24GB to start with.

2. Edit the /etc/fstab file and add this line:

tmpfs /home/minecraft/ramdisk tmpfs defaults,size=3072M 0 0

3. Reboot or run the following command to mount everything in your fstab file:

# mount -a

4. Run df to check the size/status of the ramdisk mount:

# df -h

Filesystem            Size  Used Avail Use% Mounted on 
/dev/mapper/vg_minecraft3-lv_root
                      22G  4.7G   16G  23% /
tmpfs                  12G     0   12G   0% /dev/shm
/dev/vda1             477M  185M  267M  41% /boot
tmpfs                 3.0G     0  3.0G   0% /home/minecraft/ramdisk

5. Now copy your Minecraft Server directory into the ramdisk with your favorite copy tool.

6. Remember: before rebooting, if you want to save your Minecraft ramdisk data, you will need to copy it back to your traditional hard drive so it will be persistent.

7. Upon a fresh reboot, you will need to repeat step number five and copy your data back into the ramdisk.

8. When starting the Minecraft Server, make sure you are starting from the ramdisk as well, so you can benefit from the faster I/O.

Memory

Linux kernel memory normally allocates memory in 4KB (KiloBytes) chunks. There is an option to reserve the memory needed for an application and to optimize the chunk size to a larger size of 2MB. A chunk size of 2MB is called huge pages. 

If the Minecraft Java service requires 5GB of RAM to run, we can either let the kernel track that with 1,310,720 chunks or 2,560 chunks. The more chunks it has to manage and track, the more CPU overhead. Below are the four steps to set up huge pages:

1. To determine the size required for reserving as huge pages, you need to know how big your Minecraft Java process is. This number should be the size you allocate to the Minecraft Java process in your start up script or the command line option you pass to Java when you launch the Minecraft process. Look for the “-Xmx” option, what size are you passing? I’m using 5GB, as my server supports 30 concurrent Minecraft players.

2. Configure RHEL to reserve the huge pages. This is done by setting a Linux kernel parameter in the /etc/sysctl.conf file. Add this line to that file:

vm.nr_hugepages = 2860

How did I get that number? I want to reserve 5GB of huge pages. I need to divide 5GB/2MB.

5 x 1024 = 5,120

then divide:

5,120 / 2 = 2,560

There needs to be a bit of wiggle room, so I always add an extra 300 to my final number.

2,560 + 300 = 2860

Keep in mind, you will want to account for all your memory in your system. You can not allocate more memory that you have. How much total memory does your system have? How much memory did you allocate to a ramdisk? How much memory will you allocate to huge pages? How much memory is now left over for the OS to use? 

3. Reboot, and check to make sure the huge pages allocation took effect. You can grep /proc/meminfo to verify:

#cat /proc/meminfo  | grep Huge
AnonHugePages:      2048 kB
HugePages_Total:    2860
HugePages_Free:     2860
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB

We can see 2,860 2MB HugePage total chunks and they are all free since we haven’t started our Minecraft Server yet.

4. Enable the Minecraft Java process to use huge pages. Pass these two Java options when you start your Minecraft Server: 

-XX:+UseLargePages” and “-XX:LargePageSizeInBytes=2m”

Now start your Minecraft Server, and then grep again to verify that the Java process is consuming your allocated huge pages. 

# cat /proc/meminfo | grep Huge 
AnonHugePages:     61440 kB
HugePages_Total:    2860
HugePages_Free:      168
HugePages_Rsvd:      129
HugePages_Surp:        0
Hugepagesize:       2048 kB

Above we can see we only have 168 HugePages_Free, so our configuration is working.

The Linux kernel in RHEL 7 and RHEL 8 include TransparentHugePages, which allows the kernel to automatically determine if the kernel will allocate 4KB or 2MB chunks of memory. This works great if you have the extra CPU horsepower. If you do not, you will want to pre-allocate your huge pages as laid out in this article.

More information about hugepages and transparent huge pages is in the Knowledgebase.

Java

The Java startup process is configurable with many Java command line options. Here are the Java options I found that work best for my Minecraft Server running up to 30 players.

$MINECRAFT=server.1.15.2.jar

java -Xms5G -Xmx5G -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=100 -XX:+DisableExplicitGC -X
X:TargetSurvivorRatio=90 -XX:G1NewSizePercent=50 -XX:G1MaxNewSizePercent=80 -XX:G1MixedGCLiveThresholdPercent=50 -XX:+AlwaysPreTouch -XX:+UseL
argePages -XX:+UseLargePagesInMetaspace -XX:LargePageSizeInBytes=2m -jar $MINECRAFT nogui

From my research, these options work the best. Java has five different garbage collection algorithms. I found that UseG1GC is optimal for my server, and introduces the least amount of lag.

Kernel Security Mitigations

In the recent past, the x86_64 CPU architecture has been impacted by several security related issues. These issues have been mitigated via firmware and software patches. Some older x86_64 CPU architectures which were patched via kernel software patches introduced performance penalties. 

If you have an older CPU and still want to get the best performance from it and are willing to accept the security risk, or feel you are not at risk, you can disable these security features at boot time.

Caution: Please do not do this if you are running RHEL or any other Linux distribution in production, as it will likely make your system less secure. 

That being said, you may have a system that is on a trusted network, or at home, and you want to enable its full potential.

To disable these security mitigations, append the following options to your kernel boot line:

RHEL 6 / 7

spectre_v2=off nopti

RHEL 8

nospectre_v2 nopti

Once booted, you can validate that these mitigations are disabled. For RHEL 6, first mount the debugFS, RHEL 7 and RHEL 8 already do this by default.

#mount -t debugfs nodev /sys/kernel/debug

RHEL 6/7/8

# cat /sys/kernel/debug/x86/pti_enabled
# cat /sys/kernel/debug/x86/retp_enabled
# cat /sys/kernel/debug/x86/ibrs_enabled

Each file should have a “0” in it, meaning it is disabled. More details about the security mitigations and details can be found in this post on Meltdown and Spectre from January 2018 and our Knowledgebase article updated in February 2020.

I trust this was a meaningful exercise in the many facets of RHEL optimizations that can be configured to improve performance and reduce latency. As all tuning exercises, your mileage will vary, and testing is important. There are many variables in each system and solution, but knowing where to look, what options you have, and where to find intelligent information will aid you in your journey.