Prérequis
- Un serveur sous Ubuntu, Debian, AlmaLinux, Rocky, CentOS, Fedora ou openSUSE : voir nos VPS au Canada
- Une connexion SSH ouverte en root : Se connecter en SSH à votre VPS
- 10 Go de disque libre et 6 Go de RAM
Ce guide installe le serveur dédié Satisfactory avec SteamCMD, l'outil de Valve : un utilisateur dédié, le serveur (2,9 Go), le mot de passe administrateur posé depuis le terminal avant que le premier venu ne le fasse, la première partie, un service qui sauvegarde à l'arrêt et redémarre avec la machine, le pare-feu, la connexion depuis le jeu, la sauvegarde de nuit et la mise à jour. Le serveur se pilote par son API HTTPS ; un petit script la rend accessible en une commande. Choisissez votre système ci-dessus. Copiez la commande avec le bouton Copier, collez-la dans votre terminal, et comparez avec le résultat attendu.
1Mettre à jour le système et installer les bibliothèques 32 bits
SteamCMD, l'outil de Valve qui télécharge les serveurs de jeux, est un programme 32 bits : il lui faut les bibliothèques 32 bits du système. On met d'abord le système à jour ; sans cela, l'installation 32 bits échoue sur AlmaLinux, Rocky, CentOS et Fedora (conflit avec une bibliothèque plus ancienne), et SteamCMD ne peut pas joindre Steam sur une Fedora fraîche (certificats absents).
apt update && apt upgrade -y... 212 upgraded, 8 newly installed, 0 to remove and 4 not upgraded. (les nombres dépendent de l'âge de l'image)
dnf upgrade -y... Complete!
dnf upgrade -y... Complete!
zypper -n refresh && zypper -n updateAll repositories have been refreshed. ...
dpkg --add-architecture i386 && apt update && apt install -y lib32gcc-s1 lib32stdc++6 curldnf install -y glibc.i686 libstdc++.i686dnf install -y glibc.i686 libstdc++.i686zypper -n install glibc-32bit libstdc++6-32bitopenSUSE Leap 16 livre son noyau avec l'exécution des programmes 32 bits désactivée : sans cette étape, SteamCMD répond « Exec format error ». On l'active dans GRUB et on redémarre (reconnectez-vous en SSH après une minute).
sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="ia32_emulation=1 /' /etc/default/grub && grub2-mkconfig -o /boot/grub2/grub.cfg && rebootgrep -o ia32_emulation=1 /proc/cmdlineia32_emulation=1
2Créer un utilisateur dédié et installer SteamCMD
Le serveur ne tourne jamais en root : un utilisateur satisfactory, dont le dossier personnel est /opt/satisfactory, possède SteamCMD et le serveur. Le dossier est créé avant l'utilisateur : sur Rocky et CentOS, SELinux refuse à useradd de le créer lui-même sous /opt.
mkdir -p /opt/satisfactory/server /opt/satisfactory/steamcmd && useradd -r -s /bin/bash -d /opt/satisfactory satisfactory && chown -R satisfactory:satisfactory /opt/satisfactorysu - satisfactory -c "cd /opt/satisfactory/steamcmd && curl -fsSL https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz | tar zxf - && ls"linux32 steamcmd.sh
3Télécharger le serveur Satisfactory (2,9 Go)
SteamCMD se met à jour puis télécharge l'application 1690800, le serveur dédié Satisfactory. Comptez de deux à cinq minutes. Il arrive souvent que le premier passage se termine sur « ERROR! Failed to install app '1690800' (Missing configuration) » sans rien télécharger : relancez la même commande, elle passe.
su - satisfactory -c "/opt/satisfactory/steamcmd/steamcmd.sh +force_install_dir /opt/satisfactory/server +login anonymous +app_update 1690800 validate +quit"... Update state (0x81) verifying update, progress: 61.96 (1789100577 / 2887563617) Update state (0x0) unknown, progress: 0.00 (0 / 0) Success! App '1690800' fully installed.
du -sh /opt/satisfactory/server && ls -l /opt/satisfactory/server/FactoryServer.sh2.7G /opt/satisfactory/server -rwxrwxr-x 1 satisfactory satisfactory 427 Sep 21 14:35 /opt/satisfactory/server/FactoryServer.sh (2.9G sur Debian et CentOS ; les droits et la date varient)
4Choisir le mot de passe administrateur et écrire les scripts
Le mot de passe administrateur du serveur commande tout : il est lu par les scripts et par le jeu. Il est tapé une fois, sans s'afficher, et rangé dans un fichier que seul l'utilisateur satisfactory peut lire.
read -r -s -p 'Mot de passe administrateur du serveur : ' MDP; echo
echo "$MDP" > /opt/satisfactory/motdepasse && chmod 600 /opt/satisfactory/motdepasse && chown satisfactory:satisfactory /opt/satisfactory/motdepasseLe script de démarrage lance le serveur directement (le FactoryServer.sh livré passe par un shell intermédiaire, qui empêcherait l'arrêt propre). Le serveur écrit son journal dans FactoryGame/Saved/Logs/FactoryGame.log, sous le dossier du serveur.
cat > /opt/satisfactory/start.sh << 'EOF'
#!/bin/sh
cd /opt/satisfactory/server
exec ./Engine/Binaries/Linux/FactoryServer-Linux-Shipping FactoryGame -log -unattended
EOF
chmod +x /opt/satisfactory/start.sh && chown satisfactory:satisfactory /opt/satisfactory/start.shLe serveur se pilote par son API HTTPS sur le port 7777 (certificat auto-signé). Le script api.sh s'identifie avec le mot de passe, appelle une fonction et affiche la réponse ; il sert aux étapes 6, 8 et 9.
cat > /opt/satisfactory/api.sh << 'EOF'
#!/bin/bash
# api.sh <fonction> [donnees JSON] : appelle l'API du serveur avec le mot de passe administrateur
A=https://127.0.0.1:7777/api/v1
MDP=$(cat /opt/satisfactory/motdepasse)
TOK=$(curl -sk --max-time 10 -X POST $A -H 'Content-Type: application/json' -d "{\"function\":\"PasswordLogin\",\"data\":{\"minimumPrivilegeLevel\":\"Administrator\",\"password\":\"$MDP\"}}" | python3 -c 'import sys,json; print(json.load(sys.stdin)["data"]["authenticationToken"])')
curl -sk --max-time 60 -X POST $A -H 'Content-Type: application/json' -H "Authorization: Bearer $TOK" -d "{\"function\":\"$1\",\"data\":${2:-{\}}}"
echo
EOF
chmod +x /opt/satisfactory/api.sh && chown satisfactory:satisfactory /opt/satisfactory/api.shNi le signal d'arrêt ni la fonction Shutdown de l'API ne sauvegardent la partie : le script stop.sh demande d'abord une sauvegarde nommée arret, puis l'arrêt. Le service l'utilise à chaque arrêt, redémarrage compris.
cat > /opt/satisfactory/stop.sh << 'EOF'
#!/bin/sh
# Sauvegarde la partie, puis demande l'arret au serveur ; attend qu'il soit parti
/opt/satisfactory/api.sh SaveGame '{"saveName":"arret"}' > /dev/null
sleep 5
/opt/satisfactory/api.sh Shutdown > /dev/null
while kill -0 $MAINPID 2>/dev/null; do sleep 1; done
EOF
chmod +x /opt/satisfactory/stop.sh && chown satisfactory:satisfactory /opt/satisfactory/stop.sh5Démarrer le serveur comme un service
Le service lance start.sh avec l'utilisateur satisfactory, arrête par stop.sh, relance le serveur s'il plante et le démarre avec la machine.
cat > /etc/systemd/system/satisfactory.service << 'EOF'
[Unit]
Description=Serveur Satisfactory
After=network.target
[Service]
User=satisfactory
Group=satisfactory
WorkingDirectory=/opt/satisfactory/server
ExecStart=/opt/satisfactory/start.sh
ExecStop=/opt/satisfactory/stop.sh
TimeoutStopSec=90
Restart=on-failure
RestartSec=15
LimitNOFILE=100000
[Install]
WantedBy=multi-user.target
EOFsystemctl daemon-reload && systemctl enable --now satisfactoryCreated symlink /etc/systemd/system/multi-user.target.wants/satisfactory.service → /etc/systemd/system/satisfactory.service.
Le démarrage prend moins d'une minute : le journal annonce l'API sur le port 7777 et le canal de transfert sur le port 8888, tous deux en écoute.
systemctl is-active satisfactory && grep -E 'Server API listening|bound to port 8888' /opt/satisfactory/server/FactoryGame/Saved/Logs/FactoryGame.log | cut -c31- && ss -ltnp | grep -E ':(7777|8888) 'active
LogServer: Display: Server API listening on 0.0.0.0:7777
LogServer: Display: Server API listening on '0.0.0.0:7777' Dedicated Server
LogReliableMessaging: Server streaming socket bound to port 8888
LISTEN 0 16 0.0.0.0:7777 0.0.0.0:* users:(("FactoryServer-L",pid=2500,fd=19))
LISTEN 0 8 0.0.0.0:8888 0.0.0.0:* users:(("FactoryServer-L",pid=2500,fd=21))6Réclamer le serveur et créer la première partie
Un serveur neuf appartient au premier qui le réclame
Tant que personne ne l'a réclamé, le serveur accepte sans mot de passe le premier qui lui donne un nom et un mot de passe administrateur, depuis le jeu ou par l'API. Faites-le vous-même, maintenant, depuis le serveur : la réclamation ne se fait qu'une fois.
A=https://127.0.0.1:7777/api/v1
T=$(curl -sk -X POST $A -H 'Content-Type: application/json' -d '{"function":"PasswordlessLogin","data":{"minimumPrivilegeLevel":"InitialAdmin"}}' | python3 -c 'import sys,json; print(json.load(sys.stdin)["data"]["authenticationToken"])')
curl -sk -X POST $A -H 'Content-Type: application/json' -H "Authorization: Bearer $T" -d "{\"function\":\"ClaimServer\",\"data\":{\"serverName\":\"Mon serveur Satisfactory\",\"adminPassword\":\"$(cat /opt/satisfactory/motdepasse)\"}}" -o /dev/null -w 'HTTP %{http_code}\n'HTTP 200
su - satisfactory -c '/opt/satisfactory/api.sh QueryServerState'{"data":{"serverGameState":{"activeSessionName":"SERVER-SESSION1175119901","numConnectedPlayers":0,"playerLimit":4,"techTier":0,"activeSchematic":"None","gamePhase":"None","isGameRunning":false,"totalGameDuration":0,"isGamePaused":false,"averageTickRate":29.891225814819336,"autoLoadSessionName":"","agreeToCrashUploadRequested":false}}}
(isGameRunning false : aucune partie n'existe encore ; le numéro de session varie)La partie se crée par l'API (ou depuis le jeu) : un nom de session, le tutoriel de départ sauté, les règles par défaut. Le mot de passe joueurs, distinct du mot de passe administrateur, est demandé à chaque joueur qui rejoint ; changez joueurs.
su - satisfactory -c '/opt/satisfactory/api.sh CreateNewGame '"'"'{"newGameData":{"sessionName":"MaPartie","mapName":"","startingLocation":"","bSkipOnboarding":true,"GameModeSettings":{},"advancedGameSettings":{},"customOptionsOnlyForModding":{}}}'"'"''(aucune sortie : le monde se charge, une trentaine de secondes)
su - satisfactory -c '/opt/satisfactory/api.sh SetClientPassword '"'"'{"password":"joueurs"}'"'"''(aucune sortie)
su - satisfactory -c '/opt/satisfactory/api.sh QueryServerState'{"data":{"serverGameState":{"activeSessionName":"MaPartie","numConnectedPlayers":0,"playerLimit":4,"techTier":2,"activeSchematic":"None","gamePhase":"/Script/FactoryGame.FGGamePhase'/Game/FactoryGame/GamePhases/GP_Project_Assembly_Phase_0.GP_Project_Assembly_Phase_0'","isGameRunning":true,"totalGameDuration":22,"isGamePaused":false,"averageTickRate":29.908864974975586,"autoLoadSessionName":"MaPartie","agreeToCrashUploadRequested":false}}}
(isGameRunning true et autoLoadSessionName MaPartie : la partie tourne et sera rechargée à chaque démarrage)Le serveur prend 3,1 Go de mémoire une fois la partie chargée ; sans joueur, il met le jeu en pause. Il sauvegarde tout seul toutes les 5 minutes et à chaque départ de joueur, dans /opt/satisfactory/.config/Epic/FactoryGame/Saved/SaveGames/server.
7Ouvrir les ports dans le pare-feu
Le jeu et l'API partagent le port 7777, en TCP et en UDP ; le transfert des données de la partie passe par le port 8888 en TCP. La commande active le pare-feu du système en gardant SSH autorisé, puis ouvre les ports.
apt install -y ufw && ufw allow 22/tcp && ufw allow 7777/tcp && ufw allow 7777/udp && ufw allow 8888/tcp && ufw --force enable... Firewall is active and enabled on system startup
dnf install -y firewalld && systemctl enable --now firewalld && firewall-cmd --permanent --add-port=7777/tcp --add-port=7777/udp --add-port=8888/tcp && firewall-cmd --reload... success success
dnf install -y firewalld && systemctl enable --now firewalld && firewall-cmd --permanent --add-port=7777/tcp --add-port=7777/udp --add-port=8888/tcp && firewall-cmd --reload... success success
firewall-cmd --permanent --add-port=7777/tcp --add-port=7777/udp --add-port=8888/tcp && firewall-cmd --reloadsuccess success
Depuis une autre machine, l'API répond à travers le pare-feu (remplacez 203.0.113.42 par l'adresse de votre serveur) :
curl -sk --max-time 10 -X POST https://203.0.113.42:7777/api/v1 -H 'Content-Type: application/json' -d '{"function":"HealthCheck","data":{"clientCustomData":""}}'{"data":{"health":"healthy","serverCustomData":""}}8Se connecter au serveur
Le jeu vérifie le certificat du serveur à la première connexion et affiche son empreinte : comparez-la à celle du journal.
grep -o 'SHA256:[A-Za-z0-9+/=]*' /opt/satisfactory/server/FactoryGame/Saved/Logs/FactoryGame.log | head -1SHA256:t0IYcVklEMDVybe0JcJZer1OermlbRESIHh5X2pT6Sw= (la vôtre est différente : le certificat est créé sur votre serveur)
- Lancez Satisfactory, ouvrez le gestionnaire de serveurs (Server Manager) et ajoutez un serveur : l'adresse de votre serveur (203.0.113.42) et le port 7777.
- Le jeu affiche l'empreinte du certificat : acceptez-la si elle est celle du journal.
- Le mot de passe administrateur ouvre les réglages du serveur dans le jeu ; le mot de passe joueurs (étape 6) suffit pour rejoindre la partie.
9Commander le serveur depuis le terminal
Chaque fonction de l'API passe par api.sh, lancé en tant que satisfactory ; la réponse est en JSON, vide quand il n'y a rien à dire. Les données entre accolades se donnent entre apostrophes.
su - satisfactory -c "/opt/satisfactory/api.sh SaveGame '{\"saveName\":\"manuelle\"}' && sleep 5 && ls -l /opt/satisfactory/.config/Epic/FactoryGame/Saved/SaveGames/server/"total 1632 -rw-r--r-- 1 satisfactory satisfactory 833906 Sep 21 15:26 manuelle.sav -rw-r--r-- 1 satisfactory satisfactory 833886 Sep 21 15:25 MaPartie_autosave_0.sav
QueryServerStateSaveGame '{"saveName":"manuelle"}'EnumerateSessionsLoadGame '{"saveName":"manuelle","enableAdvancedGameSettings":false}'SetClientPassword '{"password":"joueurs"}'GetServerOptionsApplyServerOptions '{"updatedServerOptions":{"FG.AutosaveInterval":"600"}}'RunCommand '{"command":"FG.NetworkQuality"}'Shutdown10Gérer, sauvegarder et mettre à jour le serveur
systemctl status satisfactorytail -f /opt/satisfactory/server/FactoryGame/Saved/Logs/FactoryGame.logsystemctl restart satisfactorysystemctl stop satisfactorysystemctl stop satisfactory && ls -l /opt/satisfactory/.config/Epic/FactoryGame/Saved/SaveGames/server/arret.sav && systemctl start satisfactory-rw-r--r-- 1 satisfactory satisfactory 833904 Sep 21 15:26 /opt/satisfactory/.config/Epic/FactoryGame/Saved/SaveGames/server/arret.sav (au démarrage suivant, le serveur recharge la sauvegarde la plus récente de la session)
Le script ci-dessous demande une sauvegarde au serveur, puis archive toutes les sauvegardes ; chaque nuit, il conserve les 7 dernières archives.
cat > /opt/satisfactory/backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR=/opt/satisfactory/backups
DATE=$(date +%Y-%m-%d_%H-%M)
mkdir -p $BACKUP_DIR
/opt/satisfactory/api.sh SaveGame '{"saveName":"nuit"}' > /dev/null 2>&1
sleep 5
tar -czf $BACKUP_DIR/satisfactory-$DATE.tar.gz -C /opt/satisfactory .config/Epic/FactoryGame/Saved/SaveGames
cd $BACKUP_DIR && ls -t | tail -n +8 | xargs -r rm
EOF
chmod +x /opt/satisfactory/backup.sh && chown satisfactory:satisfactory /opt/satisfactory/backup.shsu - satisfactory -c '/opt/satisfactory/backup.sh && ls -la /opt/satisfactory/backups'-rw-rw-r-- 1 satisfactory satisfactory 1664732 Sep 21 15:03 satisfactory-2026-09-21_15-03.tar.gz (la taille grandit avec l'usine)
apt install -y cron && systemctl enable --now crondnf install -y cronie && systemctl enable --now cronddnf install -y cronie && systemctl enable --now crondzypper -n install cronie && systemctl enable --now cronsu - satisfactory -c '(crontab -l 2>/dev/null; echo "0 4 * * * /opt/satisfactory/backup.sh") | crontab - && crontab -l'0 4 * * * /opt/satisfactory/backup.sh
Après une mise à jour du jeu, les joueurs à jour ne peuvent plus entrer sur un serveur en retard : la mise à jour est la même commande que l'installation, serveur arrêté. Les sauvegardes, le mot de passe et les scripts sont hors du dossier du serveur et ne bougent pas.
systemctl stop satisfactory && su - satisfactory -c "/opt/satisfactory/steamcmd/steamcmd.sh +force_install_dir /opt/satisfactory/server +login anonymous +app_update 1690800 +quit" && systemctl start satisfactory... Success! App '1690800' already up to date. (« fully installed » quand une mise à jour a été téléchargée)
Erreurs fréquentes
L'image du serveur a été construite sur un autre disque et GRUB demande où se réinstaller : choisissez le disque de la machine (celui que lsblk affiche, en général /dev/sda) ; la mise à jour continue ensuite.
Un raté passager côté Steam : relancez la même commande, le téléchargement reprend.
Le système n'a pas été mis à jour avant : faites la première boîte de l'étape 1, puis réessayez.
Les programmes 32 bits sont désactivés dans le noyau : appliquez la dernière boîte de l'étape 1 et redémarrez.
Plusieurs serveurs de jeux tournent sur la même machine, chacun avec son utilisateur, et SteamCMD a déjà créé dix dossiers /tmp/dumps, /tmp/dumps01... /tmp/dumps09 qui appartiennent aux autres : lancez rm -rf /tmp/dumps* en root, puis relancez la commande.
Le serveur n'écoute pas encore : attendez la boîte de contrôle de l'étape 5 (« Server API listening »), puis relancez la réclamation.
Le serveur a déjà été réclamé : le mot de passe administrateur est celui du fichier /opt/satisfactory/motdepasse, et api.sh l'utilise. Pour repartir de zéro, arrêtez le service, supprimez /opt/satisfactory/.config/Epic/FactoryGame/Saved/SaveGames/ServerSettings.7777.sav, redémarrez et réclamez à nouveau.
Le mot de passe de /opt/satisfactory/motdepasse n'est pas celui du serveur : l'identification échoue et la fonction part sans jeton. Remettez le bon mot de passe dans le fichier, ou changez celui du serveur depuis le jeu.
La version 1.2 du serveur veut bSkipOnboarding et un bloc GameModeSettings, même vide : reprenez la boîte de l'étape 6 telle quelle.
Le serveur réécrit son dossier Saved/Config/LinuxServer à chaque démarrage et efface Game.ini : la limite reste à 4 joueurs par ce moyen. Les options qui se règlent le sont par ApplyServerOptions ou depuis le jeu.
Le monde reste chargé même en pause : c'est sa taille au repos. Sans partie créée, il en prend six fois moins (470 Mo).
Aucune partie n'a été créée, ou autoLoadSessionName est vide : créez la partie (étape 6) ou chargez une sauvegarde avec LoadGame.
Le serveur crée son propre certificat au premier démarrage. Comparez l'empreinte affichée à celle du journal (étape 8), puis acceptez.
Les deux jeux écoutent sur le port 7777 : le second à démarrer ne peut pas. Pour Satisfactory, ajoutez -Port=7778 à la fin de la commande de start.sh, mettez 7778 dans la ligne A= de api.sh (l'API suit le jeu sur ce port), ouvrez 7778 à la place de 7777, redémarrez : le serveur repart à réclamer sur ce port (étape 6), les sauvegardes restent. Sans le changement dans api.sh, l'arrêt attend 90 secondes puis tue le serveur sans sauvegarder.
Serveur prêt
Votre serveur Satisfactory est réclamé, démarre avec la machine, sauvegarde à l'arrêt et chaque nuit, et écoute sur les ports 7777 et 8888. La même base SteamCMD sert aux autres serveurs de jeux.