Wissen

Residential Proxies in Docker-Containern konfigurieren

Proxy-Konfiguration in Docker: Umgebungsvariablen vs Daemon-Proxy, warum HTTP_PROXY nicht immer greift, NO_PROXY für internen Traffic, Secrets, und Identität pro Container.

Chris Collins

Chris Collins

18. Juli 2026 · 9 Min. Lesezeit

Einen Residential Proxy in einen Container einzubinden, sieht trivial aus, bis es das nicht ist. Sie setzen HTTP_PROXY, starten das Image, und Anfragen gehen weiterhin über die Host-IP hinaus. Oder der Proxy funktioniert, aber Ihre Health Checks beginnen zu scheitern, weil interne Service-Aufrufe jetzt durch einen Residential-Exit in einem anderen Land getunnelt werden. Oder, am schlimmsten, Ihre Zugangsdaten landen eingebacken in einer Image-Schicht.

Nichts davon ist exotisch. Es sind die vier Dinge, die Teams beim Einsatz von Proxies in Containern zuverlässig beißen: wo die Konfiguration greift, was sie umgehen sollte, wie Secrets hineinkommen, und wie Identität auf Container abgebildet wird. Dieser Leitfaden behandelt jedes davon, mit funktionierender Konfiguration, für DevOps- und Platform-Engineers.

Die eine Unterscheidung, die den Großteil der Verwirrung erklärt

Docker hat zwei vollständig getrennte Proxy-Konzepte, und sie zu verwechseln ist die Wurzel der meisten “warum funktioniert mein Proxy nicht”-Tickets:

  • Build-/Daemon-Proxy — konfiguriert in ~/.docker/config.json oder via --build-arg. Dieser steuert den Docker-Daemon (Images ziehen) und den Build-Prozess (RUN apt-get install). Er hat nichts mit dem Laufzeit-Traffic Ihrer Anwendung zu tun.
  • Laufzeit-Proxy — Umgebungsvariablen im laufenden Container. Das ist es, was Ihr Anwendungscode sieht.

Wenn Sie einen Proxy in ~/.docker/config.json konfiguriert haben und erwarteten, dass Ihr Python-Scraper ihn nutzt, ist das der Bug. Diese Einstellungen erreichen die Laufzeitumgebung des Containers nie.

Und noch eine Schicht: selbst zur Laufzeit ist HTTP_PROXY eine Konvention, keine Durchsetzung. Der Kernel routet den Traffic nicht dorthin. Jede Bibliothek entscheidet selbst, ob sie sie beachtet:

ClientBeachtet HTTP_PROXY/HTTPS_PROXY?
curl, wgetJa
Python requests, httpxJa (standardmäßig)
Node fetch / undiciNein, braucht einen expliziten Dispatcher/Agent
Go net/httpJa, via http.ProxyFromEnvironment (der Standard-Transport)
Chromium / Playwright / PuppeteerNein, braucht ein Launch-Flag oder eine Proxy-Option

Die erste Debugging-Frage ist also nie “ist die Umgebungsvariable gesetzt?”, sondern “liest dieser Client sie überhaupt?”.

Laufzeit-Proxy: das Grundsetup

Übergeben Sie den Proxy zur Laufzeit, nicht beim Build, und referenzieren Sie das Gateway mit dem im Benutzernamen kodierten Targeting:

Terminal window
docker run --rm \
-e HTTP_PROXY="http://${SHIFTER_USER}-country-us:${SHIFTER_PASS}@p.shifter.io:443" \
-e HTTPS_PROXY="http://${SHIFTER_USER}-country-us:${SHIFTER_PASS}@p.shifter.io:443" \
-e NO_PROXY="localhost,127.0.0.1,postgres,redis,.internal,169.254.169.254" \
my-scraper:latest

Setzen Sie Groß- und Kleinschreibung (HTTP_PROXY und http_proxy), wenn Sie sich bei Ihren Bibliotheken unsicher sind; die Konventionen unterscheiden sich, und manche Tools lesen nur eine. Beachten Sie, dass HTTPS_PROXY weiterhin auf eine http://-URL zeigt, das ist korrekt: das Schema beschreibt, wie Sie mit dem Proxy sprechen, und HTTPS wird per CONNECT durch ihn getunnelt.

