CyberSDF

Reprenez la main sur votre numérique

Port occupé : identifier le processus et libérer le port

« Address already in use » apparaît quand un couple adresse et port est déjà lié

Le message « bind: Address already in use » sort quand un second programme demande un couple adresse et port déjà lié par un autre socket. La page de manuel errno(3) donne cette chaîne exacte pour le code EADDRINUSE, et bind(2) la résume par une phrase : l’adresse donnée est déjà utilisée. Aucun défaut du logiciel n’est en cause, un autre processus détient l’adresse. Le cas se produit au redémarrage d’un démon arrêté trop vite, avant que le noyau ait relâché son socket.

Sommaire
  1. « Address already in use » apparaît quand un couple adresse et port est déjà lié
  2. Étape 1 : ss -lntp montre le socket en écoute et le programme qui le détient
  3. Étape 2 : lsof -nP -iTCP:8080 -sTCP:LISTEN donne le PID, l’utilisateur et le descripteur
  4. Étape 3 : couper le programme, puis vérifier que la ligne d’écoute a disparu
  5. ss, lsof et fuser ne se remplacent pas sur quatre critères mesurables
  6. Trois cas où la ligne LISTEN survit à l’arrêt du processus
  7. Cinq arbitrages départagent kill, systemctl et fuser
  8. Sources

La règle est écrite dans ip(7) : un seul socket IP peut être lié à un couple adresse et port donné. Le même document signale que les ports inférieurs à 1024 sont privilégiés et exigent la capacité CAP_NET_BIND_SERVICE. La documentation du noyau Linux fixe à 1024 la valeur par défaut du réglage ip_unprivileged_port_start, ce qui explique l’échec sur le port 80 d’un service lancé sans privilèges alors que le port 8080 accepte la même écoute ; la rubrique Linux regroupe les autres réglages de ce type.

bind: Address already in use

La cause tient à un socket encore lié, soit en écoute, soit fermé mais retenu quelques instants par le noyau. Le remède se joue en trois gestes : lire la liste des sockets en écoute, nommer le processus qui tient la ligne, couper ce processus puis relire la liste. bind(2) documente un second cas de EADDRINUSE, l’épuisement de la plage de ports éphémères, dont la documentation du noyau situe les bornes par défaut à 32768 et 60999.

Étape 1 : ss -lntp montre le socket en écoute et le programme qui le détient

La commande ss -lntp affiche les sockets en écoute et le programme qui détient chacun d’eux sur une machine Debian ou Ubuntu. La page ss(8) précise que les sockets en écoute sont omis par défaut, d’où l’option -l, que -n garde les numéros de port bruts au lieu des noms de service, que -t restreint à TCP et que -p ajoute la colonne des processus. Ces quatre lettres répondent à la question posée sans lecture supplémentaire, y compris pour un service lancé par un utilisateur ordinaire.

Une sortie se lit colonne par colonne : Local Address:Port donne l’adresse et le port, State porte la valeur LISTEN, et la dernière colonne nomme le programme accompagné de son identifiant de processus et de son descripteur de fichier. Le filtre d’expression documenté dans ss(8) accepte les prédicats sport et dport, donc la seconde commande ci-dessous restreint l’affichage au port 8080 sans tri manuel.

ss -lntp
ss -lntp '( sport = :8080 )'

Après la première commande, la ligne du port cherché porte LISTEN dans la colonne State et se termine par une mention du type users:((“nginx”,pid=812,fd=6)), avec le nom du programme, son PID et son descripteur entre parenthèses. Quand aucune ligne ne sort, plus rien n’écoute sur ce port. Une dernière colonne vide alors que la ligne existe signale un défaut de droits de lecture, cas traité dans les questions ci-dessous.

Étape 2 : lsof -nP -iTCP:8080 -sTCP:LISTEN donne le PID, l’utilisateur et le descripteur

La commande lsof -nP -iTCP:8080 -sTCP:LISTEN nomme le processus qui écoute sur le port 8080, avec son identifiant, son utilisateur et son descripteur de fichier. La page de manuel Debian de lsof annonce la révision 4.99.4 dans le paquet lsof de Debian 13. Le motif attendu par l’option -i suit la forme [46][protocole][@hôte][:service ou port], ce qui rend l’écriture -iTCP:8080 valide. Un filtre par port évite de parcourir la liste complète des sockets de la machine, longue sur un serveur qui héberge plusieurs services.

