« bind: Address already in use » : qui tient vraiment ce port ?
Publié le 14 août 2026Brouillon
Le port local que vous demandez est déjà utilisé, presque toujours par un tunnel SSH précédent que vous croyiez fermé. Un ssh -L interrompu par la fermeture d'un terminal laisse parfois son processus derrière lui, et ce processus tient le port. Une seule commande nomme le coupable : lsof -nP -iTCP:5433 -sTCP:LISTEN. Le reste de cette page explique pourquoi il est encore là, et comment ne plus jamais le rencontrer.
Qui occupe le port ?
La question n'a qu'une réponse fiable, et c'est le noyau qui la donne. Voici la panne reproduite : un premier tunnel détaché avec ssh -f -N -L, puis un second qui demande le même port.
ssh -N -L 15434:127.0.0.1:8080 serveur
bind [127.0.0.1]:15434: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 15434
Could not request local forwarding.
Et la commande qui identifie l'occupant, avec sa sortie réelle :
lsof -nP -iTCP:15434 -sTCP:LISTEN
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ssh 42628 leandre 7u IPv6 0xb5c650980393c14e 0t0 TCP [::1]:15434 (LISTEN)
ssh 42628 leandre 8u IPv4 0x5a68cd66228f0eae 0t0 TCP 127.0.0.1:15434 (LISTEN)
Deux lignes pour un seul processus : OpenSSH écoute par défaut en IPv4 et en IPv6, et le même PID tient les deux. C'est ce PID qu'il faut regarder avant tout geste. S'il s'agit d'un vieux tunnel à vous, kill 42628 suffit : le processus se termine proprement et libère le port. Le kill -9 réflexe, sans avoir identifié le processus d'abord, est le moyen le plus sûr d'arrêter autre chose que ce qu'on visait.
Pourquoi un tunnel mort tient-il encore le port ?
Parce qu'il n'est pas mort. Un ssh -N -L lancé dans un terminal appartient à ce terminal, mais il n'en meurt pas toujours avec lui : détaché avec -f, lancé sous un multiplexeur, ou simplement épargné par la façon dont le terminal ferme ses processus, il continue de tourner sans fenêtre pour le montrer. Il ne consomme rien, ne dit rien, et écoute toujours. Des jours plus tard, le port que « personne » n'utilise est toujours à lui.
L'inverse existe aussi et se voit dans la sortie ci-dessus : le second ssh n'obtient pas le port, mais il ne s'arrête pas pour autant. Could not request local forwarding n'est pas fatal : la session SSH s'établit, tourne, et le tunnel qu'elle devait porter n'existe pas. Vous avez alors deux processus ssh vivants, un qui tient le port avec l'ancien tunnel, un qui tourne sans tunnel du tout. C'est la combinaison qui rend cette panne si déroutante.
lsof, ss ou netstat : la commande selon le système
| Système | La commande | Ce qu'elle donne |
|---|---|---|
| macOS | lsof -nP -iTCP:5433 -sTCP:LISTEN | Processus, PID, adresse d'écoute |
| Linux | ss -tlnp 'sport = :5433' | Pareil, plus vite, sans lsof |
| Linux ancien | netstat -tlnp | grep 5433 | La forme historique, encore partout |
| Les deux | kill <PID> puis relancer le tunnel | La réparation, après identification |
Les drapeaux de lsof ne sont pas décoratifs : -nP évite les résolutions de noms qui font attendre la commande, -sTCP:LISTEN ne montre que ce qui écoute, c'est-à-dire ce qui peut tenir un port. Sans lui, la sortie mélange les connexions établies et noie la ligne qui compte.
Comment ne plus jamais rencontrer cette erreur ?
Deux réflexes suffisent. Le premier : un port par usage, choisi une fois et noté. Les collisions viennent presque toujours de ports improvisés, 5433 aujourd'hui, 5433 encore demain pour autre chose. Le second : vérifier avant d'ouvrir, lsof d'abord, ssh -L ensuite, ce qui transforme l'erreur en information.
C'est exactement ce que Kestro fait à chaque ouverture : il demande « puis-je écouter ici ? » avant de lancer le tunnel, et propose un port libre au lieu d'échouer. La méthode manuelle ci-dessus rend le même service si vous préférez vos alias, et le guide du tunnel qui tombe rassemble les autres pannes de la même famille.
Ce qui reste
Address already in use a un cousin plus rare, Permission denied, qui ne parle pas d'un port pris mais d'un port interdit : en dessous de 1024, seul root écoute, et la réponse est un port plus haut, pas un sudo. Reste aussi le cas où lsof ne montre rien alors que le bind échoue : un autre utilisateur de la machine tient le port, et lsof ne montre par défaut que vos propres processus. sudo lsof tranche. Et si votre tunnel semble ouvert mais que la connexion échoue quand même, le message est différent et la cause aussi : c'est l'erreur « connect failed: Connection refused ».