Désactiver le préchargeur
Tutoriels Linux Serveurs de jeux

Installer un serveur Satisfactory sur Linux (Ubuntu, Debian, AlmaLinux)

Documentation / Tutoriels Linux / Serveurs de jeux / Installer un serveur Satisfactory sur Linux (Ubuntu, Debian, AlmaLinux)
Mis à jour le 21/09/2026
9 vues
Intermédiaire
30 minutes

Prérequis

Votre serveur :

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).

Mettre à jour le système
apt update && apt upgrade -y
Résultat attendu
...
212 upgraded, 8 newly installed, 0 to remove and 4 not upgraded.
(les nombres dépendent de l'âge de l'image)
Mettre à jour le système
dnf upgrade -y
Résultat attendu
...
Complete!
Mettre à jour le système
dnf upgrade -y
Résultat attendu
...
Complete!
Mettre à jour le système
zypper -n refresh && zypper -n update
Résultat attendu
All repositories have been refreshed.
...
Installer les bibliothèques 32 bits et curl
dpkg --add-architecture i386 && apt update && apt install -y lib32gcc-s1 lib32stdc++6 curl
Installer les bibliothèques 32 bits
dnf install -y glibc.i686 libstdc++.i686
Installer les bibliothèques 32 bits
dnf install -y glibc.i686 libstdc++.i686
Installer les bibliothèques 32 bits
zypper -n install glibc-32bit libstdc++6-32bit

openSUSE 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).

Activer les programmes 32 bits dans le noyau, puis redémarrer
sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="ia32_emulation=1 /' /etc/default/grub && grub2-mkconfig -o /boot/grub2/grub.cfg && reboot
Après le redémarrage : vérifier que l'option est active
grep -o ia32_emulation=1 /proc/cmdline
Résultat attendu
ia32_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.

Créer les dossiers et l'utilisateur satisfactory
mkdir -p /opt/satisfactory/server /opt/satisfactory/steamcmd && useradd -r -s /bin/bash -d /opt/satisfactory satisfactory && chown -R satisfactory:satisfactory /opt/satisfactory
Télécharger SteamCMD (en tant que satisfactory)
su - satisfactory -c "cd /opt/satisfactory/steamcmd && curl -fsSL https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz | tar zxf - && ls"
Résultat attendu
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.

Installer le serveur Satisfactory
su - satisfactory -c "/opt/satisfactory/steamcmd/steamcmd.sh +force_install_dir /opt/satisfactory/server +login anonymous +app_update 1690800 validate +quit"
Résultat attendu
... Update state (0x81) verifying update, progress: 61.96 (1789100577 / 2887563617)
 Update state (0x0) unknown, progress: 0.00 (0 / 0)
Success! App '1690800' fully installed.
Vérifier la taille et la présence du lanceur
du -sh /opt/satisfactory/server && ls -l /opt/satisfactory/server/FactoryServer.sh
Résultat attendu
2.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.

Choisir le mot de passe administrateur (tapez-le, puis Entrée)
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/motdepasse

Le 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.

Créer start.sh
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.sh

Le 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.

Créer api.sh
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.sh

Ni 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.

Créer stop.sh
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.sh

5Dé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.

Créer le service satisfactory
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
EOF
Activer et démarrer le service
systemctl daemon-reload && systemctl enable --now satisfactory
Résultat attendu
Created 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.

Une minute plus tard : le serveur est démarré et é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) '
Résultat attendu
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.