In Compose halten Sie die Werte aus der Datei selbst heraus:

services:
scraper:
image: my-scraper:latest
environment:
HTTP_PROXY: "http://${SHIFTER_USER}-country-us:${SHIFTER_PASS}@p.shifter.io:443"
HTTPS_PROXY: "http://${SHIFTER_USER}-country-us:${SHIFTER_PASS}@p.shifter.io:443"
NO_PROXY: "localhost,127.0.0.1,postgres,redis,.internal"
depends_on: [postgres, redis]

Die Variablen werden aus Ihrer Shell oder einer .env-Datei interpoliert, die nicht eingecheckt wird.

NO_PROXY: der Schritt, den man überspringt und bereut

Das ist der, der die verwirrenden Fehler verursacht. Sobald HTTP_PROXY gesetzt ist, schickt jeder beachtende Client alles durch den Proxy, einschließlich Aufrufen an Ihre Datenbank, Ihren Cache, Ihre internen APIs und Cloud-Metadaten-Endpunkte. Folgen: interner Traffic verlässt Ihr Netz und kommt zurück (langsam, und manchmal kaputt), Health Checks scheitern, und Sie verbrennen Pro-GB-Bandbreite für Traffic, der den Host nie hätte verlassen dürfen.

Setzen Sie NO_PROXY immer so, dass es abdeckt:

  • localhost, 127.0.0.1, ::1
  • Compose-/Kubernetes-Servicenamen (postgres, redis, api.default.svc.cluster.local)
  • Interne Domains und private Bereiche (.internal, 10.0.0.0/8)
  • Cloud-Metadaten: 169.254.169.254

Zwei Vorbehalte, die man kennen sollte: das Matching von NO_PROXY ist über Bibliotheken hinweg nicht einheitlich implementiert (besonders CIDR-Unterstützung ist lückenhaft, und manche matchen Suffixe, während andere einen führenden Punkt brauchen), verifizieren Sie also, statt anzunehmen. Und in Kubernetes gehören .svc.cluster.local und Ihre Pod-/Service-CIDRs ebenfalls in NO_PROXY.

Secrets: Zugangsdaten nicht ins Image backen

Proxy-Zugangsdaten sind Secrets. Die Regeln:

Setzen Sie sie nie in ein Dockerfile via ENV oder ARG, beide bleiben in Image-Schichten bestehen, und docker history druckt sie bereitwillig aus. Jeder, der das Image ziehen kann, hat Ihre Zugangsdaten.

Injizieren Sie stattdessen zur Laufzeit. Für Compose eine Env-Datei außerhalb von Git; für Orchestrierung ein echter Secret-Store:

# Kubernetes: Zugangsdaten aus einem Secret, nicht aus dem Manifest
env:
- name: SHIFTER_USER
valueFrom:
secretKeyRef: { name: proxy-creds, key: username }
- name: SHIFTER_PASS
valueFrom:
secretKeyRef: { name: proxy-creds, key: password }

Bauen Sie die Proxy-URL dann in der App aus diesen zwei Variablen, damit die vollständige Zugangsdaten-Zeichenfolge nie in einem Manifest, einer Log-Zeile oder docker inspect auftaucht. Wenn Sie den Proxy während des Builds brauchen (Pakete installieren), nutzen Sie BuildKit-Secrets (--mount=type=secret) statt ARG, damit nichts in einer Schicht landet.

Außerdem: bereinigen Sie Proxy-URLs aus Logs. Ein Crash-Dump, der die effektive Konfiguration druckt, leakt user:pass@host direkt in Ihren Log-Aggregator.

Identität auf Container abbilden

Hier trifft Container-Architektur auf Proxy-Architektur. Weil das Targeting im Benutzernamen lebt, kann jeder Container seine eigene Identität tragen, einfach indem er eine andere Umgebungsvariable bekommt, keine separaten Endpunkte, keine IP-Listen.