Les options -n et -P coupent la résolution des noms d’hôte et des noms de service, et la même page indique que cette conversion ralentit l’outil. L’option -sTCP:LISTEN ne conserve que les sockets en écoute et écarte les connexions établies. L’option -t réduit la sortie aux identifiants de processus, sans en-tête, et la page précise qu’elle implique -w, donc la disparition des avertissements.

lsof -nP -iTCP:8080 -sTCP:LISTEN
lsof -t -nP -iTCP:8080 -sTCP:LISTEN

La première sortie porte les colonnes COMMAND, PID, USER, FD, TYPE, DEVICE, SIZE/OFF, NODE et NAME, la dernière se terminant par (LISTEN) sur un socket en écoute. La seconde commande n’écrit qu’un nombre par ligne, directement réutilisable dans une boucle. Quand rien ne correspond, lsof sort avec un code de retour non nul ; la page présente l’option -Q comme le moyen d’obtenir un code 0 malgré une recherche vide.

Étape 3 : couper le programme, puis vérifier que la ligne d’écoute a disparu

Libérer un port occupé se fait en deux gestes, couper le programme qui le détient puis relire le port pour vérifier que la ligne d’écoute a disparu. Le nom lu à l’Étape 1 décide du geste : un service géré par systemd se coupe avec systemctl stop suivi du nom de l’unité, un programme lancé à la main se termine avec kill suivi du PID lu à l’Étape 2. Aucune de ces deux commandes ne demande de privilèges particuliers lorsque le processus appartient au compte qui les exécute.

Le signal ordinaire laisse le programme fermer ses fichiers et relâcher ses sockets, ce qu’un SIGKILL ne permet pas ; la page fuser(1) rappelle d’ailleurs que fuser envoie SIGKILL par défaut. La vérification reprend la commande ss de l’Étape 1, filtrée sur le même port, pour éviter de conclure sur une impression.

systemctl stop nginx.service
kill 812
ss -lntp '( sport = :8080 )'

La dernière commande ne renvoie plus aucune ligne sur le port visé. Si une ligne subsiste avec un autre PID, un second programme a pris le port entre-temps ou un superviseur a relancé le service arrêté. Un service relancé par systemd ne se contente pas d’un kill, et le paragraphe suivant détaille ce point ainsi que deux autres cas où la ligne LISTEN survit à l’arrêt du programme.

ss, lsof et fuser ne se remplacent pas sur quatre critères mesurables

Le tableau ci-dessous compare ss, lsof et fuser sur quatre critères vérifiables dans leurs pages de manuel respectives. La page ss(8) appartient au projet iproute2, la page fuser(1) au projet psmisc, et la page Debian de lsof annonce la révision 4.99.4 pour le paquet lsof de Debian 13. Un seul des trois outils se contente de lire la table des sockets, les deux autres peuvent agir dessus sans passer par kill.

Le choix dépend de la suite : nommer un processus sans rien lui envoyer, fermer un socket que plus personne ne garde, ou terminer en une commande un programme dont on ne connaît que le port. Les trois lisent les mêmes fichiers du noyau exposés par /proc, et l’écart tient aux options disponibles et aux droits exigés.

Trois outils de diagnostic réseau et les options qui les distinguent
Outil Projet qui publie la page de manuel Option qui nomme le processus Action directe sur le socket
ss iproute2 -p, et -T pour les threads -K tente de fermer les sockets IPv4 et IPv6 seulement
lsof lsof, révision 4.99.4 dans Debian 13 PID affiché dès la sélection -i, -t pour les PID seuls aucune
fuser psmisc les PID forment toute la sortie standard -k envoie SIGKILL sauf si un signal est nommé

Trois cas où la ligne LISTEN survit à l’arrêt du processus

Trois situations gardent un port occupé après la disparition du processus visé par le kill. La page systemd.socket(5) décrit la première : une unité .socket détient le socket en écoute pour le compte d’un service, et cette page note qu’aucune dépendance implicite WantedBy= ou RequiredBy= ne relie le socket au service, si bien que le service peut tourner sans le socket, et réciproquement.

