Minecraft on SmartOS
Minecraft.
It's one of those games that can appeal to many different people in many different ways. I got into playing under the assumption that modded Minecraft was the norm, and that calculating the optimum designs for automated power production and gargantuan resource-gathering apparatuses was the entire point of the game.
Today, we'll explore how to robustly host Minecraft on SmartOS, either as a simple vanilla server, a Minecraft Forge server with mods, a Spigot server or as a BungeeCord proxy server.
Zone setup
We'll be operating within a current standard base-64 zone. A base-32 zone should work as well, assuming your server never exceeds 4GB of memory. You will likely need to tune max_physical_memory
, cpu_cap
and quota
for your specific Minecraft server, and are likely to change over time. If you plan on hosting multiple Minecraft servers from this zone, your resource usage is going to skyrocket. Plan accordingly.
First, let's install Java. Some older versions of Minecraft may require Java 7, but you really should prefer using Java 8, as it's significantly more performant than it's predecessor.
Also, I'd recommend installing tmux
. It's the easiest way to periodically access a server console.
Lastly, you will want to install git
if you're planning on building a Spigot server (which must be built from source). This additional package is unnecessary with both vanilla Minecraft and Minecraft Forge.
# pkgin in openjdk8 tmux git
I prefer cleaning up the delegated dataset cruft. We can create a dataset mounted under /var/db/minecraft
as well.
# UUID=$(sysinfo | json UUID)
# zfs set mountpoint=none zones/$UUID/data
# rmdir -p /zones/$UUID
# zfs create -o mountpoint=/var/db/minecraft -o quota=8G zones/$UUID/data/minecraft
Next, we can create our Minecraft group and user, set up limits and set the proper permissions for their home directory.
# groupadd -g 900 minecraft
# useradd -u 900 -g minecraft -d /var/db/minecraft -s /bin/bash \
-c "Minecraft user" minecraft
# projadd -U minecraft -G minecraft -c "Minecraft server" \
-K "process.max-file-descriptor=(basic,65536,deny)" minecraft
# chown minecraft:minecraft /var/db/minecraft
# chmod 700 /var/db/minecraft
You will need to unlock the minecraft account if you want it to be able to run its own crontab (helpful later on).
# passwd -N minecraft
And then su
into them to install Minecraft.
# su - minecraft
Installing Minecraft
There are effectively three different Minecraft servers that are popular today:
- (Vanilla) Minecraft from Mojang is the easiest to install and maintain, and generally uses the least resources of the three.
- Spigot, a high-performance Minecraft server that supports Bukkit plugins, and is compatible with Vanilla clients.
- Minecraft Forge, which supports client and server side mods and is arguably the most powerful of the three (also generally considered the most resource intensive). Minecraft Forge is also required if you're using a Minecraft Forge client and is generally incompatible with the vanilla client.
We'll cover how to install all three below.
Notice: Each of these sub-sections represents a different type of server install, and should be done separately.
Minecraft server
The Vanilla Minecraft server can be downloaded directly from Majong.
As the Minecraft user, use wget
to download the Java Archive file.
$ wget https://s3.amazonaws.com/Minecraft.Download/versions/1.10.2/minecraft_server.1.10.2.jar -O server.1.10.2.jar
Agree to the EULA.
$ echo eula=true > eula.txt
And then start the server.
$ java -server -jar server.1.10.2.jar nogui
If all went well, you should have a functional vanilla Minecraft server.
Spigot server
Due to a legal battle between the Mojang and the developers of Spigot, the later project must be downloaded as source code and compiled locally (this is a legal requirement, not a technical one). Fortunately, the developers of Spigot have written a tool that does just that.
First, we will need to download this jar file.
$ wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar
And then just run it to build against the latest version of Minecraft.
$ java -jar BuildTools.jar
If you want to build against a specific version of Minecraft, that can be specified through the --rev
flag.
$ java -jar BuildTools.jar --rev 1.9.4
Wait for the build process to complete (it can take quite a while) and if all goes well, you'll have a shiny new spigot-1.10.jar
and craftbukkit-1.10.jar
file to work with.
Agree to the EULA.
$ echo eula=true > eula.txt
And then start the Spigot server.
$ java -server -jar spigot.1.10.jar nogui
If all went well, you should have a functional Spigot Minecraft server. Shut down the server and delete all of the additional files and directories produced by BuildTools.
> stop
...
$ rm -r .m2 BuildData BuildTools.log.txt Bukkit CraftBukkit Spigot apache-maven-3.5.0 work
You can also delete BuildTools.jar
if you don't intend on using it again.
$ rm BuildTools.jar
Minecraft Forge server
Minecraft Forge is most popularly distributed at the core of Modpacks: Community designed combinations of community developed Minecraft mods that have been balanced for reasonable gameplay.
Popular mod-pack projects such as Feed the Beast and ATLauncher ship client launchers that allow a user to download and install hundreds of different versions of dozens of different mod-packs. These launchers can also produce server packs which are suitable to be uploaded to and extracted on the SmartOS zone we're working on.
Feed the Beast distributes their server mod-packs as zip files directly from their CDN, making them the ideal example to use for this section.
Use wget
to download and unzip
to decompress a server mod-pack.
$ wget http://ftb.cursecdn.com/FTB2/modpacks/FTBInfinity/2_0_0/FTBInfinityServer.zip
...
$ unzip FTBInfinityServer.zip
Download the vanilla Minecraft server and launch wrapper by running the FTBInstall
script.
$ sh FTBInstall.sh
Agree to the EULA.
$ echo eula=true > eula.txt
And then start the Minecraft server.
$ java -server -Xms1G -Xmx4G -jar FTBServer-1.7.10-1448.jar nogui
Notice: Minecraft Forge takes the longest to start of the three different server types, and won't work without extra memory. In the above example: 4G.
Once this test is complete, you can remove much of the install cruft that comes with FTB mod-pack servers. We will be replacing their start scripts with something more robust in the optimization section below.
$ rm FTBInfinityServer.zip FTBInstall.* ServerStart.*
BungeeCord Server
BungeeCord is a Minecraft proxy server that sits between your clients and one or more Minecraft servers (as it's part of the Spigot project, it's safe to assume Spigot servers are supported), and enables large-scale server deployments. Details about the configuration of BungeeCord are available on their website.
Download the BungeeCord server.
wget https://ci.md-5.net/job/BungeeCord/lastSuccessfulBuild/artifact/bootstrap/target/BungeeCord.jar
Configure it, and then start the server.
$ java -server -jar BungeeCord.jar
Server optimizations
Getting a Minecraft server to start and run is just the first step. This guide will also focus on getting your server to run well, and to make the most of the SmartOS platform. What follows are a series of sections that focus on optimizing different aspects of running a Minecraft server on SmartOS.
Optimized Java parameters
Java Virtual Machines can be started with parameters which adjust their default behavior, and while the start commands in the above examples allowed us to start and test our Minecraft servers just fine, they're the bare minimum and hardly optimal for the daily operation of a high volume server.
The optional parameters we'll be examining below should be applied immediately after the java command:
$ java <parameters> -jar server.1.10.2.jar nogui
We'll break them up into the following three classifications.
Essential parameters
These parameters should always be set for a Minecraft server.
-XmsM
: Sets the initial size of the heap to M. This is the memory consumed by the JVM when first starting. If you want to start with 1GB, the parameter would be:-Xms1G
.-XmxM
: Sets the maximum size of the heap to M. This is the maximum amount of memory that can be consumed by the JVM and should always be greater than or equal to-XmsM
. If you want to limit the server to 4GB, the parameter would be:-Xmx4G
.-XX:+UseConcMarkSweepGC
or-XX:+UseG1GC
: Enables the use of the CMS garbage collector for the old generation. It's recommended that you use this GC when application latency requirements cannot be met by the throughput garbage collector. The G1 garbage collector (+UseG1GC
) is another alternative, but it might not be as memory efficient.-XX:+UseLargePages
: SmartOS supports large pages and enabling this should translate to a performance improvement.
Optional parameters
These parameters may be useful for a Minecraft server in certain situations.
-d64
: Forces JVM to be 64-bit. This is only practical on abase-multiarch
image, as java onbase-32
andbase-64
will always be one or the other.-server
: Selects the Java HotSpot Server VM. This is only necessary with the 32-bit JVM, as the 64-bit JVM only supports the Server VM.-showversion
: Displays JVM version information before continuing execution of the application. This is practical for logging purposes.-XX:+AggressiveOpts
: Enables the use of aggressive performance optimization features which are expected to become the default in upcoming Java releases. This option may improve performance, but that performance may come at the cost of stability.-XX:MinHeapFreeRatio=N
,-XX:MaxHeapFreeRatio=N
: Sets the minimum and maximum allowed percentage of free heap space (0 to 100) after a garbage collection event. If the free space after a garbage collection event is less than the minimum by the given percentage than the JVM will attempt to allocate additional memory to the heap until the difference is greater than the minimum given percentage. If the free space after a garbage collection event is greater than the maximum by the given percentage than the JVM will attempt to free memory from the heap until the difference is less than the maximum given percentage. The current defaults of 40% and 75% should be suitable, but I've seen configurations as extreme as 5% and 10%.-XX:MaxGCPauseMillis=10
: Sets a target for the maximum garbage collection pause time (in milliseconds). This is a soft goal, and the JVM will make it's best effort to achieve it. In the case of Minecraft, 10ms is unreasonably short, but this should help to tune the JVM to run shorter more frequent garbage collection cycles.
Depreciated parameters
Despite often being recommended by other guides, these parameters should not be used on SmartOS.
-Xincgc
: Enables incremental garbage collection. This option was deprecated in Java 8 with no replacement. Use CMS, G1, or let the JVM decide instead.-XX:PermSize=M
: Sets the space allocated to the permanent generation that triggers garbage collection if it is exceeded. This parameter has been deprecated in Java 8 and superseded by the-XX:MetaspaceSize
parameter, which shouldn't really be tuned either.-XX:+UseParNewGC
,-XX:+UseParallelGC
: Explicitly tells the JVM which garbage collection strategy should be used. Neither of these garbage collectors should be selected (UPN will be automatically enabled with CMS).-XX:+CMSIncrementalPacing
: Enables automatic adjustment of the incremental mode duty cycle based on statistics collected while the JVM is running. This option has been deprecated in Java 8 with no replacement.-XX:+CMSClassUnloadingEnabled
: Enables class unloading when using the concurrent mark-sweep (CMS) garbage collector. This option is enabled by default, meaning there's no reason to enable it explicitly.-XX:ParallelGCThreads=2
: Sets the number of threads used for parallel garbage collection. This should be automatically determined by the JVM.-XX:-UseVMInterruptibleIO
: Thread interrupts before or with EINTR for I/O operations results in OS_INTRPT. I could find no additional documentation describing what this parameter does, so I can't really recommend using it.
Example
Based on the above parameters, I came up with the following JVM options for a Minecraft server running on SmartOS. It's not perfect, but it leaves most of the finer configuration up to the JVM and out of our hair.
$ java -Xms256M -Xmx4G -XX:+UseConcMarkSweepGC -XX:+UseLargePages \
-jar server.1.10.2.jar nogui
If you'd prefer something more complicated, there are other recommendations out there, but I would recommend you cross-reference them with the Java8 command line documentation to ensure you're not specifying a default or depreciated parameter.
Now with that sorted, you can either take this string of options and write them into a startup.sh script to manually call from within your Minecraft directory, or you can start up Minecraft the fun way.
Minecraft and SMF
SmartOS ships with the Solaris Service Management Facility, which is the ideal tool to ensure our Minecraft server stays running. And since we're on SmartOS, It'd be a shame not to use it!
Note: Minecraft can either be managed directly by SMF or wrapped with tmux
. Direct is simpler, but you will be unable to issue commands directly to the Minecraft server console, which will cause issues later on. For the sake of completeness, we will explore both options below.
Import SMF Manifest
As root, download the following SMF manifest:
This manifest has been configured for a default Minecraft server instance wrapped with tmux
, if you just want to use Java, uncomment the java only sections and comment out the tmux
wrapped sections (both the exec_method
and propval
tags).
Edit the value_node
tags to reflect the java parameters you want to use with your server. This step can be skipped and done later.
Edit the server propval
tag to reflect your specific minecraft server. This can be done via symlink or skipped and done later as well.
# su - minecraft
$ ln -sf server.1.10.2.jar server.jar
$ logout
Import the manifest and enable the minecraft service.
# svccfg import minecraft-single-smf.xml
# svcadm enable minecraft
You should now have an SMF managed Minecraft server.
Edit Java parameters
Java parameters can be changed and updated directly within SMF without the need to delete and re-import the manifest.
For instance, if you want to expand the initial heap (-XmsN
) from 256M to 1G, you'll need to remove the old value and replace it with a new value:
# svccfg -s minecraft
svc:/gameserver/minecraft> delpropvalue options/parameters -Xms256M
svc:/gameserver/minecraft> addpropvalue options/parameters -Xms1G
svc:/gameserver/minecraft> exit
You can also completely clear a property list and replace it with new values (note, the astring: is only required for the first value and the quotations are optional):
# svccfg -s minecraft
svc:/gameserver/minecraft> delprop options/parameters
svc:/gameserver/minecraft> addpropvalue options/parameters astring: "-Xms1G"
svc:/gameserver/minecraft> addpropvalue options/parameters "-Xmx4G"
svc:/gameserver/minecraft> addpropvalue options/parameters "-XX:+UseConcMarkSweepGC"
svc:/gameserver/minecraft> addpropvalue options/parameters "-XX:+UseLargePages"
svc:/gameserver/minecraft> exit
You can also check the pending property list:
# svccfg -s minecraft listprop options/parameters
If you're happy with what you have, refresh (commit) the configuration and restart the Minecraft service:
# svccfg -s minecraft:default refresh
# svcadm restart minecraft
Edit server JAR
Along with the parameters, the Minecraft server string can be changed and updated directly within SMF without the need to delete and re-import the manifest.
For example, if you want to update the server jar to server.1.10.2.jar
, set the new value using svccfg
(quotations are optional):
# svccfg -s minecraft setprop options/server="server.1.10.2.jar"
Pending configuration data can be read with svccfg
:
# svccfg -s minecraft listprop options/server
options/server astring server.1.10.2.jar
If the pending configuration is correct, refresh (commit) the configuration and restart the Minecraft service:
# svccfg -s minecraft:default refresh
# svcadm restart minecraft
Delegated authorization
By default, only the superuser can manage the Minecraft service state and properties. Permission to alter the state or properties of the Minecraft service can be independently granted to additional users through Role-Based Access Control (RBAC).
First, define authorization descriptions under /etc/security/auth_attr
:
solaris.smf.manage.minecraft:::Manage Minecraft Service States::
solaris.smf.value.minecraft:::Change Values of Minecraft Service Properties::
Then authorize one or more users with these descriptions. For example, the following line will grant state and property management permission to the Minecraft user:
# usermod -A solaris.smf.manage.minecraft,solaris.smf.value.minecraft minecraft
This user is now able to reboot the server and adjust the configuration options discussed above.
# su - minecraft
$ svcadm restart minecraft
This is especially practical if you want to grant one or more users access to reboot your server without having any access to its files:
# useradd -m -A solaris.smf.manage.minecraft brian
# passwd brian
...
# su - brian
$ svcadm restart minecraft
$ ls /var/db/minecraft/
ls: cannot open directory '/var/db/minecraft/': Permission denied
Now Brian can log in and reboot the server without any additional access to server data.
Snapshots > backups
What is possibly the best reason to host Minecraft on SmartOS is the first-class availability of ZFS. Setting up a dataset to contain the world directory along with cron-driven periodic snapshots completely supersedes any functionality provided by CraftBukkit plugins or MinecraftForge mods.
Setup the World Dataset
First, switch your Minecraft server into maintenance state if it's been enabled.
# svcadm mark maintenance minecraft
Move the current world directory to a temporary location.
# mv /var/db/minecraft/world /var/db/minecraft/world_tmp
Create a new dataset for the world data. Optionally set a quota (20G in our example).
# zfs create -o quota=20G zones/$(sysinfo | json UUID)/data/minecraft/world
Chown the root of the new dataset and copy the contents of the old world directory to the new (this may take some time on established worlds). Delete the temporary directory when you're done.
# chown minecraft:minecraft /var/db/minecraft/world
# mv /var/db/minecraft/world_tmp/* /var/db/minecraft/world/
# rmdir /var/db/minecraft/world_tmp
If you're done in maintenance mode, you can now clear your Minecraft service.
# svcadm clear minecraft
Delegated ZFS management
Normally ZFS administration is handled by the superuser, but authorizations over actions can be delegated to other users on a per dataset basis. In our case, we want our Minecraft user to be able to create snapshots of the world dataset so that the periodic snapshot script can be managed under that user.
# zfs allow -lu minecraft snapshot zones/$(sysinfo|json UUID)/data/minecraft/world
Destroying and renaming snapshots can be achieved by granting destroy and rename permissions to any descendant datasets (of which snapshots are). Note that this will allow any user to destroy or rename any descendant dataset.
# zfs allow -du brian destroy,rename zones/$(sysinfo|json UUID)/data/minecraft/world
You can review what permissions have been granted by calling zfs allow <dataset>
:
# zfs allow zones/$(sysinfo|json UUID)/data/minecraft/world
And you can revoke permissions with zfs unallow
:
# zfs unallow -du brian destroy,rename zones/$(sysinfo|json UUID)/data/minecraft/world
Setup periodic snapshots
The easiest way to create snapshots of the world directory is by using a script that disables auto-saving, issues an explicit save creates the snapshot, and then re-enables auto-saving.
I put together a simple implementation in bash that does these things, based on the script described in this blog post.
Notice: This script needs to be able to communicate with the Minecraft server console to function. It's designed to interface with tmux
, as configured in the previous section, and will not work in a java-only configuration.
As the minecraft user, copy the following script to the Minecraft directory:
Be sure to adjust snappath
to reflect your dataset name. Add the script to your crontab scheduled to run as often as you want it to.
For example, here's a crontab that will snapshot your world dataset every 15 minutes:
0,15,30,45 * * * * ./snapshot.sh
Notice: If you get errors about Minecraft not being allowed to execute cronjobs, you will need to unlock the account as root.
# passwd -N minecraft
I am aware that this script does not manage existing snapshots (expiring old ones or rotating snapshots) or send snapshots to other systems for remote replication. I leave extending this script to encompass additional functionality as an exercise for the reader.
Conclusion
This is pretty much everything you need to get a single instance of Minecraft up and running within a single SmartOS zone. I had a bunch more notes that delved into advanced Minecraft multitenancy but I figured that was a bit much for a single blog post, so we'll save that for another day.