10 mins read

Factorio Headless Server Inside LXC Container

Table of Contents

    Short Intro

    This guide takes alot of information from these helpful sources:
    Old Github tutorial and the very helpful comments
    Official Factorio Wiki
    Please consult them first if you are stuck with any part of this guide, as this info may be eventually outdated.

    Create LXC Container

    Create your LXC container. Nothing special has to be done here, I will note that I am using Debian 12 and starting off with 10GB of space as well as 4 CPU cores and 4GB of memory, just to make setup and updating faster. You can reduce this later. If you would like more detailed info on creating an LXC container you can check out my guide for deploying a Minecraft server in an LXC.

    One important thing to note is you may want to set a static IP address, this can be done in the network page by setting IPv4 to static, setting the IP address to your desired address as well as subnet mask (e.g. 192.168.1.25/24) and set your gateway (modem/router) IP address, this is usually something like 192.168.1.1

    Once your LXC is created, make sure to run apt update && apt upgrade you may have to use sudo.

    Downloading and Extracting Server Files

    You can download the files wherever you want, I’m choosing to download it to the home directory, it doesn’t really matter though since the Factorio server itself will be created inside /opt/

    wget -O factorio_headless.tar.xz https://factorio.com/get-download/stable/headless/linux64
    You will now extract the server in /opt/
    cd /opt
    sudo tar -xJf ~/factorio_headless.tar.xz
    This should create a factorio folder inside /opt/

    Permissions

    You will need to create a user named factorio and chown the entire folder.

    NOTE: if you intend to upload your own saves and add mods, you should create the directories mods and saves (/opt/factorio/mods and /opt/factorio/saves) now, and if you do upload a save at a later date you may have to re-run the below commands.

    Create the user:
    useradd factorio
    Chown the directory
    chown -R factorio:factorio /opt/factorio

    Server Settings

    There is an example settings file stored in /opt/factorio/data named server-settings.example.json
    Make a copy of this file and edit it cp server-settings.example.json server-settings.json
    nano server-settings.json
    You will want to set a server name, description and password if wanted. If you want your server to be visible in the public servers list, you will need to enter your factorio.com username and password
    There are other interesting settings in this file, you may want to browse through them as they all have descriptions.

    Test the Server and (optionally) Create World

    To start your server from the CLI you’ll have to run it from the factorio user created earlier, you can swap to this user by running su factorio . if you’d like to get back to the root user from this you can simply run su again.

    To create a world (this is optional if you intend to upload your own world but may be useful for quickly testing it) run /opt/factorio/bin/x64/factorio --create ./saves/newgame.zip
    Run /opt/factorio/bin/x64/factorio --start-server newgame.zip --server-settings /opt/factorio/data/server-settings.json
    Noting that you will have to replace newgame with whatever the created world is called, and if you proceed to upload a world it will be whatever that is called. And as you can probably tell the --server-settings flag just points the command to the settings file you modified earlier.

    You should be able to join the server locally via the containers IP address.

    To stop the server while running it in the CLI, use CTRL+C.

    Port Forwarding

    Simple as adding a TCP/UDP record pointing to your servers IP on port 34197. Most routers/modems do this differently so it’s difficult to go into detail here, if it’s not working at first, try defining a port range (my old router required this) such as 34196:34198 or 34196-34198.

    Routing through NGINX Proxy Manager and Cloudflare

    This isn’t strictly necessary, I just wanted my friends to be able to join via factorio.mydomain.com

    Assuming you have a preexisting Cloudflare account, domain registered that points to your IP address, and an NGINX instance:

    Create a CNAME record with the name being whatever you’d like (I chose factorio) and the target being @ which will point it to your domain, you should be able to leave proxy enabled.

    Next you’ll want to head over to your NGINX Proxy Manager instance and add a proxy host. Set the domain name to the domain you just created in cloudflare (e.g. factorio.mydomain.com). Set the forward hostname/IP to your LXC containers IP address, and finally set the port to 34197.

    As far as I’m aware, HTTPS and SSL aren’t necessary at all and don’t seem to make a difference, they could harden security but take that with a grain of salt as this is a Factorio server and I’m unsure, however the server still works fine with Block Common Exploits, Cache Assets and SSL enabled.

    The DNS changes in Cloudflare may take a while to propagate, but you and your friends should be able to join directly via the domain you setup rather than your public IP address.

    Mods/Saves

    Mods are shockingly easy. The method I used was to install mods on my Factorio client and simply copy the whole mods folder over to the server. You might achieve this with something like WinSCP, which will also work for uploading saves.

    NOTE: If your LXC container is unprivileged you may need to enable SSH/SFTP
    To enable SSH/SFTP:
    nano /etc/ssh/sshd_config
    – Add to the bottom of the file: PermitRootLogin yes and PasswordAuthentication yes
    – Exit and save then restart the services with systemctl restart ssh && systemctl restart sshd

    Your games mods and saves should be stored in %appdata%/Factorio, you can copy the entire mods folder (assuming it has all the mods you would like for your server) to the mods folder created earlier on the server in /opt/factorio/mods and they should just work.

    NOTE: If you copy a save to the server, the process will be the same of dragging it over using WinSCP into the created saves folder, however you may have to re run chown -R factorio:factorio /opt/factorio as stated earlier.

    Auto Start Using Systemd

    If you are still in the factorio user, you will want to change back to root using su

    The following will create a systemd service to start Factorio, and we will enable it to run at container startup.

    Create a file named factorio.service with nano /etc/systemd/system/factorio.service :

    [Unit]
    Description=Factorio Server
    StartLimitIntervalSec=600
    StartLimitBurst=5
    
    [Service]
    ExecStart=/opt/factorio/bin/x64/factorio --start-server-load-latest /opt/factorio/saves/SAVENAME.zip --server-settings /opt/factorio/data/server-settings.json #save location MAKE SURE TO CHANGE SAVENAME TO YOUR SAVES NAME and server settings location
    WorkingDirectory=/opt/factorio
    Restart=on-failure
    Type=simple
    User=factorio
    KillSignal=SIGINT
    
    [Install]
    WantedBy=multi-user.target
    INI

    NOTE: In the starting command above, –start-server has been changed to –start-server-load-latest, supposedly this allows the server to better select the latest save between autosaves and manual saves when starting.

    Save the above file and run systemctl daemon-reload
    To start the service and test it, run systemctl start factorio and systemctl status factorio, also try joining the server to verify.

    Once you have verified everything is working correctly, you can enable the server to startup with your LXC container using systemctl enable factorio
    Do note that any time you make changes to the above file you must re-run systemtl daemon-reload

    Updating/Update Script

    Now you will eventually need to update your server, especially with the frequency of patches around the release of Space Age. It’s fairly straightforward and just involves re-downloading and re-extracting the server files, it can be automated with a simple bash script too.

    To update the server run the following commands, starting in the root directory, if you’d like to get to the root directory simply type cd

    Assuming you setup a systemctl job to start the server, run: systemctl stop factorio
    Now re-grab the server file: wget-O ~/factorio_headless.tar.xz https://factorio.com/get-download/stable/headless/linux64 noting that ~/factorio_headless.tar.xz will be the location and name of the file
    cd /opt
    tar -xJf ~/factorio_headless.tar.xz
    systemctl start factorio
    And that’s it!

    To automate this, you can simply turn the above commands into a bash script, I made mine in the root directory
    nano factorio-update.sh
    Inside this file put the following:

    #! /usr/bin/bash
    systemctl stop factorio
    wget -O ~/factorio-space.tar.xz https://factorio.com/get-download/stable/headless/linux64
    cd /opt
    tar -xJf ~/factorio-space.tar.xz
    chown -R factorio:factorio /opt/factorio
    systemctl start factorio
    Bash

    You will have to make the file executable, first save it with ctrl+x and y
    And run: chmod u+x factorio-update.sh
    To execute this script, in the directory it’s in, run ./factorio-update.sh

    If you’d like to automate this further, you could make a cron job to run this every few hours or days

    Automate Updates with Cron

    Access crontab by typing crontab -e
    It may ask you what editor you would like to use, I chose nano with 1
    At the bottom of the cron file, enter 0 */48 * * * ~/factorio-update.sh NOTE: You can change out the start of this line to whatever you want, the above will run the script once every 48 hours
    ctrl+x and y to save, and the job should be ready.

    Conclusion

    You should now have your very own Factorio server up and running! Note as stated above, in my experience it was 50/50 whether the server could be joined from the public servers list or not, however it was always joinable via the domain setup.

    Cheers!