Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Cloud / Hosting 03.11.2026 · 9 min Lesezeit

Docker mit eigener IPv6 auf netcup: /64 per NDP-Proxy für Container

Teil 8 der Reihe netcup Spezial: Warum Container mit IPv6 aus dem netcup-/64 zuerst nicht erreichbar sind und wie Sie das mit Docker im Modus routed und ndppd lösen, getestet mit Neustart.

Geprüft am 03.11.2026 · für Docker 29.9.2, ndppd 0.2.5, Ubuntu 24.04.5

Mit KI erstellt – redaktionelle Prüfung ausstehend

WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Titelbild: Docker mit eigener IPv6 auf netcup per NDP-Proxy, Teil 8 der Reihe netcup Spezial

Jeder netcup-VPS bekommt ein ganzes /64-IPv6-Netz. Da liegt der Gedanke nahe, Docker-Containern eigene, öffentliche IPv6-Adressen daraus zu geben, ohne Portweiterleitung und ohne NAT. In der Praxis scheitert das zuerst, und zwar aus einem netcup-typischen Grund: Das Netz ist nicht zum Server geroutet, sondern direkt am Interface angeschlossen. Diese Anleitung zeigt Schritt für Schritt, wie Sie das erkennen, ein Docker-Netz mit IPv6 aus dem /64 anlegen und es mit dem NDP-Proxy ndppd von außen erreichbar machen. Alle Befehle und Ausgaben stammen von unserem Testserver, einem VPS 500 G12.5 mit Ubuntu 24.04 und Docker 29.9.2.

Dies ist Teil 8 der Reihe „netcup Spezial“.

Voraussetzungen

  • netcup-VPS oder Root-Server mit IPv6-Netz, im Test ein VPS 500 G12.5 mit Ubuntu 24.04
  • Docker in aktueller Version, im Test 29.9.2, siehe Docker Compose unter Linux
  • Ihr /64-Präfix aus dem SCP unter Netzwerk, siehe Teil 6
  • Ein zweiter Rechner mit IPv6 für den Test von außen
  • Etwa 30 Minuten

In allen Beispielen steht 2001:db8:1:2 für die ersten vier Blöcke Ihres /64. Ersetzen Sie es durch Ihr Präfix.

Schritt 1: Prüfen, ob das /64 geroutet ist

Es gibt zwei Arten, ein IPv6-Netz an einen Server zu geben. Geroutet heißt: Der Router schickt alles für das Netz an Ihren Server, Sie verteilen es selbst. On-link heißt: Der Router fragt für jede einzelne Adresse per Neighbor Solicitation, wer sie hat, und schickt Pakete nur, wenn jemand antwortet. Welche Variante Sie haben, zeigt ein Mitschnitt. Schneiden Sie auf dem Server Neighbor Solicitations mit und rufen Sie von außen eine unbenutzte Adresse aus Ihrem /64 auf:

apt-get install -y tcpdump
tcpdump -n -i eth0 'icmp6 and ip6[40]==135'

Wir haben von einem zweiten Rechner eine Verbindung zu 2001:db8:1:2::abcd versucht. Der Mitschnitt zeigte, dass der Router nach genau dieser Adresse fragte (Zeitstempel gekürzt):

IP6 fe80::… > ff02::1:ff00:abcd: ICMP6, neighbor solicitation, who has 2001:db8:1:2::abcd, length 32

Das /64 ist bei netcup also on-link. Für eine Adresse, die nicht am Interface eth0 eingetragen ist, antwortet der Server nicht, und die Pakete kommen nie an. Genau das betrifft Container, deren Adressen hinter einer Docker-Bridge liegen.

Verifizieren: Im Mitschnitt erscheint „who has“ mit der Adresse, die Sie von außen aufgerufen haben.

Schritt 2: Ein Docker-Netz mit IPv6 anlegen

Teilen Sie ein Stück aus Ihrem /64 für Docker ab. Ein /80 ist reichlich und lässt den Rest des /64 frei. Docker kann IPv6-Netze im Modus routed anlegen, im Test mit Docker 29.9.2. Dann leitet Docker Pakete direkt an die Container weiter, ohne die Adressen zu übersetzen:

docker network create --ipv6 --subnet 2001:db8:1:2:d0c::/80 \
  -o com.docker.network.bridge.gateway_mode_ipv6=routed v6test

Eine Änderung an /etc/docker/daemon.json war dafür nicht nötig. Starten Sie einen Container mit fester Adresse aus dem /80:

docker run -d --name v6web --network v6test --ip6 2001:db8:1:2:d0c::10 nginx:1.29-alpine
docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' v6web
2001:db8:1:2:d0c::10

