Pourquoi Linux domine le monde des serveurs
Entrez dans n’importe quel centre de données, ouvrez n’importe quelle console cloud ou docker exec dans n’importe quel container, et vous serez presque certainement sur un noyau Linux. Il propulse la majorité des serveurs web, chaque téléphone Android, la plupart des routeurs et l’immense majorité des instances cloud. Les raisons sont anciennes et immuables : c’est gratuit, cela fonctionne sur presque n’importe quel matériel, c’est assez stable pour rester en ligne pendant des années, et tout peut y être automatisé via des scripts.
Pour un développeur, l’enjeu pratique est plus précis. Dès l’instant où vous déployez, vous cessez d’être une personne qui édite des fichiers dans un éditeur pour devenir quelqu’un qui tape des commandes dans un shell, souvent via SSH, sur une machine que vous ne voyez pas. Les compétences abordées dans ce guide sont celles qui rendent cette machine lisible plutôt qu’effrayante.
Vous n’avez pas besoin de devenir un administrateur système. Vous avez simplement besoin d’une aisance suffisante pour vous déplacer, lire des logs, corriger des permissions, redémarrer un service et comprendre quel processus écoute sur un port.
WSL et macOS : vous en savez déjà plus que vous ne le pensez
Si vous utilisez macOS, vous êtes déjà sur un système Unix. Le terminal, la structure du système de fichiers et la plupart des commandes — ls, cd, grep, chmod, ssh — fonctionnent de la même manière. Les différences se situent principalement au niveau du gestionnaire de paquets et de quelques options (flags) BSD versus GNU.
Sur Windows, WSL 2 vous offre un véritable noyau Linux à l’intérieur d’une machine virtuelle légère. wsl --install vous permet d’installer Ubuntu avec un shell, et vos fichiers sont accessibles des deux côtés. C’est la voie recommandée car c’est le même environnement dans lequel votre code sera exécuté.
Même si vous n’installez jamais Linux directement, ce modèle mental s’applique parfaitement aux conteneurs, qui ne sont en réalité que des processus Linux userland portant un costume très convaincant.
Le système de fichiers est un arbre unique
Contrairement à Windows, où les lecteurs sont des racines séparées, Linux possède un arbre unique dont la racine est /. Chaque disque, partage réseau et périphérique est monté quelque part à l’intérieur de celui-ci. Quelques répertoires ont des significations conventionnelles, et les connaître vous permet de toujours savoir où chercher.
/ the root of everything
├── bin essential user binaries (ls, cp, bash)
├── etc system-wide configuration files
├── home per-user home directories
├── opt optional third-party software
├── proc kernel and process state, as files
├── root the root user's home
├── tmp temporary files, cleared on reboot
├── usr installed programs and libraries
│ ├── bin local binaries
│ └── lib shared libraries
└── var variable data: logs, caches, databases
├── log system and application logs
└── www web content on Debian systems
Les deux que vous visiterez le plus sont /etc pour la configuration et /var/log pour les logs. Quand quelque chose ne va pas, ce sont les premiers endroits à vérifier. Lorsque vous installez vous-même une application, /opt ou /usr/local permet de ne pas interférer avec le gestionnaire de paquets.
Les chemins sont soit absolus (commencent par /, comme /etc/hosts), soit relatifs (résolus à partir de votre répertoire actuel). ~ est un raccourci pour votre répertoire personnel, . désigne le répertoire actuel et .. son répertoire parent. Ces trois symboles apparaissent dans presque toutes les commandes que vous saisirez.
Tout est un fichier
L’idée fondamentale d’Unix est que presque tout est représenté sous forme de fichier. Un disque dur est /dev/sda. Un terminal est /dev/pts/0. Les informations sur les processus en cours se trouvent sous /proc/<pid>/. Les nombres aléatoires proviennent de /dev/urandom. Un socket de domaine Unix est également un fichier.
C’est cette uniformité qui rend le shell si puissant. Les mêmes cat, grep, less et opérateurs de redirection fonctionnent sur un fichier de log, la carte mémoire d’un processus ou un périphérique, car ils ne sont tous que des flux d’octets derrière un chemin. Une fois que l’on a compris cela, /proc et /sys cessent de ressembler à de la magie et deviennent inspectables.
cat /proc/cpuinfo | grep 'model name' | head -1
ls -l /dev/null # the bit bucket
Navigation et commandes de fichiers pour un usage quotidien
Le shell fonctionne comme une boucle : il lit une ligne, l’étend, trouve le programme correspondant et l’exécute. Six commandes couvrent la majeure partie de la navigation.
pwd # print working directory
ls -lah # list, long format, human sizes, hidden files
cd /var/log # change directory
cd - # back to the previous directory
cd ~ # home
tree -L 2 # a readable tree, two levels deep
La création, la copie, le déplacement et la suppression de fichiers reposent sur un vocabulaire tout aussi restreint. mkdir -p crée les dossiers parents si nécessaire. cp -r copie les répertoires de manière récursive. mv permet de renommer des fichiers au sein d’un système de fichiers ou de les déplacer d’un système à un autre. rm -rf supprime tout un arbre de fichiers, c’est pourquoi cette commande impose le respect : il n’y a pas de corbeille en ligne de commande.
mkdir -p app/{src,test,docs}
cp -r app app.bak
mv notes.txt docs/notes.txt
rm -rf build/
Deux habitudes vous permettront de rester en sécurité. Utilisez ls ou find pour confirmer les fichiers correspondant à un glob avant de lancer rm, et préférez mv vers un répertoire de sauvegarde plutôt que de supprimer un élément dont vous n’êtes pas certain. L’historique du shell n’est pas un filet de sécurité une fois que vous avez appuyé sur Enter.
Lire des fichiers sans ouvrir d’éditeur
Vous avez rarement besoin d’un éditeur complet pour inspecter un élément sur un serveur. Ces outils affichent uniquement la partie qui vous intéresse.
cat /etc/os-release # print the whole file
less /var/log/syslog # page through, / to search, q to quit
head -n 20 access.log # first 20 lines
tail -n 50 access.log # last 50 lines
tail -f app.log # follow a live log
wc -l access.log # count lines
tail -f est la commande la plus utile lors d’un incident : elle diffuse les nouvelles lignes au fur et à mesure qu’elles sont écrites. Appuyez sur Ctrl+C pour arrêter. less est plus rapide qu’un éditeur et ne modifie jamais accidentellement le fichier, ce qui est crucial en production.
Rechercher avec find et grep
find parcourt le système de fichiers et effectue des correspondances sur les métadonnées ; grep recherche à l’intérieur du contenu des fichiers. Ensemble, ils répondent à la plupart des questions du type « où se trouve cet élément ? ».
find /etc -name '*.conf' # by name
find . -type d -name node_modules -prune -o -type f -name '*.ts' -print
find /var/log -type f -mtime -1 # modified in the last day
find . -type f -size +50M # larger than 50 MB
find . -type f -name '*.tmp' -delete # delete matches
grep -RIn 'TODO' src/ # recursive, line numbers, ignore binary
grep -c 'ERROR' app.log # count matches
grep -E 'timeout|refused' app.log # extended regex
grep -v '^#' config.ini # invert: drop comments
Le fait que grep ne retourne rien est un succès et non une erreur, mais il quitte avec le statut 1. Cela peut poser problème pour les scripts sous set -e ; ajoutez || true lorsqu’un résultat vide est acceptable.
Permissions : rwx et la notation octale
Chaque fichier possède un propriétaire, un groupe et trois bits de permission pour chacun : lecture (r), écriture (w) et exécution (x). ls -l les affiche sous la forme d’une chaîne de dix caractères.
-rwxr-xr-- 1 deploy www-data 512 Sep 16 10:00 deploy.sh
L’octal est la notation raccourcie. Chaque triplet est la somme de 4 (lecture), 2 (écriture) et 1 (exécution), ainsi rwx vaut 7, r-x vaut 5 et r-- vaut 4. Par conséquent, 755 signifie rwxr-xr-x et 644 signifie rw-r--r--.
chmod 755 deploy.sh # scripts and directories
chmod 644 index.html # regular files
chmod 600 .env # secrets only the owner can read
chmod u+x,g-w,o-rwx script # symbolic form, easier to review
Les répertoires ont besoin du bit d’exécution pour être parcourus, c’est pourquoi le retrait de x d’un répertoire masque son contenu même si la lecture est toujours activée. C’est une source subtile d’erreurs “permission denied” alors que les droits de lecture semblent corrects.
Propriété, groupes et umask
chown modifie le propriétaire et le groupe ; chgrp modifie uniquement le groupe. Utilisez le préfixe sudo car seul l’utilisateur root peut céder la propriété de fichiers.
sudo chown deploy:www-data /var/www/app
sudo chgrp -R www-data /var/www/app/uploads
Les nouveaux fichiers héritent de leurs permissions via le umask, un masque soustrait aux valeurs par défaut. Un umask de 022 donne 644 pour les fichiers et 755 pour les répertoires ; 027 donne 640 et 750. Configurez-le dans le profil de votre shell ou dans le fichier d’unité d’un service lorsqu’un défaut plus restrictif est nécessaire.
umask # show current, e.g. 0022
umask 027 # stricter for this shell
sudo et l’habitude du moindre privilège
sudo exécute une commande en tant qu’autre utilisateur, root par défaut, après avoir vérifié /etc/sudoers. Son fonctionnement est délibérément restreint : l’idée est d’accorder les commandes spécifiques dont une personne ou un service a besoin, plutôt que de fournir un shell root. Privilégiez sudo -u www-data <cmd> plutôt que de modifier des fichiers en tant que root, ce qui laisserait root comme propriétaire de ces fichiers.
sudo systemctl restart my-api
sudo -u postgres psql -c '\l'
sudo -l # what am I allowed to run?
Une règle d’or : root possède la configuration du système, un utilisateur de service dédié possède les fichiers de l’application, et votre application ne s’exécute jamais en tant que root. Si une compromission de votre application permettait à un attaquant d’obtenir les droits root sur l’hôte, c’est que vos permissions sont inutiles.
Utilisateurs et groupes
Les utilisateurs sont identifiés par un nom et un uid numérique ; les groupes par un nom et un gid. Le mappage se trouve dans /etc/passwd et /etc/group, tandis que les mots de passe hachés sont stockés dans /etc/shadow, lisible uniquement par root.
whoami # current user
id # uid, gid and groups
groups deploy # groups a user belongs to
sudo adduser appuser # create a user (Debian/Ubuntu)
sudo usermod -aG docker appuser # add to a group
sudo userdel -r appuser # remove the user and home
Les comptes de service n’ont généralement ni mot de passe ni shell de connexion. C’est intentionnel : ils existent pour posséder des fichiers et exécuter un processus unique, et non pour être utilisés pour une session utilisateur.
Processus : ps, top et signaux
Chaque programme en cours d’exécution est un processus avec un pid, un propriétaire et un parent. ps prend un instantané ; top et htop se mettent à jour en temps réel.
ps aux | grep node # find a process
ps -eo pid,ppid,user,%cpu,%mem,cmd --sort=-%cpu | head
top -o %MEM # interactive, sort by memory
pgrep -af node # pids and full command lines
Les processus communiquent via des signaux. SIGTERM (15) demande à un processus de s’arrêter et est le signal par défaut de kill ; SIGINT (2) correspond à Ctrl+C ; SIGHUP (1) signifie traditionnellement un rechargement ; SIGKILL (9) ne peut pas être intercepté et doit être utilisé en dernier recours.
kill 1234 # SIGTERM, graceful
kill -HUP 1234 # reload config
kill -9 1234 # force, no cleanup
pkill -f 'node server.js'
Envoyez SIGTERM en premier et laissez quelques secondes au processus pour fermer les connexions et vider l’état. Ne passez à -9 que si le processus est réellement bloqué, car cela ignore toutes les étapes de nettoyage prévues par votre application.
Jobs d’arrière-plan, & et nohup
L’ajout de & permet d’exécuter une commande en arrière-plan ; jobs, fg et bg permettent de la gérer depuis le shell actuel. Cependant, un job d’arrière-plan s’arrête toujours lorsque vous fermez le terminal, à moins de le détacher du signal de déconnexion (hangup) avec nohup ou d’utiliser un multiplexeur de terminal.
long-task & # background in this shell
jobs -l # list background jobs
fg %1 # bring job 1 to the foreground
nohup ./worker.sh > worker.log 2>&1 &
disown # detach from the shell's job table
Pour tout processus devant s’exécuter sur le long terme, utilisez tmux ou screen. Ils maintiennent une session active malgré les déconnexions, ce qui est précieux lorsqu’un déploiement prend plus de temps que votre connexion SSH.
tmux new -s deploy
# Ctrl+B, then D to detach
tmux attach -t deploy
services systemd avec systemctl
Sur les distributions modernes, systemd est le système d’init qui démarre les services au boot et les supervise. Vous définissez un service dans un fichier d’unité sous /etc/systemd/system/.
# /etc/systemd/system/my-api.service
[Unit]
Description=My API
After=network.target
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/my-api
Environment=NODE_ENV=production
EnvironmentFile=/opt/my-api/.env
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=3
[Install]
WantedBy=multi-user.target
Après avoir créé ou modifié une unité, rechargez le gestionnaire et activez le service pour qu’il se lance au démarrage.
sudo systemctl daemon-reload
sudo systemctl enable --now my-api
systemctl status my-api --no-pager
sudo systemctl restart my-api
sudo systemctl reload my-api # if the service supports it
Restart=on-failure est ce qui transforme un processus planté en un service capable de s’auto-réparer. EnvironmentFile permet de garder les secrets hors de l’unité, laquelle est souvent lisible par tous. Utilisez Type=notify uniquement si le programme supporte réellement le protocole de notification ; simple est le choix par défaut sécurisé.
Logs avec journalctl
systemd capture le stdout et le stderr d’un service dans le journal. journalctl permet de l’interroger, et vous l’utiliserez constamment.
journalctl -u my-api # all logs for one unit
journalctl -u my-api -n 100 # last 100 lines
journalctl -u my-api -f # follow live
journalctl -u my-api --since today
journalctl -u my-api --since '10 min ago' -p err
journalctl -b -p warning # this boot, warnings and worse
journalctl --disk-usage
sudo journalctl --vacuum-time=7d # keep a week
Le flag -p filtre par priorité, de emerg à debug. Suivre une unité avec -f pendant que vous appelez un endpoint est le moyen le plus rapide de comprendre l’origine d’une erreur 500.
Variables d’environnement et PATH
Les variables d’environnement sont des chaînes de caractères sous forme de paires clé-valeur qu’un processus hérite de son parent. PATH est la plus importante : elle liste, dans l’ordre, les répertoires dans lesquels le shell recherche le nom d’une commande.
echo "$PATH" | tr ':' '\n'
export NODE_ENV=production
export PATH="$HOME/.local/bin:$PATH"
printenv DATABASE_URL
Comme un service n’hérite pas de votre shell interactif, définissez les variables dans son fichier d’unité ou dans un EnvironmentFile. Si une variable fonctionne dans votre terminal mais pas sous systemd, c’est presque toujours la cause du problème. Pour rendre vos propres paramètres persistants, ajoutez les lignes export à ~/.bashrc ou ~/.profile.
Gestionnaires de paquets : apt, dnf, apk
Chaque famille de distribution possède son propre gestionnaire de paquets, mais les commandes sont similaires : mettre à jour l’index, installer, mettre à jour, supprimer, rechercher.
# Debian / Ubuntu
sudo apt update && sudo apt upgrade -y
sudo apt install nginx
sudo apt remove --purge nginx
# Fedora / RHEL
sudo dnf install nginx
sudo dnf upgrade --refresh
# Alpine (common in containers)
sudo apk add nginx
sudo apk upgrade
Installez vos paquets uniquement à partir des dépôts officiels dans la mesure du possible ; ils sont signés et corrigés. Utilisez un PPA ou un dépôt tiers de manière réfléchie, et fixez les versions dans vos images de production pour éviter qu’une reconstruction ne modifie silencieusement les composants exécutés.
Pipes, redirection et petits scripts
Un pipe envoie la sortie d’un programme vers l’entrée d’un autre. La redirection envoie cette sortie vers un fichier ou la lit depuis celui-ci. Ensemble, ils transforment de petits outils en véritables pipelines.
ls -l | wc -l # count files
grep ' 500 ' access.log | wc -l # count 500s
cmd > out.txt # stdout to file, overwrite
cmd >> out.txt # append
cmd 2> err.txt # stderr to file
cmd > all.txt 2>&1 # both streams together
cmd < input.txt # read stdin from a file
Chaque processus démarre avec trois descripteurs de fichiers : stdin (0), stdout (1) et stderr (2). 2>&1 signifie « envoyer stderr là où stdout est dirigé », et l’ordre est important — cela doit impérativement venir après la redirection de stdout.
Encapsulez une séquence dans un script et vous obtenez de l’automatisation. Deux lignes en haut de fichier rendent vos scripts bien plus sûrs :
#!/usr/bin/env bash
set -euo pipefail
-e arrête l’exécution à la première commande échouée, -u génère une erreur en cas de variable non définie, et -o pipefail fait échouer un pipeline si l’une de ses étapes échoue. Ensemble, ils transforment les échecs partiels silencieux en erreurs explicites, ce qui est précisément ce que l’on recherche dans un script de déploiement.
SSH et clés
SSH est le protocole utilisé pour accéder à une machine distante. Les connexions par mot de passe fonctionnent, mais les clés sont plus sécurisées et permettent l’automatisation via des scripts. Vous gardez une clé privée secrète et placez la clé publique correspondante sur le serveur.
ssh-keygen -t ed25519 -C "you@laptop" # creates ~/.ssh/id_ed25519(.pub)
ssh-copy-id [email protected] # install the public key
ssh [email protected] # log in
Un alias d’hôte dans ~/.ssh/config permet d’éviter les saisies répétitives et de fixer des options utiles :
Host prod
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 30
Ensuite, ssh prod établit la connexion. Une fois que vous faites confiance aux clés, désactivez l’authentification par mot de passe dans /etc/ssh/sshd_config (PasswordAuthentication no) et rechargez sshd. scp et rsync utilisent les mêmes clés pour copier des fichiers ; rsync -avz --delete est la méthode habituelle pour synchroniser un répertoire.
rsync -avz --exclude node_modules ./app/ prod:/opt/app/
ssh prod 'sudo systemctl restart my-api'
Disque et mémoire : df, du, free
Les serveurs tombent rarement en panne à cause du CPU ; ils plantent parce qu’un disque est saturé ou que la mémoire est épuisée. Ces commandes permettent de répondre aux questions : « combien reste-t-il d’espace ? » et « qu’est-ce qui l’utilise ? ».
df -h # free space per filesystem
df -i # inode usage — a disk can be full of inodes
du -sh /var/log/* # size per entry
du -h --max-depth=1 / | sort -h
free -h # memory and swap
ls -lhS /var/log | head # biggest log files
Lorsque /var est plein, la cause est presque toujours liée aux logs, au cache d’un package ou à une base de données qui s’emballe. Nettoyez de manière réfléchie — journalctl --vacuum-time, apt clean, rotation des logs — plutôt que de supprimer des fichiers dont une application a besoin.
Réseau : ss, curl, dig
ss affiche les sockets et les ports en écoute, remplaçant l’ancien netstat. Il permet de répondre à la question : « Quelque chose écoute-t-il, et qui est connecté ? ».
ss -tulpn # TCP, UDP, listening, process names
curl -I https://example.com # response headers only
curl -sS localhost:3000/health
curl -X POST -H 'content-type: application/json' \
-d '{"name":"ada"}' localhost:3000/users
dig example.com +short # DNS resolution
dig @1.1.1.1 example.com MX
curl -I est le moyen le plus rapide de confirmer qu’un serveur répond et de voir quel statut il renvoie. Si ss n’affiche rien sur un port alors que l’application indique qu’elle a démarré, il se peut qu’elle soit liée à 127.0.0.1 au lieu de 0.0.0.0, ce qui la rend invisible depuis l’extérieur de l’hôte.
Planification avec cron
cron exécute des commandes selon un calendrier défini. Chaque utilisateur possède une crontab composée de cinq champs temporels suivis de la commande.
# ┌ min (0-59)
# │ ┌ hour (0-23)
# │ │ ┌ day of month (1-31)
# │ │ │ ┌ month (1-12)
# │ │ │ │ ┌ day of week (0-6, Sun=0)
# * * * * * command
crontab -e # edit your crontab
crontab -l # list it
0 3 * * * /opt/app/backup.sh >> /var/log/backup.log 2>&1
*/5 * * * * curl -fsS localhost:3000/health > /dev/null
0 7 * * 1 /opt/app/weekly-report.sh
L’environnement de cron est minimal : il possède un PATH différent, aucun profil de shell et aucune variable interactive. Utilisez des chemins absolus, redirigez la sortie vers un log pour que les échecs soient visibles, et n’oubliez pas que cron ne relance pas une exécution ayant échoué. Pour tout besoin plus complexe, planifiez un timer systemd ou placez un job dans une file d’attente à la place.
Bonnes pratiques
- Utilisez des chemins absolus dans les scripts et les fichiers d’unité ; ne vous appuyez jamais sur l’environnement interactif.
- Placez
set -euo pipefailen haut de chaque script shell non trivial. - Appliquez le principe du moindre privilège : utilisateurs de service dédiés,
sudopour des commandes spécifiques, et aucune application ne doit s’exécuter en tant que root. - Privilégiez
SIGTERMet laissez les services s’arrêter proprement avant d’utiliserkill -9. - Configurez
Restart=on-failureet consultezjournalctl -u <unit>lors du débogage. - Gardez la configuration sous contrôle de version et ne modifiez qu’un seul élément à la fois sur un hôte en production.
- Effectuez une rotation des logs et surveillez
df -hetdf -i; les disques se remplissent sans prévenir. - Utilisez des clés SSH plutôt que des mots de passe, et ne stockez pas les clés privées sur les serveurs.
- Apprenez
find,grep,awket les pipes — ils remplacent une grande partie des outils ponctuels. - Testez une configuration avant de recharger le service qui l’utilise.
Erreurs courantes
- Exécuter des applications en tant que root, transformant ainsi le moindre bug en compromission totale du système.
- Utiliser
chmod -R 777pour « corriger » les permissions, supprimant ainsi toutes les protections entre les utilisateurs. - Supprimer des fichiers avec un wildcard non vérifié et sans sauvegarde.
- Envoyer
kill -9en premier, corrompant ainsi l’état que would un arrêt progressif (graceful shutdown) aurait vidé. - Définir des variables d’environnement dans votre shell en espérant que systemd puisse les voir.
- Supposer qu’un service est hors ligne alors qu’il est uniquement lié à
127.0.0.1. - Oublier
2>&1dans les entrées cron et perdre l’erreur qui explique l’échec d’une tâche. - Modifier la configuration directement sur un serveur sans contrôle de version et sans possibilité de retour en arrière.
- Ignorer l’épuisement des inodes parce que
df -hindique toujours qu’il reste de l’espace libre. - Laisser l’authentification par mot de passe activée sur un port SSH exposé sur Internet.
Et après ?
Linux est le socle de tout ce que vous déployez. Une fois que vous savez naviguer dans un serveur, lire ses logs et redémarrer ses services, le guide Nginx vous présente le serveur web que vous configurerez devant votre application, et Docker & Deployment transforme ces primitives en images isolées et reproductibles. Pour automatiser ces mêmes commandes à chaque push, consultez la section CI/CD, et pour comprendre le processus d’exécution réel de votre service, revenez aux Node.js Basics.