The Blog of Charles Daniels

Running a Headless Factorio Server on FreeBSD

Contents

Introduction

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:

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.

source

Software Versions

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:

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:

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.