Im Container zeigte ip -6 route als Standardweg default via 2001:db8:1:2:d0c::1, die Adresse der Docker-Bridge. Von außen war der Container in diesem Zustand nicht erreichbar, curl von unserem zweiten Rechner lief ins Leere. Auch ein Aufruf aus dem Container ins IPv6-Internet lief in einen Timeout. Das passt zu Schritt 1: Niemand beantwortete die Neighbor Solicitation des Routers für die Adresse des Containers.

Verifizieren: docker inspect zeigt die gewünschte IPv6-Adresse.

Schritt 3: Ports freigeben

Bevor Sie den NDP-Proxy einrichten, noch eine Falle, die wir erst im Test gefunden haben. Auch im Modus routed lässt Docker eingehende Verbindungen nur zu veröffentlichten Ports durch. Die Regeln zeigt ip6tables:

-A DOCKER -d 2001:db8:1:2:d0c::10/128 ! -i br-95945824c29e -o br-95945824c29e -p tcp -m tcp --dport 80 -j ACCEPT
-A DOCKER ! -i br-95945824c29e -o br-95945824c29e -j DROP

Diese Ausgabe, erzeugt mit ip6tables -S DOCKER | grep -v icmp, stammt von einem Container mit veröffentlichtem Port 80. Ohne Veröffentlichung fehlt die erste Zeile, und außer ICMP wird alles verworfen. Im Test blieb Port 80 ohne Veröffentlichung von außen zu, obwohl nginx lief und der NDP-Proxy aus Schritt 4 schon eingerichtet war.

Wie Sie den Port veröffentlichen, macht einen Unterschied. Mit dem üblichen -p 80 legt Docker zusätzlich einen zufälligen Port auf der IPv4-Adresse des Servers an, bei uns 0.0.0.0:32769. Dieser Port war im Test aus dem Internet erreichbar. Geben Sie deshalb nur IPv6 als Ziel an:

docker rm -f v6web
docker run -d --name v6web --restart unless-stopped --network v6test   --ip6 2001:db8:1:2:d0c::10 -p '[::]::80' nginx:1.29-alpine
docker ps --format '{{.Names}} {{.Ports}}'
v6web5 [::]:0->80/tcp

Die Ausgabe stammt von unserem Testcontainer v6web5. Es gab keinen IPv4-Port, die ACCEPT-Regel für Port 80 am Container war trotzdem da, und von außen lieferte der Container über IPv6 HTTP 200. Die Option --restart unless-stopped sorgt dafür, dass der Container nach einem Neustart wieder läuft. Wie Sie Docker-Ports und die netcup-Firewall zusammen absichern, zeigt unsere Anleitung zu Docker-Ports auf dem netcup-vServer.

Verifizieren: docker ps zeigt bei den Ports keinen Eintrag mit 0.0.0.0, und ip6tables -S DOCKER zeigt eine ACCEPT-Regel für Adresse und Port des Containers.

Schritt 4: Eine einzelne Adresse per Kernel freigeben

Für einen schnellen Test können Sie den Linux-Kernel selbst auf Neighbor Solicitations für eine Adresse antworten lassen:

sysctl -w net.ipv6.conf.eth0.proxy_ndp=1
ip -6 neigh add proxy 2001:db8:1:2:d0c::10 dev eth0
ip -6 neigh show proxy
2001:db8:1:2:d0c::10 dev eth0 proxy

Mit veröffentlichtem Port lieferte der Container danach von außen HTTP 200. Der Haken: Jede Adresse braucht einen eigenen Eintrag, und die Einträge gelten nur bis zum nächsten Neustart. Für mehrere Container ist ein NDP-Proxy-Dienst praktischer.

Verifizieren: curl -6 http://[2001:db8:1:2:d0c::10]/ von einem zweiten Rechner liefert die Startseite von nginx.

Schritt 5: ndppd für das ganze /80

ndppd beantwortet Neighbor Solicitations für ganze Bereiche. Unter Ubuntu 24.04 installieren Sie es aus den Paketquellen. Im Test kam ndppd in Version 0.2.5 aus den Paketquellen. Die Konfiguration für unser /80:

apt-get install -y ndppd
cat > /etc/ndppd.conf <<'EOF'
proxy eth0 {
  rule 2001:db8:1:2:d0c::/80 {
    static
  }
}
EOF
systemctl restart ndppd

Die Methode static antwortet für jede Adresse im Bereich, ohne zu prüfen, ob dort ein Container läuft. Im Journal meldete ndppd dazu eine Warnung, die bei diesem Aufbau zu erwarten ist:

(warning) Low prefix length (80 <= 120) when using 'static' method

