Hosting Minecraft Servers + BungeeCord with FreeNAS
Published:
1. Prerequisites
-
Console access to a server running FreeNAS
(Don’t download these yet, read on)
-
Your server jarfile: spigot.jar, or paper.jar, etc.
-
Your proxy jarfile: bungeecord.jar, or waterfall.jar, etc.
This guide will be using Paper
and Waterfall
as they are more
performant versions of spigot and bungeecord, respectively. They also
fully support spigot/bungee plugins.
2. Preparing the Jails
The first thing we’ll need to do is create the main jail (the one running bungeecord or waterfall). Alter the version for the current one your server is running.
echo '{"pkgs":["openjdk12", "curl"]}' > /tmp/minecraft.json
When a jail is created, pkg
isn’t installed by default. Using this
saves us some commands in the next step.
iocage create \
--name <name> \
--pkglist /tmp/minecraft.json
--release 11.2-RELEASE \
ip4_addr="vnet0|192.168.1.xxx/24" \
defaultrouter="192.168.1.1" \
vnet="on" \
allow_raw_sockets="1" \
boot="on"
ip4_addr
and defaultrouter
configure the network for our jail. If
you’re running the server on your local network, then it’s likely the
address of your router. (Typically x.x.x.1 or x.x.x.255, consult your
router’s manual). ip4_addr
is the address you want to assign to the
jail.
For name
: If you’re just creating a single server, the name
minecraft
would suffice, but I recommend a structure like:
mc_main mc_survival mc_creative ...etc
Next, let’s create directories in the jails and mount them in the root system:
# Creates the directories in the root system
mkdir -p /xxx/data/minecraft/mc_main
mkdir -p /xxx/data/minecraft/waterfall
# Creates the directories in the jail
iocage exec mc_main mkdir /root/minecraft
iocage exec mc_main mkdir /root/waterfall
# Mounts the directories in the root system
iocage fstab -a mc_main \
/xxx/data/minecraft/mc_main /root/minecraft nullfs rw 0 0
iocage fstab -a mc_main \
/xxx/data/minecraft/waterfall /root/waterfall nullfs rw 0 0
Change /xxx/data/
to a relevant filepath for your setup. My main ZFS
pool is named lily
, so I put my minecraft folders under
/mnt/lily/data/
.
3. Setting up Minecraft
We can access our jails with:
iocage console mc_main
Now we can cd to the minecraft directory and download our server jarfile:
cd /root/minecraft
# paper
curl -L 'https://papermc.io/api/v1/paper/1.15.1/27/download' -o paper-1.15.1_27.jar
Run your server jarfile with:
java -jar /root/minecraft/paper-1.15.1_27.jar
The process should end shortly, printing text about a eula.txt
. Edit
the file with:
sed -i .bak 's/eula=false/eula=true/g' /root/minecraft/eula.txt
We also need to edit the server.properties
file. Open it and change
the server port to 25566
. We’re changing this value because our proxy
server (bungeecord/waterfall) will listen to the default port later.
Now we can run the server!
java -Xms1024M -Xmx1024M -jar /root/minecraft/<name>.jar
Observe the text output for any errors, and then go ahead and connect to
it! (Go to the Multiplayer
tab, click Add Server
, and type in the ip
address of your server, e.g. 192.168.1.xx:25566
) Once you join, text
should get printed to the screen.
However, we have an issue. The server is currently running in an
interactive session (i.e. we ran it manually). We need it to run by
itself, in a noninteractive session. Some guides will recommend using
screen
or tmux
, but I strongly recommend not using them. If your
server ever restarts, you will have to go in manually and restart
screen/tmux, and then your minecraft server.
It’s far better to let
rc.d
manage it for us. Add this script to /usr/local/etc/minecraftd
:
#!/bin/sh # # PROVIDE: minecraftd # REQUIRE: LOGIN DAEMON NETWORKING mountcritlocal # KEYWORD: shutdown # # Use the following variables to configure the minecraft server. For example, to # configure the ON/OFF knob variable: # sysrc minecraftd_enable="YES" # # minecraftd_enable="YES" # minecraftd_user_dir="/root/minecraft" # minecraftd_jar_path="/root/minecraft/server.jar" # minecraftd_java_opts="-Xms1024M -Xmx1024M" . /etc/rc.subr name=minecraftd rcvar=`set_rcvar` pidfile=/var/run/minecraftd.pid load_rc_config $name start_cmd="${name}_start" stop_cmd="${name}_stop" status_cmd="${name}_status" : ${minecraftd_enable="NO"} : ${minecraftd_user_dir="/root/minecraft"} : ${minecraftd_jar_path="/root/minecraft/server.jar"} : ${minecraftd_java_opts="-Xms1024M -Xmx1024M"} minecraftd_start() { if [ -e $pidfile ]; then echo "$name already running." else echo "Starting $name..." /usr/sbin/daemon -f -p $pidfile \ /usr/local/bin/java -Duser.dir=$minecraftd_user_dir \ $minecraftd_java_opts \ -jar $minecraftd_jar_path nogui echo "$name started." fi } minecraftd_stop() { if [ -e $pidfile ]; then echo "Stopping $name..." cat $pidfile | xargs kill echo "Stopped." else echo "$name is not running." fi } minecraftd_status() { if [ -e $pidfile ]; then echo "$name is running." else echo "$name is not running." fi } run_rc_command $1
We have to make the service file executable, so run:
chmod +x /usr/local/etc/rc.d/minecraftd
Essentially, this script lets us not have to manage the server process manually. However we also need to update some settings:
sysrc minecraftd_enable="YES"
sysrc minecraftd_jar_path="/root/minecraft/server.jar"
sysrc minecraftd_java_opts="-Xms1G -Xmx1G"
Make sure to change the minecraftd_jar_path
to reflect your downloaded
jarfile, and minecraftd_java_opts
for how much memory you want to give
it.
Now you should be able to the following:
service minecraftd start
Confirm it with:
ps aux | grep openjdk
You should see something like:
root 72816 1.2 2.5 3156872 1273052 - IJ Fri19 99:04.12 /usr/local/openjdk12/bin/java -Duser.dir=/root/minecraft -Xms1G -Xmx1G -jar /root/minecraft/paper-1.15.1_27.jar
If not, double check your settings in /etc/rc.conf
, and make sure they
point to the right files. Manually run the command to make sure it’s not
a minecraft configuration issue (i.e. no warnings/errors show up in the
console).
Connect to the server again to make sure it works, then stop the server with:
service minecraftd stop
In order for bungee/waterfall to work, we need to edit the
server.properties
file again. Change online-mode
to false
.
In spigot.yml
, update bungeecord
to true
.
In paper.yml
, update bungee-online-mode
to true
.
In bukkit.yml
, update connection-throttle
to -1
.
That’s it! Now we just need to set up our proxy to get access to the server again.
4. Setting up Waterfall/Bungeecord
If you’re not in the mc_main
jail already, access it with:
iocage console mc_main
Now can cd to the waterfall directory and download waterfall.jar
.
cd /root/waterfall
The process for the jarfile is the same as before, except we’re going to
the waterfall
directory now.
cd /root/waterfall
# waterfall
curl -L 'https://papermc.io/api/v1/waterfall/1.15/309/download' -o waterfall-1.15_309.jar
Run the proxy jarfile with:
java -jar /root/waterfall/waterfall-1.15_309.jar
If necessary, edit eula.txt
again:
sed -i .bak 's/eula=false/eula=true/g' /root/waterfall/eula.txt
Now we need to edit config.yml
. Look for the servers
section, and
change it to the following:
servers:
hub:
motd: '&1My amazing hub server'
address: localhost:25566
restricted: false
Under listeners
, change priorities
to:
priorities:
- hub
Change host
to:
host: 0.0.0.0:25565
Finally, set ip_forward: true
.
In order to run waterfall
noninteractively, we’ll use a similar rc.d
script like before:
#!/bin/sh
#
# PROVIDE: waterfall
# REQUIRE: LOGIN DAEMON NETWORKING mountcritlocal
# KEYWORD: shutdown
#
# Use the following variables to configure the minecraft server. For example, to
# configure the ON/OFF knob variable:
# sysrc waterfall_enable="YES"
#
# waterfall_enable="YES"
# waterfall_user_dir="/root/waterfall"
# waterfall_jar_path="/root/waterfall/waterfall.jar"
# waterfall_java_opts="-Xms512M -Xmx1024M"
. /etc/rc.subr
name=waterfall
rcvar=`set_rcvar`
pidfile=/var/run/waterfall.pid
load_rc_config $name
start_cmd="${name}_start"
stop_cmd="${name}_stop"
status_cmd="${name}_status"
: ${waterfall_enable="NO"}
: ${waterfall_user_dir="/root/waterfall"}
: ${waterfall_jar_path="/root/waterfall/waterfall.jar"}
: ${waterfall_java_opts="-Xms512M -Xmx1024M"}
waterfall_start() {
if [ -e $pidfile ]; then
echo "$name already running."
else
echo "Starting $name..."
cd $waterfall_user_dir
/usr/sbin/daemon -f -p $pidfile \
/usr/local/bin/java -Duser.dir=$waterfall_user_dir \
$waterfall_java_opts \
-jar $waterfall_jar_path nogui
echo "$name started."
fi
}
waterfall_stop() {
if [ -e $pidfile ]; then
echo "Stopping $name..."
cat $pidfile | xargs kill
echo "Stopped."
else
echo "$name is not running."
fi
}
waterfall_status() {
if [ -e $pidfile ]; then
echo "$name is running."
else
echo "$name is not running."
fi
}
run_rc_command $1
We have to make the service file executable again, so run:
chmod +x /usr/local/etc/rc.d/waterfall
Like before, we’ll need to edit some settings:
sysrc waterfall_enable="YES"
sysrc waterfall_jar_path="/root/waterfall/waterfall-1.15_309.jar"
Now run the following and connect to your server!
service waterfall start
Congrats, you’ve set up a minecraft server and a proxy server in FreeNAS!
For more servers, create more jails with the instructions from earlier,
and follow the same server javafile setup. In server.properties
,
change ip-address
to the address of the jail, and update waterfall’s
config to something like below:
servers:
hub:
motd: 'My hub server'
address: localhost:25566
restricted: false
creative:
motd: 'My creative server'
address: 192.168.1.21:25566
restricted: false
survival:
motd: 'My survival server'
address: 192.168.1.22:25566
restricted: false
5. Databases for Plugins
Many minecraft plugins can use mysql or postgres for storage. I highly recommend setting up mysql in a jail and connecting your plugins to it. Here’s how you can do it:
echo '{"pkgs":["mysql80-server"]}' > /tmp/mysql.json
iocage create \
--name mysql \
--pkglist /tmp/minecraft.json
--release 11.2-RELEASE
ip4_addr="vnet0|192.168.1.xxx/24" \
defaultrouter="192.168.1.1" \
vnet="on" \
boot="on"
# Creates the directories in the root system
mkdir -p /xxx/configs/mysql
mkdir -p /xxx/data/mysql
# Creates the directories in the jail
iocage exec mysql mkdir /config
iocage exec mysql mkdir /data
# Mounts the directories in the root system
iocage fstab -a mysql \
/xxx/configs/mysql /config nullfs rw 0 0
iocage fstab -a mysql \
/xxx/data/mysql /data nullfs rw 0 0
iocage console mysql
sysrc mysql_enable="YES"
sysrc mysql_dbdir="/data"
sysrc mysql_confdir="/config"
sysrc mysql_optfile="/config/my.cnf"
cp /usr/local/etc/mysql/my.cnf /config/my.cnf
Now edit /config/my.cnf
and change bind-address
to 0.0.0.0
. This
lets us connect remotely, except for root
. We don’t want to run things
as root, anyway.
Connect to the local mysql database after creating the root user and password:
service mysql-server start
mysql_secure_installation
mysql -uroot -p
Once connected, create the database and user that the plugin will be
using. For example, if we were making a database for coreprotect
:
create database coreprotect_hub;
CREATE USER 'coreprotect'@'192.168.1.0/255.255.255.0' IDENTIFIED WITH mysql_native_password BY 'password';
grant all privileges on coreprotect_hub.* to 'coreprotect'@'192.168.1.0/255.255.255.0';
The reason we’re using @'192.168.1.0/255.255.255.0'
is to allow remote
connections, but only within the local network.
Now update your plugin’s config.yml
, where host
is the ip address of
the mysql jail, and port
is 3306
.
It’s a good idea to create a separate database and user for each plugin
and server. For example, I have the databases coreprotect_survival
,
and coreprotect_creative
, both of which have different users.