Running a Headless Factorio Server on FreeBSD
In this guide, I will discuss how you can go about running a headless
factorio game server on your FreeBSD server.
My server is an AMD64 system running FreeBSD 11-RELEASE
, although to my
knowledge nothing in this guide is specific to this version of FreeBSD.
In particular, this guide will cover:
- Installing and running the Factorio standalone server.
- Writing a FreeBSD rc.d script to run the Factorio server as a system service.
- Configuring the Factorio standalone server.
- Managing the Factorio standalone servier via RCON.
This guide assumes you are familiar with the basics of UNIX server administration, and the UNIX shell. You must own a legitimately licensed copy of Factorio to run the game server.
Note: except as noted, all commands should be run as root.
What is Factorio
For the un-initiated:
Factorio is a game in which you build and maintain factories.
You will be mining resources, researching technologies, building infrastructure, automating production and fighting enemies. Use your imagination to design your factory, combine simple elements into ingenious structures, apply management skills to keep it working and finally protect it from the creatures who don’t really like you.
The game is very stable and optimized for building massive factories. You can create your own maps, write mods in Lua or play with friends via Multiplayer.
Software Versions
- FreeBSD
11.0-RELEASE
- Factorio
0.15.30
(experimental release) - RCON
1.0.2
Last updated: 2017-07-24
Installing Factorio
We will install Factorio under it’s own prefix in /opt
, which will run under
it’s own user account (for security). Before starting, you should enable Linux
binary compatibility on
your server. To get the Linux libraries needed to run the server, you should
install linux_base-c7
(CentOS 7.X compatibility).
First, we will setup the Factorio user account:
pw useradd factorio
Now, we will create the required /opt
folder. For convenience, and to make
snapshots easy, you can optionally create /opt/factorio
as a ZFS filesystem.
if you don’t know what that means, just make it as a folder.
To make /opt/factorio
as a ZFS filesystem:
zfs create -o mountpoint=/opt/factorio zroot/factorio
OR (don’t do both!)
To make /opt/factorio
as a normal directory:
mkdir -p /opt/factorio
Set this directory to be owned by the factorio
user:
chown -R factorio:factorio /opt/factorio
Henceforth, we will run as the factorio user account (su factorio
) - all
commands should be run as the user factorio
unless otherwise noted.
Download the Factorio headless server for Linux from this
page. The current
version at the time this article was last updated is noted in the previous
section. Save the current release tarball to /opt/factorio
, and let’s call it
/opt/factorio/factorio-release.tar.gz
. Extract it and move the content’s to
the current directory:
cd /opt/factorio
tar xvf factorio-release.tar.gz
mv factorio/* ./
rmdir factorio
Optionally, you can also remove the release tarball as well. We will not be using it again in this guide.
To test that you have installed Factorio correctly, and that Linux emulation is working as it should, you can run:
bin/x64/factorio --version
The output should reflect the version you downloaded. On my particular system at the time of this article’s writing, the output was:
Version: 0.15.30 (build 30727, linux64, headless)
Binary version: 64
Map input version: 0.12.0-0
Map output version: 0.15.30-1
Running Factorio as a Service
While reading this section, you may find the document Practical rc.d scripting in BSD helpful as reference material.
Note: in this section, we are running as root again.
Place the following script at /usr/local/etc/rc.d/factorio
. Be sure to read
through the script and configure it to your needs. Don’t forget to mark the
script executable with chmod +x
.
#!/bin/sh
# KEYWORD: shutdown
. /etc/rc.subr
# service name
name=factorio
# "on/off" variable for /etc/rc.conf
rcvar=factorio_enable
# handle loading RC variables
load_rc_config $name
: ${factorio_enable:=no} # default disabled
# command to start the service
start_cmd="${name}_start"
# command to stop the service
stop_cmd="${name}_stop"
### factorio-specific configuration ###
# factorio installation dir (should contain bin/x64/factorio)
FACTORIO_PREFIX=/opt/factorio
# factorio binary path
FACTORIO_BINARY="$FACTORIO_PREFIX/bin/x64/factorio"
# factorio save file
FACTORIO_SAVE_FILE="$FACTORIO_PREFIX/serversave.zip"
# factorio server port (default is 34197)
FACTORIO_PORT=34197
# bind IP (XXX.XXX.XXX.XXX:port)
FACTORIO_IP="0.0.0.0:$FACTORIO_PORT"
# RCON port
FACTORIO_RCON_PORT=34198
# RCON password (CHANGE THIS!)
FACTORIO_RCON_PASSWORD="default"
# server server-settings.json
FACTORIO_SETTINGS="$FACTORIO_PREFIX/data/server-settings.json"
# path to write console logs to
FACTORIO_LOGFILE="/var/log/factorio.log"
# path for the pidfile
FACTORIO_PIDFILE="/var/run/factorio.pid"
# user to run the server as
FACTORIO_USER="factorio"
# build up the factorio arguments list
FACTORIO_ARGS=" --bind $FACTORIO_IP --rcon-port $FACTORIO_RCON_PORT --rcon-password $FACTORIO_RCON_PASSWORD --server-settings $FACTORIO_SETTINGS --start-server $FACTORIO_SAVE_FILE"
#######
factorio_get_running_pid() {
# get the PIDfile stored on disk. If the PID is current running, echo it, else
# return 1
if [ -e "$FACTORIO_PIDFILE" ] ; then
PID=$(cat "$FACTORIO_PIDFILE")
fi
N=$(ps aux | grep "$PID" | grep -v "grep" | wc -l)
if [ $N == 1 ] ; then
echo "$PID"
else
return 1
fi
}
factorio_start() {
# check if there is already a copy of this service running
if [ -e "$FACTORIO_PIDFILE" ] ; then
RUNNING_PID=$(factorio_get_running_pid)
if [ $? -ne 0 ] ; then
# there is a stale pidfile
rm "$FACTORIO_PIDFILE"
else
echo "factorio is already running with PID $RUNNING_PID"
exit 1
fi
fi
# back up the old log file
if [ -e "$FACTORIO_LOGFILE" ] ; then
mv "$FACTORIO_LOGFILE" "$FACTORIO_LOGFILE.old"
fi
# we have to create the log file so we can chown it to the factorio user
touch "$FACTORIO_LOGFILE"
chown "$FACTORIO_USER" "$FACTORIO_LOGFILE"
# launch the server
/usr/local/bin/sudo -u $FACTORIO_USER sh -c "$FACTORIO_BINARY $FACTORIO_ARGS 2>&1 > $FACTORIO_LOGFILE" &
PID=$!
echo "$PID" > "$FACTORIO_PIDFILE"
echo "started $name, pidfile is: $FACTORIO_PIDFILE, PID is $(cat $FACTORIO_PIDFILE) "
# make sure the server started correctly
sleep 5
factorio_get_running_pid > /dev/null
if [ $? -eq 0 ] ; then
echo "factorio is running with PID $PID"
else
echo "factorio failed to start, check $FACTORIO_LOGFILE"
exit 1
fi
}
factorio_stop() {
# check to see if factorio is really running
RUNNING_PID=$(factorio_get_running_pid)
if [ $? -ne 0 ] ; then
echo "factorio is not running."
exit 0
fi
# if so, shut it down gracefully
echo "Killing PID $RUNNING_PID..."
kill "$RUNNING_PID"
sleep 10
# clean up the pidfile
factorio_get_running_pid > /dev/null
if [ $? -ne 0 ] ; then
# the server has closed
echo "cleaning pidfile..."
rm "$FACTORIO_PIDFILE"
else
# the server is still running, try again
echo "waiting for factorio server to stop..."
factorio_stop
fi
echo "factorio has stopped."
}
# implement "status" command
if [ "$1" == "status" ] ; then
RUNNING_PID=$(factorio_get_running_pid)
if [ $? -ne 0 ] ; then
echo "factorio is not running."
else
echo "factorio is running with pid $RUNNING_PID"
fi
exit 0
fi
run_rc_command "$1"
This script will allow us to:
- Run factorio as a background daemon, rather than leaving it attached to a console session.
- Start and stop the factorio daemon.
- Check if Factorio is running or not.
- All of this via the standard
rc
componant of FreeBSD.
Further, thanks to the line # KEYWORD: shutdown
, the Factorio service should
be shutdown safely if the system is powered off through normal means
(/sbin/shutdown
).
Note that this script isn’t perfect by any means - this is my first BSD rc.d script, so I’ve probably missed some things. If you have a suggestion for how to improve this script, please send me an email!
Configuring the Factorio Server
You can edit the file /opt/factorio/data/server-settings.json
to configure
Factorio. You can find some relevant information on this subject on the
Factorio wiki,
however, I have found the following modifications are particularly convienient:
autosave_interval
- should be set to a fairly low number (I suggest 3 minutes); raise it if it causes performance issues.autosave_slots
- I prefer to set this to a high number. Combined with a short autosave interval, this will give you a highly granular, but also long-lived backup history (if you have the disk space to space!)
Using the Factorio Server
The first time you start the server via service factorio onestart
, you will
probably get a nasty error like this:
started factorio, pidfile is: /var/run/factorio.pid, PID is 1234
factorio failed to start, check /var/log/factorio.log
If you consult the logfile, you will see that this error is caused by the server’s world save file not existing:
0.504 Error ServerMultiplayerManager.cpp:94: MultiplayerManager failed: "File /opt/factorio/serversave.zip does not exist."
Fortunately, this is very easy to rectify, simply run as the factorio
user:
/opt/factorio/bin/x64/factorio --create /opt/factorio/serversave
Now you should be able to launch the server with service factorio onestart
You may have noticed that simply running service factorio start
makes rc
yell at you with an error like this:
Cannot 'start' factorio. Set factorio_enable to YES in /etc/rc.conf or use 'onestart' instead of 'start'.
This is because the service has not been enabled. To enable the service (which
will also cause your factorio server to boot when your turn on your FreeBSD
server), add the line factorio_enable=YES
to /etc/rc.conf
.
Managing the Factorio Server with RCON
RCON is an extremely helpful tool for managing Factorio servers which run
headlessly. It can be downloaded from a forum
post. You can connect
to your Factorio server with RCON via the information you configured in the
rc
script above.
Disappointingly, this tool does not run on UNIX systems, nor could I get it to run under WINE. However, running RCON via Parallels on my MacBook worked quite well.
For now, the main uses of RCON are to create additional saves on the server, send messages to and amdinistrate the connected players, and to terminate the server safely. This is valuable if you want to give operators access to administrate your Factorio server, but without giving them access to start and stop services on your FreeBSD host. Likewise, RCON is also useful for sending messages or otherwise interacting with players without launching the Factorio client first.
Conclusion
Thus, with a little know how and some shell scripts, it is quite straightforward to run Factorio on FreeBSD thanks to FreeBSD’s excellent Linux emulation. Using the method outlined above, it can even be run as a background daemon, without requiring one to leave a console session attached. Furthermore, the RCON administration tool allows one to perform common administrative tasks on the Factorio server without requiring a Factorio game client to be running, or shell access on the FreeBSD host running the game server.