2024.04.27 - Factorio Headless Server

Intro

Prace nad HomeLab/3 postępują i kolejnym ważnym krokiem jest serwerek Factorio (pewnie nawet kilka) 😁

Przygotowanie VM

Postanowiłem zainstalować Factorio Headless Server (alias FHS) na dedykowanej maszynce. W PVE wyklikałem następującą VMkę:

pve - vm
pve - vm

Następnie przygotowałem maszynę z domowego playbooka Ansibla:

  • konfiguracja sshd (klucze)
  • instalacja pakietów

Instalacja serwera

Serwer będzie chodził bezpośrednio na maszynie (na razie bez dockera).

Przygotowanie katalogów, użytkownika i plików (wszystkie peracje z poziomu root):

# add user
$ adduser --disabled-login --no-create-home --gecos factorio factorio

# download server
$ cd /opt
$ wget -O factorio-headless-1.1.107.tar.xz https://www.factorio.com/get-download/1.1.107/headless/linux64
$ tar -xJf factorio-headless-1.1.107.tar.xz

# prepare folders
$ cd factorio
$ mkdir saves mods

# create config
$ cp -a server-settings.example.json server-settings.json

# set permissions
$ chown -R factorio:factorio /opt/factorio

Następnie poprawiłem konfigurację serwera i ustawiłem grę prywatną z hasełkiem.

$ mcedit /opt/factorio/data/server-settings.json

---
{
  "name": "HomeLab/3/Factorio",
  "description": "Vanilla",
  "tags": ["homelab", "nerdoza"],
  "visibility":
  {
    "public": false,
    "lan": true
  },
  "game_password": "<tajne-przez-poufne>",
  "<inne ustawienia>": "(...)"
}

Dodatkowo utworzyłem listę adminów serwera.

$ mcedit /opt/factorio/server-adminlist.json

---
[ "kim-jestes?-jestem-adminem!" ]

Przygotowanie serwisów

Serwer będzie odpalany przez systemd. W przypadku awarii ma zostać wysłane powiadomienie na prywatną instancję ntfy.

"Główny" serwis FHS (gist)

Podczas awarii zostanie wyzwolony serwis ntfy@.service z nazwą aktualnego unitu %n. (docs)

ExecStopPost= powoduje 'fail' serwisu nawet w przypadku ręcznego zatrzymania i wysłanie powiadomienia na ntfy.
Później™ rozszerzę powiadomenia o zdarzeniach uruchomienia/zatrzymania serwera.

$ mcedit /etc/systemd/system/factorio.service

---
[Unit]
Description=Factorio Headless Server
After=network.target
OnFailure=ntfy@%n.service

[Service]
Type=simple
User=factorio
WorkingDirectory=/opt/factorio
ExecStart=/opt/factorio/bin/x64/factorio --start-server-load-latest --server-settings ./data/server-settings.json
ExecStopPost=/bin/false
Restart=on-failure
RestartSec=60s

[Install]
WantedBy=multi-user.target

Serwis do powiadomień ntfy (gist)

Przekazujemy orginalną nazwę unitu w nazwie instancji %i (docs)

$ mcedit /etc/systemd/system/ntfy@.service

---
[Unit]
Description=Notify about service failure

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/ntfy.sh %i

[Install]
WantedBy=multi-user.target

Skrypt notyfikujący ntfy (gist)

$ mcedit /usr/local/sbin/ntfy.sh

---
#!/bin/bash

# Get input
SERVICE_NAME=${1:-service_unknown}

# Local variables
NTFY_URL="https://ntfy.home/"
NTFY_AUTH="Basic <auth>"
NTFY_TOPIC=homelab

# Get status
STATUS=$(systemctl is-active $SERVICE_NAME)

# Compute message
MESSAGE=$(jq -cn '{"topic": $topic, "message": $message}' \
    --arg topic "$NTFY_TOPIC" \
    --arg message "Service $SERVICE_NAME is ${STATUS:-status_unknown}")

# Send notification
curl -qk --connect-timeout 5 \
    -X POST $NTFY_URL \
    -H "Authorization: $NTFY_AUTH" \
    -H "X-Tags: rotating_light" \
    -H "X-Title: $SERVICE_NAME" \
    -H "X-Priority: 4" \
    -H "Content-Type: application/json" \
    -d "$MESSAGE"

Uruchomienie serwera

Za pierwszym razem trzeba wygenerować mapę do gry. Nie bawiłem się w żadne customizacje generatora więc komenda wygląda prosto

$ cd /opt/factorio/saves
$ sudo -u factorio /opt/factorio/bin/x64/factorio --create map01.zip

Odpalenie serwera sprowadza się kilku komend systemctl

# enable service
$ systemctl enable factorio.service

# start service
$ systemctl start factorio.service

# check status
$ systemctl status factorio.service
● factorio.service - Factorio Headless Server
     Loaded: loaded (/etc/systemd/system/factorio.service; enabled; preset: enabled)
     Active: active (running) since Sat 2024-04-27 20:14:04 CEST; 46min ago
   Main PID: 11889 (factorio)
      Tasks: 13 (limit: 2303)
     Memory: 206.8M
        CPU: 15.110s
     CGroup: /system.slice/factorio.service
             └─11889 /opt/factorio/bin/x64/factorio --start-server-load-latest --server-settings ./data/server-settings.json

Restart serwera powoduje wysłanie powiadomienia na telefon.

Linki