Verbindungszustände lesen, Ports zuordnen, Konflikte vor dem Start erkennen
Wer einen Dienst nicht startet, weil ein Port bereits belegt ist, oder wissen muss, welcher Prozess wirklich hinter einer Verbindung steckt, kommt an ss nicht vorbei. Dieser Artikel zeigt, warum ss das alte netstat abgelöst hat, wie man Verbindungszustände wie ESTABLISHED und TIME_WAIT richtig liest und wie man Port-Konflikte vor dem Start zuverlässig diagnostiziert.
Inhaltsverzeichnis
- 1. Warum Verbindungsanalyse zum Alltag gehört
- 2. ss vs. netstat: Warum ss das alte Tool abgelöst hat
- 3. ss-Grundlagen: Syntax und wichtigste Optionen
- 4. Verbindungszustände lesen: LISTEN, ESTABLISHED, TIME_WAIT
- 5. Welcher Prozess einen Port belegt
- 6. Address-already-in-use-Fehler vor dem Start diagnostizieren
- 7. Filterausdrücke und Verbindungsstatistiken
- 8. netstat: Wann das Legacy-Tool noch sinnvoll ist
- 9. ss und netstat im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Verbindungsanalyse zum Alltag gehört
Auf jedem Linux-Server, der einen Webshop, eine Datenbank oder einen Queue-Consumer betreibt, laufen ständig TCP- und UDP-Verbindungen auf, ab und in wartende Zustände über. Ein PHP-FPM-Pool, das nicht antwortet, eine Datenbankverbindung, die nie geschlossen wird, oder ein Deployment, das mit einer kryptischen Bind-Fehlermeldung abbricht: In all diesen Fällen ist der erste Schritt derselbe, nämlich ein Blick auf die aktuellen Netzwerkverbindungen des Systems. Ohne dieses Werkzeug bleibt die Fehlersuche reines Raten.
Die beiden klassischen Werkzeuge dafür heißen ss und netstat. Beide zeigen offene Sockets, deren Zustand und, mit den passenden Rechten, den dazugehörigen Prozess. Der Unterschied liegt nicht in der grundsätzlichen Aufgabe, sondern in der Umsetzung: Wie schnell liefert das Tool die Daten, wie zuverlässig ist die Ausgabe bei tausenden gleichzeitigen Verbindungen, und wie gut lässt sich das Ergebnis in Skripten weiterverarbeiten. Genau diese Fragen entscheiden im Produktivbetrieb, welches Tool die richtige Wahl ist.
2. ss vs. netstat: Warum ss das alte Tool abgelöst hat
netstat stammt aus dem net-tools-Paket, dessen Entwicklung seit vielen Jahren praktisch stillsteht. Das Tool liest seine Informationen aus den Textdateien unter /proc/net/tcp, /proc/net/tcp6 und /proc/net/udp, parst diese Zeile für Zeile und löst anschließend für jede Verbindung Zusatzinformationen wie den Prozessnamen separat auf. Bei wenigen Dutzend Verbindungen fällt das nicht auf, bei einem stark ausgelasteten Webserver mit zehntausenden gleichzeitigen Sockets wird netstat spürbar langsam.
ss gehört zum iproute2-Paket und spricht direkt mit dem Kernel über Netlink-Sockets, genauer über die inet_diag-Schnittstelle. Der Kernel liefert die Socket-Informationen dabei in einem strukturierten Binärformat statt als Text, wodurch das Parsen entfällt und die Abfrage auch bei sehr vielen Verbindungen in Millisekunden abgeschlossen ist. Seit einigen Jahren ist iproute2 auf praktisch jeder gängigen Distribution vorinstalliert, während net-tools auf minimalen Images wie schlanken Docker-Basis-Images oft fehlt und manuell nachinstalliert werden muss. Aus diesem Grund empfiehlt auch die offizielle Dokumentation vieler Distributionen inzwischen ss als Standardwerkzeug.
3. ss-Grundlagen: Syntax und wichtigste Optionen
Der Aufruf ss [Optionen] [Filter] folgt einem einfachen Schema. Die wichtigsten Optionen lassen sich beliebig kombinieren: -t zeigt TCP-Sockets, -u UDP-Sockets, -l beschränkt die Ausgabe auf lauschende Sockets, -a zeigt zusätzlich alle aktiven Verbindungen, -n unterdrückt die DNS-Auflösung von Adressen und beschleunigt die Ausgabe spürbar, und -p zeigt den Prozessnamen samt PID, benötigt dafür aber Root-Rechte beziehungsweise sudo. Die Kombination ss -tlnp ist damit der Befehl, den man in der Praxis am häufigsten tippt.
Ohne -n versucht ss, jede IP-Adresse per Reverse-DNS in einen Hostnamen aufzulösen. Bei einer Liste mit hunderten Verbindungen summieren sich diese DNS-Anfragen zu einer spürbaren Verzögerung, besonders wenn der DNS-Server selbst langsam antwortet. Das Bash-Pattern für schnelle, skriptfreundliche Abfragen lautet deshalb immer, -n mitzugeben, sobald das Ergebnis maschinell weiterverarbeitet oder unter Zeitdruck gelesen werden soll.
# List all listening TCP sockets with owning process (needs root/sudo)
sudo ss -tlnp
# Example output
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1284,fd=6))
LISTEN 0 4096 127.0.0.1:9000 0.0.0.0:* users:(("php-fpm8.3",pid=2201,fd=9))
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=901,fd=3))
# All TCP sockets, numeric output, no DNS lookups
ss -atn
# Only currently established connections
ss -tan state established
4. Verbindungszustände lesen: LISTEN, ESTABLISHED, TIME_WAIT
TCP-Verbindungen durchlaufen eine feste Zustandsmaschine, und wer die wichtigsten Zustände kennt, kann eine ss-Ausgabe in Sekunden richtig einordnen. LISTEN bedeutet, dass ein Prozess einen Port gebunden hat und auf eingehende Verbindungen wartet, ohne dass bereits Daten fließen. ESTABLISHED ist eine vollständig aufgebaute Verbindung mit aktivem Datenaustausch in beide Richtungen, der Normalzustand für jede laufende Anfrage. SYN-SENT und SYN-RECV markieren den Drei-Wege-Handshake, der gerade läuft, und sollten in der Regel nur kurz sichtbar sein.
TIME-WAIT tritt auf, nachdem eine Verbindung aktiv geschlossen wurde, und dauert standardmäßig zwei Minuten (zweimal die maximale Segmentlebensdauer). Der Zustand verhindert, dass verspätete Pakete einer alten Verbindung fälschlich einer neuen Verbindung zugeordnet werden. Viele kurze TIME-WAIT-Verbindungen auf einem Webserver mit hohem Anfrageaufkommen sind völlig normal und kein Fehler. CLOSE-WAIT dagegen ist ein wichtiges Warnsignal: Der entfernte Partner hat die Verbindung bereits geschlossen, aber die eigene Anwendung hat den Socket noch nicht geschlossen. Häufen sich CLOSE-WAIT-Einträge über längere Zeit an, deutet das fast immer auf einen Socket-Leak in der Anwendung hin, etwa fehlendes Verbindungsschließen nach einem HTTP-Request in einem PHP-Worker oder einem Backend-Dienst.
5. Welcher Prozess einen Port belegt
Die Frage "Wer hört gerade auf Port 3306?" lässt sich mit einem Filterausdruck direkt beantworten, statt die komplette Liste manuell zu durchsuchen. ss unterstützt eine eigene Filtersyntax mit den Schlüsselwörtern sport und dport für Quell- und Zielport, sodass sich die Ausgabe schon auf Kernel-Ebene einschränken lässt, bevor überhaupt etwas auf dem Bildschirm erscheint. Das ist deutlich effizienter, als die volle Liste auszugeben und anschließend mit grep nach dem gesuchten Port zu filtern.
Ohne sudo zeigt ss -tlnp zwar weiterhin alle lauschenden Ports, die Spalte mit Prozessname und PID bleibt für Sockets fremder Benutzer aber leer, da der Kernel diese Information aus Sicherheitsgründen nur dem Besitzer des Prozesses oder Root preisgibt. Als Alternative eignen sich lsof -i :PORT, das denselben Zweck über die Dateideskriptor-Sicht erfüllt, oder fuser PORT/tcp, das direkt die PID des Prozesses ausgibt, der den Port hält, und sich gut für kurze Ad-hoc-Checks in Skripten eignet.
# Which process owns port 3306 (MySQL)?
sudo ss -tlnp sport = :3306
# Alternative tools if ss is not available
sudo lsof -i :3306
sudo fuser 3306/tcp
# Check before starting a service to avoid a bind failure
sudo ss -ltn sport = :8080 | grep -q LISTEN && echo "Port 8080 is already in use" || echo "Port 8080 is free"
# Real failure from systemd when the port is already taken
sudo systemctl start nginx
# Job for nginx.service failed because the control process exited with error code.
# nginx[1421]: bind() to 0.0.0.0:80 failed (98: Address already in use)
6. Address-already-in-use-Fehler vor dem Start diagnostizieren
Der Fehler bind() to 0.0.0.0:80 failed (98: Address already in use) ist einer der häufigsten Gründe, warum ein Deployment scheitert. Die Ursache ist fast immer dieselbe: Ein anderer Prozess, ein Docker-Container über docker-proxy, oder ein hängender alter Prozess derselben Anwendung belegt den Port bereits. Statt den Dienst blind neu zu starten und auf Besserung zu hoffen, lohnt sich ein gezielter Check mit ss vor jedem Start, gerade in Deployment-Skripten und CI/CD-Pipelines.
In systemd-Units lässt sich dieser Check sogar automatisieren: Eine ExecStartPre-Zeile prüft den Port und bricht mit einer klaren Fehlermeldung im Journal ab, statt dass der Dienst in eine wenig aussagekräftige Restart-Schleife läuft. Das spart im Ernstfall wertvolle Minuten bei der Fehlersuche, weil sofort klar ist, dass ein Portkonflikt und kein Konfigurationsfehler im Dienst selbst vorliegt.
[Unit]
Description=Custom Magento Queue Consumer
After=network.target mysql.service
[Service]
Type=simple
User=magento
# Fail fast with a clear log line instead of a cryptic bind error
ExecStartPre=/bin/sh -c '! ss -ltn sport = :8082 | grep -q LISTEN'
ExecStart=/usr/bin/php /var/www/magento/bin/magento queue:consumers:start async.operations.all
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Ein häufiger Auslöser in containerisierten Umgebungen ist eine falsch aufgeräumte docker-compose.yml: Ein Reverse Proxy bindet Port 80 direkt an den Host, während ein zweiter Dienst denselben Port auf dem Host über eine ältere Compose-Datei ebenfalls beanspruchen will. docker ps --filter "publish=80" zeigt in diesem Fall zuverlässig, welcher Container den Port bereits hält, bevor ss überhaupt zum Einsatz kommt.
# docker-compose.yml: a leftover container is a common cause,
# of "Address already in use" on the host
services:
varnish:
image: varnish:7.5
ports:
- "80:80" # binds host port 80 directly
depends_on:
- nginx
nginx:
image: nginx:1.27
expose:
- "8080"
# Find the container holding the host port before starting nginx natively:
# docker ps --filter "publish=80"
7. Filterausdrücke und Verbindungsstatistiken
Die Filtersyntax von ss geht deutlich über einfache Portangaben hinaus. Ausdrücke wie state established, state time-wait oder Kombinationen mit and lassen sich direkt hinter die Optionen schreiben, etwa ss -tan state established '( dport = :443 or sport = :443 )', um gezielt alle aktiven HTTPS-Verbindungen zu isolieren. Diese Filter werden vom Kernel selbst ausgewertet, was insbesondere auf Systemen mit vielen tausend Verbindungen deutlich schneller ist als eine nachgelagerte Filterung mit grep oder awk.
Für einen schnellen Überblick ohne Einzelverbindungen liefert ss -s eine kompakte Statistik: Gesamtzahl der Sockets, aufgeschlüsselt nach TCP, UDP und weiteren Protokollen, sowie die Verteilung der TCP-Verbindungen auf Zustände wie established, closed, orphaned und timewait. Für die maschinelle Weiterverarbeitung, etwa in einem Monitoring-Skript, eignet sich ss -J, das die Socket-Liste als JSON ausgibt und damit robustes Parsen ohne fragile Spaltenzählung ermöglicht.
// ss -J emits machine-readable JSON, stable for scripts and monitoring
$ ss -tlnp -J
{
"sockets": [
{
"local": { "address": "0.0.0.0", "port": 80 },
"peer": { "address": "0.0.0.0", "port": 0 },
"state": "LISTEN",
"process": { "name": "nginx", "pid": 1284 }
},
{
"local": { "address": "127.0.0.1", "port": 9000 },
"peer": { "address": "0.0.0.0", "port": 0 },
"state": "LISTEN",
"process": { "name": "php-fpm8.3", "pid": 2201 }
}
]
}
8. netstat: Wann das Legacy-Tool noch sinnvoll ist
Trotz aller Nachteile ist netstat nicht wertlos. Auf sehr minimalen Systemen, alten Appliances oder in Legacy-Container-Images ohne iproute2 ist netstat mitunter das einzig verfügbare Werkzeug. Die Ausgabe von netstat -s liefert zudem seit Jahrzehnten dasselbe vertraute Format mit detaillierten Protokollstatistiken, etwa zu retransmittierten TCP-Segmenten oder verworfenen ICMP-Paketen, das viele erfahrene Administratoren aus Gewohnheit weiterhin gut lesen können.
Problematisch wird es, wenn alte Monitoring- oder Deployment-Skripte die Textausgabe von netstat mit grep und awk zeilenweise zerlegen. Solche Skripte brechen bei jeder kleinen Änderung der Spaltenbreite oder Lokalisierung. Wer bestehende Skripte modernisiert, sollte sie schrittweise auf ss -H, das die Kopfzeile unterdrückt, oder auf ss -J für strukturiertes JSON umstellen. Beide Varianten liefern ein stabiles, versioniertes Ausgabeformat, das sich nicht bei jedem Distributions-Update ändert, und machen Automatisierung damit deutlich robuster.
9. ss und netstat im direkten Vergleich
Für die tägliche Praxis lohnt sich eine direkte Gegenüberstellung der gebräuchlichsten Befehle. Wer netstat-Gewohnheiten aus älteren Skripten oder aus der Ausbildung mitbringt, findet hier die jeweilige ss-Entsprechung, inklusive des konkreten Vorteils im Produktivbetrieb.
| Aufgabe | netstat (veraltet) | ss (empfohlen) | Vorteil |
|---|---|---|---|
| Lauschende TCP-Ports anzeigen | netstat -tlnp |
ss -tlnp |
Netlink statt /proc-Parsing, deutlich schneller |
| Prozess hinter Port finden | netstat -tulpn | grep :80 |
ss -tlnp sport = :80 |
Kernel-Filter statt nachgelagerter Textsuche |
| Verbindungsstatistiken | netstat -s |
ss -s |
Kompaktere, aktuelle Kernel-Zähler |
| Etablierte Verbindungen zählen | netstat -ant | grep ESTABLISHED | wc -l |
ss -tan state established | wc -l |
Kein Parsen von Freitext nötig |
| UNIX-Sockets auflisten | netstat -x |
ss -x |
Gleiche Filtersyntax wie bei TCP/UDP |
Der Trend in der Tabelle ist eindeutig: Überall dort, wo netstat auf Textverarbeitung mit grep oder awk angewiesen ist, bietet ss einen nativen Filterausdruck, der vom Kernel direkt ausgewertet wird. Das reduziert nicht nur die Laufzeit, sondern auch die Fehleranfälligkeit von Skripten, die auf einer stabilen Ausgabe angewiesen sind.
Mironsoft
Server-Diagnose, Deployment-Absicherung und Linux-Infrastruktur
Portkonflikte und hängende Verbindungen im Griff?
Wir analysieren eure Server-Infrastruktur, identifizieren Portkonflikte und Socket-Leaks und bauen belastbare Vorab-Checks in Deployment- und systemd-Prozesse ein, damit Address-already-in-use-Fehler nicht mehr im Produktivbetrieb auftauchen.
Server-Audit
Vollständige Analyse offener Ports, Prozesse und Verbindungszustände
Deployment-Härtung
Port-Checks in systemd-Units und CI/CD-Pipelines nachrüsten
Monitoring-Integration
ss -J und strukturierte Verbindungsdaten in bestehende Monitoring-Systeme einbinden
10. Zusammenfassung
ss und netstat lösen dieselbe Grundaufgabe, nämlich Netzwerkverbindungen sichtbar zu machen, aber ss tut dies über Netlink direkt im Kernel und damit schneller und zuverlässiger als das textbasierte netstat. Verbindungszustände wie LISTEN, ESTABLISHED und TIME-WAIT beschreiben normale Phasen einer TCP-Verbindung, während sich häufende CLOSE-WAIT-Einträge fast immer auf einen Fehler in der Anwendung zurückführen lassen. Mit ss -tlnp und gezielten Filterausdrücken lässt sich in Sekunden klären, welcher Prozess einen Port belegt, noch bevor ein Deployment mit einem kryptischen Bind-Fehler abbricht.
Der größte praktische Nutzen entsteht, wenn dieser Check nicht erst beim Debugging, sondern präventiv in Deployment-Skripten und systemd-Units eingebaut wird. Eine ExecStartPre-Prüfung, die einen belegten Port sofort mit einer klaren Meldung im Journal quittiert, spart in der Praxis regelmäßig wertvolle Minuten gegenüber dem manuellen Debugging eines fehlgeschlagenen Service-Starts. netstat bleibt als Fallback auf minimalen Systemen relevant, sollte aber in aktiv gepflegten Skripten schrittweise durch ss -H oder ss -J ersetzt werden.
ss und netstat: Das Wichtigste auf einen Blick
ss statt netstat
Netlink-basiert, deutlich schneller bei vielen Sockets und Teil des vorinstallierten iproute2-Pakets auf fast jeder Distribution.
Zustände richtig lesen
TIME-WAIT ist normal nach dem Verbindungsende, häufende CLOSE-WAIT-Einträge deuten fast immer auf einen Socket-Leak hin.
Prozess pro Port finden
sudo ss -tlnp sport = :PORT zeigt Prozessname und PID direkt, ohne Textfilterung mit grep.
Konflikte vorbeugen
Port-Check als ExecStartPre in systemd-Units oder als Vorabschritt im Deployment-Skript verhindert kryptische Bind-Fehler.