Den Kernel-Eintrag aus Schritt 4 haben wir vorher gelöscht. Direkt danach blieb ::10 von außen noch erreichbar, eine neue Adresse wie ::20 erst mit ndppd. Ein Mitschnitt zeigte, dass ndppd für eine Adresse aus dem /80 antwortete:

IP6 fe80::… > ff02::1:ff00:31: ICMP6, neighbor solicitation, who has 2001:db8:1:2:d0c::31, length 32
IP6 fe80::… > fe80::… ICMP6, neighbor advertisement, tgt is 2001:db8:1:2:d0c::31, length 32

Weitere Container mit Adressen aus dem /80 waren damit ohne neuen Eintrag von außen erreichbar. Ein Container fragte von innen eine Adresse ab, die ihm seine öffentliche Adresse zurückmeldet, und bekam 2001:db8:1:2:d0c::20 zu sehen, also seine eigene Adresse, ohne NAT.

Verifizieren: systemctl is-active ndppd meldet active, und ein neuer Container mit -p ist von außen erreichbar.

Schritt 6: Geduld bei neuen Adressen

Nicht jede neue Adresse war sofort erreichbar. Ein Container auf 2001:db8:1:2:d0c::40 antwortete beim ersten Versuch nicht, beim zweiten mit HTTP 200. Ein anderer auf 2001:db8:1:2:d0c::30, den wir sehr kurz nach dem Start aufgerufen hatten, war erst nach rund anderthalb Minuten von außen erreichbar. Beim Container auf ::50 antwortete der zweite Versuch. Prüfen Sie eine neue Adresse deshalb nach einer kurzen Pause erneut, bevor Sie nach Fehlern suchen.

Verifizieren: Prüfen Sie zuerst auf dem Server mit curl -6 http://[Adresse]/, dass der Container antwortet. Antwortet er von außen noch nicht, warten Sie eine bis zwei Minuten und versuchen Sie es erneut.

Schritt 7: Nach einem Neustart

Wir haben den Server neu gestartet. ndppd lief danach von selbst wieder, und unser Testcontainer mit einer Neustart-Regel lief ebenfalls und war von außen gleich beim ersten Versuch erreichbar. Der Kernel-Schalter proxy_ndp aus Schritt 4 stand nach dem Neustart auf 0. Das störte nicht, denn ndppd beantwortet die Anfragen selbst.

Verifizieren: Nach dem Neustart zeigen systemctl is-active ndppd und ein Aufruf von außen, dass alles wieder läuft.

Typische Fehler

SymptomUrsache und Lösung
Container mit IPv6 von außen nicht erreichbar/64 ist on-link. NDP-Proxy wie ndppd einrichten.
Trotz NDP-Proxy keine VerbindungPort nicht veröffentlicht. Container mit -p '[::]::80' starten.
Unerwarteter offener IPv4-PortMit -p 80 veröffentlicht. Stattdessen -p '[::]::80' nutzen.
Ausgehende Verbindungen aus dem Container hängenAntworten finden nicht zurück, gleiche Ursache. NDP-Proxy einrichten.
Neue Adresse antwortet nichtKurz warten und erneut versuchen, im Test half das nach bis zu anderthalb Minuten.
Kernel-Proxy nach Neustart wegEinträge mit ip -6 neigh add proxy sind nicht dauerhaft. ndppd nutzen.

Häufige Fragen

Warum ein /80 und nicht gleich das ganze /64?

Das /64 enthält auch die Adresse des Servers selbst. Mit einem eigenen Bereich für Docker bleiben Server und Container sauber getrennt, und ndppd antwortet nur für den Container-Bereich.

Brauche ich dafür das App-Image von netcup?

Unser Testserver lief mit dem App-Image aus Teil 2. Die Schritte nutzen aber nur Docker, ndppd und Standardwerkzeuge von Ubuntu.

Ist das sicherer oder unsicherer als NAT?

Im Test ähnlich: Container sind direkt adressierbar, offen waren aber nur veröffentlichte Ports. Achten Sie auf den IPv4-Nebeneffekt von -p 80 und prüfen Sie nach jeder Änderung von außen, welche Ports erreichbar sind.

Fazit

Eigene IPv6-Adressen für Container sind bei netcup gut machbar, wenn man die Eigenheit kennt: Das /64 ist on-link, ohne NDP-Proxy kommt nichts an. Mit einem /80 für Docker im Modus routed, ndppd, Ports nur für IPv6 und einer Neustart-Regel haben Sie Container mit echten öffentlichen Adressen ohne NAT, die einen Neustart überstehen.

Weiterführende Anleitungen und Quellen

netcupnetcup SpezialDockerIPv6ndppdNDP-Proxy