Zwei Muster decken die meisten Bedürfnisse ab:

Ein Container, eine Geo. Führen Sie Worker pro Markt aus, indem Sie das Länder-Flag variieren:

Terminal window
docker run -d -e HTTP_PROXY="http://${U}-country-us:${P}@p.shifter.io:443" scraper:latest
docker run -d -e HTTP_PROXY="http://${U}-country-de:${P}@p.shifter.io:443" scraper:latest

Ein Container, eine Sticky Session. Geben Sie jeder Replik eine eigene sid, damit sie ihre eigene Exit-IP hält, nützlich, wenn ein Container einen mehrstufigen Flow besitzt:

services:
worker:
image: scraper:latest
environment:
# {{.Task.Slot}} gibt jeder Swarm-Replik eine eindeutige, stabile Session-ID
HTTP_PROXY: "http://${U}-country-us-sid-w{{.Task.Slot}}-ttl-600:${P}@p.shifter.io:443"
deploy:
replicas: 4

Eine Warnung: eine Umgebungsvariable auf Container-Ebene ist eine statische Identität für die Lebensdauer dieses Containers. Wenn Ihr Workload Rotation pro Anfrage oder pro Arbeitseinheit braucht, täuschen Sie sie nicht durch Container-Neustarts vor, setzen Sie den Proxy im Anwendungscode, wo Sie sid pro Job variieren können (das Muster aus dem Load-Balancing-Post). Umgebungsvariablen sind das richtige Werkzeug für grobe Identität pro Container; Code ist das richtige Werkzeug für feingranulare Rotation.

Clients, die die Umgebung ignorieren

Zwei häufige Fälle, die Ihnen in Containern begegnen:

Nodes fetch/undici liest keine Proxy-Umgebungsvariablen. Verdrahten Sie es explizit:

import { ProxyAgent, setGlobalDispatcher } from "undici";
setGlobalDispatcher(new ProxyAgent(process.env.HTTP_PROXY));

Headless-Browser ignorieren sie ebenfalls, Chromium braucht seinen Proxy beim Start übergeben, und Zugangsdaten über den eigenen Mechanismus des Frameworks (Playwright behandelt die Besonderheiten, einschließlich warum inline Zugangsdaten in Chromium scheitern). Pythons requests/httpx beachten die Umgebungsvariablen zwar, aber Proxies explizit zu übergeben ist klarer (Python-Leitfaden).

Ebenfalls erwähnenswert: SOCKS5 über Umgebungsvariablen ist unzuverlässig über Clients hinweg in Containern. Für die meisten Container-Workloads ist HTTP-Proxying der richtige Standard (SOCKS5-Abwägungen).

Verifizieren Sie es von innerhalb des Containers

Nie annehmen, prüfen Sie die Exit-IP aus dem laufenden Container heraus:

Terminal window
docker exec -it my-scraper sh -c \
'curl -s http://ip-api.com/json | head -c 200'
# Erwarten Sie eine Residential-IP im Zielland, nicht Ihre Host-IP.

Gibt es Ihre Host-IP zurück, beachtet der Client die Umgebungsvariablen nicht (siehe Tabelle oben). Fügen Sie das als Start-Assertion in Staging hinzu, damit ein fehlkonfiguriertes Deploy laut scheitert, statt still von Ihrer Rechenzentrums-IP zu scrapen, und verifizieren Sie, dass NO_PROXY funktioniert, indem Sie bestätigen, dass ein interner Service-Aufruf den Proxy nicht durchquert. Breitere Messmethoden stehen in wie man Proxy-Geschwindigkeit, Erfolgsrate und Standortgenauigkeit testet.