Réclamer le serveur avec votre mot de passe administrateur
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'
Résultat attendu
HTTP 200
Vérifier : le serveur répond au mot de passe, sans partie pour l'instant
su - satisfactory -c '/opt/satisfactory/api.sh QueryServerState'
Résultat attendu
{"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.

Créer la partie MaPartie
su - satisfactory -c '/opt/satisfactory/api.sh CreateNewGame '"'"'{"newGameData":{"sessionName":"MaPartie","mapName":"","startingLocation":"","bSkipOnboarding":true,"GameModeSettings":{},"advancedGameSettings":{},"customOptionsOnlyForModding":{}}}'"'"''
Résultat attendu
(aucune sortie : le monde se charge, une trentaine de secondes)
Poser le mot de passe demandé aux joueurs
su - satisfactory -c '/opt/satisfactory/api.sh SetClientPassword '"'"'{"password":"joueurs"}'"'"''
Résultat attendu
(aucune sortie)
Une minute plus tard : la partie tourne
su - satisfactory -c '/opt/satisfactory/api.sh QueryServerState'
Résultat attendu
{"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.

Activer le pare-feu (SSH reste autorisé) et ouvrir les ports du jeu
apt install -y ufw && ufw allow 22/tcp && ufw allow 7777/tcp && ufw allow 7777/udp && ufw allow 8888/tcp && ufw --force enable
Résultat attendu
...
Firewall is active and enabled on system startup
Activer le pare-feu (SSH reste autorisé) et ouvrir les ports du jeu
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
Résultat attendu
...
success
success
Activer le pare-feu (SSH reste autorisé) et ouvrir les ports du jeu
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
Résultat attendu
...
success
success
Ouvrir les ports du jeu (firewalld est déjà actif sur openSUSE)
firewall-cmd --permanent --add-port=7777/tcp --add-port=7777/udp --add-port=8888/tcp && firewall-cmd --reload
Résultat attendu
success
success

Depuis une autre machine, l'API répond à travers le pare-feu (remplacez 203.0.113.42 par l'adresse de votre serveur) :

Interroger le serveur depuis une autre machine
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":""}}'
Résultat attendu
{"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.

Lire l'empreinte du certificat du serveur
grep -o 'SHA256:[A-Za-z0-9+/=]*' /opt/satisfactory/server/FactoryGame/Saved/Logs/FactoryGame.log | head -1
Résultat attendu
SHA256:t0IYcVklEMDVybe0JcJZer1OermlbRESIHh5X2pT6Sw=
(la vôtre est différente : le certificat est créé sur votre serveur)
  1. 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.
  2. Le jeu affiche l'empreinte du certificat : acceptez-la si elle est celle du journal.
  3. 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.

Sauvegarder la partie tout de suite, puis lister les sauvegardes
su - satisfactory -c "/opt/satisfactory/api.sh SaveGame '{\"saveName\":\"manuelle\"}' && sleep 5 && ls -l /opt/satisfactory/.config/Epic/FactoryGame/Saved/SaveGames/server/"
Résultat attendu
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
L'état : partie en cours, joueurs connectés, pause
QueryServerState
Sauvegarde la partie sous ce nom
SaveGame '{"saveName":"manuelle"}'
Les sessions et leurs sauvegardes, avec date et durée de jeu
EnumerateSessions
Charge une sauvegarde (le serveur y passe en une trentaine de secondes)
LoadGame '{"saveName":"manuelle","enableAdvancedGameSettings":false}'
Change le mot de passe demandé aux joueurs (vide : plus de mot de passe)
SetClientPassword '{"password":"joueurs"}'
Les options : FG.AutosaveInterval (300 s), FG.DSAutoPause, FG.DSAutoSaveOnDisconnect
GetServerOptions
Change une option
ApplyServerOptions '{"updatedServerOptions":{"FG.AutosaveInterval":"600"}}'
Une commande de la console du serveur
RunCommand '{"command":"FG.NetworkQuality"}'
Arrête le serveur sans sauvegarder (systemctl stop satisfactory sauvegarde d'abord)
Shutdown

10Gérer, sauvegarder et mettre à jour le serveur

Voir l'état du service
systemctl status satisfactory
Suivre le journal du serveur en direct (Ctrl+C pour sortir)
tail -f /opt/satisfactory/server/FactoryGame/Saved/Logs/FactoryGame.log
Redémarrer
systemctl restart satisfactory
Arrêter
systemctl stop satisfactory
Vérifier que l'arrêt a sauvegardé (une dizaine de secondes)
systemctl stop satisfactory && ls -l /opt/satisfactory/.config/Epic/FactoryGame/Saved/SaveGames/server/arret.sav && systemctl start satisfactory
Résultat attendu
-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.

Créer le script de sauvegarde
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.sh
Tester une sauvegarde tout de suite
su - satisfactory -c '/opt/satisfactory/backup.sh && ls -la /opt/satisfactory/backups'
Résultat attendu
-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)
Installer et démarrer cron
apt install -y cron && systemctl enable --now cron
Installer et démarrer cron
dnf install -y cronie && systemctl enable --now crond
Installer et démarrer cron
dnf install -y cronie && systemctl enable --now crond
Installer et démarrer cron
zypper -n install cronie && systemctl enable --now cron
Programmer la sauvegarde chaque nuit à 4 h
su - satisfactory -c '(crontab -l 2>/dev/null; echo "0 4 * * * /opt/satisfactory/backup.sh") | crontab - && crontab -l'
Résultat attendu
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.

Mettre à jour le serveur (serveur arrêté, puis relancé)
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
Résultat attendu
... Success! App '1690800' already up to date.
(« fully installed » quand une mise à jour a été téléchargée)

Erreurs fréquentes

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.

💬 Écrivez-nous sur Messenger !