Factorio Headless Server Inside LXC Container
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 directorychown -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
ININOTE: 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 filecd /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 directorynano 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
BashYou 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 hoursctrl+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!