Container-Fallstricke, die einen Nachmittag kosten

  • localhost meint den Container. Ein Proxy auf dem Host ist von innen nicht unter 127.0.0.1 erreichbar; nutzen Sie host.docker.internal (Docker Desktop) oder die Netzwerkadresse des Hosts.
  • Build- und Laufzeit-Proxies sind verschieden. Eines zu setzen setzt nicht das andere.
  • Env-Änderungen brauchen ein Recreate. Das Bearbeiten der Environment in Compose erfordert up --force-recreate, keinen Restart.
  • Der Ort der DNS-Auflösung variiert. Manche Clients lösen lokal auf, andere lassen den Proxy auflösen. Sehen geo-sensitive Ergebnisse falsch aus, ist das ein Verdächtiger.
  • Images können CA-Zertifikate vermissen. Slim-/Alpine-Basen brauchen manchmal installierte ca-certificates, damit HTTPS durch den Proxy validiert.
  • Pro-GB-Abrechnung ist pro Container. Zehn Repliken, die volle Seiten ziehen, multiplizieren Ihre Bandbreite mit zehn, Bandbreitenkosten senken gilt pro Replik.

FAQ

Warum nutzt mein Container den Proxy nicht, obwohl HTTP_PROXY gesetzt ist? Weil HTTP_PROXY eine Konvention ist, kein Routing. Die Bibliothek muss sie beachten. Nodes fetch und Headless-Browser tun das nicht; curl, Pythons requests und Gos Standard-Transport schon. Prüfen Sie zuerst Ihren Client, dann bestätigen Sie die Exit-IP aus dem Container heraus.

Sollte ich den Proxy im Dockerfile setzen? Nein. ENV/ARG-Werte bleiben in Image-Schichten bestehen und erscheinen in docker history, was Zugangsdaten an jeden leakt, der das Image ziehen kann. Injizieren Sie zur Laufzeit via Umgebungsvariablen aus einem Secret-Store, und nutzen Sie BuildKit-Secrets, falls Sie beim Build einen Proxy brauchen.

Wie verhindere ich, dass interner Traffic durch den Proxy geht? Setzen Sie NO_PROXY mit localhost, Ihren Servicenamen, internen Domains, privaten Bereichen und 169.254.169.254. Sonst tunnelt Datenbank- und Health-Check-Traffic über einen Residential-Exit hinaus, was langsam, fragil und abrechenbar ist.

Kann jeder Container eine andere IP oder ein anderes Land haben? Ja. Das Targeting ist im Proxy-Benutzernamen kodiert, also gibt eine andere Umgebungsvariable pro Container jedem sein eigenes Land oder seine Sticky Session, ohne zusätzliche Endpunkte. Für Rotation pro Anfrage setzen Sie den Proxy stattdessen im Anwendungscode.

Funktioniert der Proxy für docker build? Nur wenn Sie den Build-/Daemon-Proxy separat konfigurieren (~/.docker/config.json oder Build-Args). Laufzeit-Umgebungsvariablen des Containers beeinflussen Builds nicht, und Build-Zugangsdaten sollten nicht als ARG übergeben werden.

Fazit

Die meisten Docker-Proxy-Probleme laufen auf vier Dinge hinaus. Wissen Sie, wo die Konfiguration greift (Build vs Laufzeit, und welche Clients Umgebungsvariablen überhaupt beachten), setzen Sie NO_PROXY, damit interner Traffic intern bleibt, halten Sie Zugangsdaten aus Image-Schichten heraus und injizieren Sie sie zur Laufzeit, und entscheiden Sie bewusst, wie Identität auf Container abgebildet wird (Umgebungsvariablen für grobe Identität pro Container, Anwendungscode für Rotation pro Anfrage). Dann verifizieren Sie die Exit-IP aus dem Container heraus, statt der Konfiguration zu vertrauen.

Machen Sie das richtig, und containerisierte Erfassung ist im besten Sinne langweilig. Das Residential-Gateway hilft hier, weil das Targeting im Benutzernamen mitreist: ein Endpunkt, und Geo oder Session eines beliebigen Containers ist nur eine andere Umgebungsvariable. Die Poolqualität bestimmt weiterhin, wie oft Sie überhaupt retryen (IP-Reputation), und die Preisseite hat die Pro-GB-Tarife, wobei zu bedenken ist, dass die Bandbreite mit Ihrer Replikenzahl skaliert.

Bereit, loszulegen?

Testen Sie Shifters Residential-Proxys, 205M+ IPs, 195+ Länder, ab $0.75/GB.

Jetzt starten