La deuxième vient du noyau, car ip(7) écrit qu’une adresse TCP liée reste indisponible un certain temps après la fermeture, sauf si l’option SO_REUSEADDR a été posée, et que cette option rend TCP moins fiable. La troisième tient aux droits d’accès : la page proc_pid_fd(5) indique que la lecture des liens de /proc/PID/fd passe par une vérification ptrace, ce qui masque le processus à un compte qui n’en est pas propriétaire.

Un port occupé par un service systemd se libère en arrêtant l’unité .socket et non le seul service. La page systemd.socket(5) réserve d’autres surprises du même ordre : une adresse déclarée par un simple numéro dans ListenStream= produit une écoute en IPv6, disponible aussi en IPv4 selon la valeur de BindIPv6Only=, et l’option ReusePort= autorise plusieurs liages du même port TCP ou UDP. Deux lignes LISTEN sur un même port ne sont donc pas toujours une erreur.

Pour un socket en cours de fermeture, ss(8) fournit un filtre d’état ; la commande ss -tan state time-wait ‘( sport = :8080 )’ liste les sockets de ce port restés dans cet état. La page fuser(1) énonce une limite comparable : lancé sans privilèges sur un port TCP, fuser ne rapporte aucun accès, faute de pouvoir lire la table des descripteurs de processus qui ne lui appartiennent pas. Diagnostiquer une connexion réseau se heurte souvent au même mur.

Cinq arbitrages départagent kill, systemctl et fuser

Le choix entre kill, systemctl stop et fuser -k se joue sur des détails que les pages de manuel précisent une par une. La cible compte d’abord : un processus orphelin se termine avec un signal, un service sous systemd se coupe par son unité, et un socket détenu par une unité .socket survit à l’arrêt du service qu’il active.

Les droits décident ensuite, car la lecture de la table des descripteurs d’un processus étranger est filtrée par le noyau. Vient enfin le coût d’un signal trop brutal, qui prive le programme de ses dernières écritures. Les cinq questions ci-dessous reprennent ces points avec les commandes qui tranchent.

Pourquoi la colonne Process de ss -lntp reste-t-elle vide ?

Le noyau ne livre pas la table des descripteurs d’un processus à un compte qui n’en est pas propriétaire, comme le précise la page proc_pid_fd(5) à propos de la vérification ptrace. La page fuser(1) décrit la même limite et avertit qu’un utilisateur non privilégié ne rapporte aucun accès sur un port TCP. La sortie devient complète avec sudo ss -lntp, ou en interrogeant le port depuis le compte qui exécute le service, dont dépendent les permissions des utilisateurs.

Faut-il installer lsof quand ss suffit déjà ?

ss vient du projet iproute2, présent sur une installation Debian ou Ubuntu standard, tandis que lsof est un paquet à part, en révision 4.99.4 dans Debian 13. L’écart tient à la sortie : ss affiche le nom du programme et son PID, lsof ajoute l’utilisateur, le type de socket et le descripteur, et son mode -t alimente un script en une ligne. Sur une machine sans lsof, ss -lntp couvre le besoin sans rien installer.

Que fait fuser -k -n tcp 8080 exactement ?

La page fuser(1) indique que -k envoie SIGKILL si aucun signal n’est nommé, et qu’un signal explicite s’écrit fuser -k -TERM -n tcp 8080. fuser ne place que les PID sur la sortie standard, le reste partant sur la sortie d’erreur, et il retourne un code non nul quand rien n’est trouvé. Sur un port TCP sans privilèges, il annonce aucun accès plutôt que de deviner.

Le processus est mort et le port reste pris, que reste-t-il ?

ip(7) écrit qu’une adresse TCP liée reste indisponible quelque temps après la fermeture, sauf si SO_REUSEADDR a été posée. La commande ss -tan state time-wait ‘( sport = :8080 )’ montre les sockets concernés. Activer SO_REUSEADDR dans un serveur règle les redémarrages rapides, mais ip(7) avertit que cette option rend TCP moins fiable, ce qui la réserve aux cas où le redémarrage immédiat compte.

Faut-il arrêter l’unité .service ou l’unité .socket ?

La page systemd.socket(5) attribue le socket en écoute à l’unité .socket, et rappelle qu’aucune dépendance implicite ne relie cette unité au service qu’elle active. Arrêter nginx.service laisse donc le port sous la garde de nginx.socket, et la seule commande utile est systemctl stop nginx.socket. Un redémarrage machine ou un systemctl daemon-reload ne change rien à cet ordre.

Sources