¿Trabajas con Docker en una red corporativa, en un servidor con acceso restringido o quieres que tus contenedores salgan a Internet a través de una IP específica? Sin la configuración adecuada del proxy, Docker simplemente no podrá descargar imágenes de Docker Hub o de cualquier otro registro. En este artículo, analizaremos todos los niveles de proxy: desde el demonio hasta un contenedor separado.
¿Por qué necesita Docker un proxy?
Docker es una herramienta que constantemente accede a recursos externos. En cada docker pull o docker build, el demonio accede a Docker Hub, GitHub Container Registry, Google Container Registry o a tu registro privado. Y aquí es donde comienzan los problemas.
Situaciones en las que no se puede prescindir de un proxy:
- Red corporativa con firewall — todo el tráfico debe pasar a través de un servidor proxy corporativo, de lo contrario, la conexión se bloquea.
- Restricciones geográficas — Docker Hub o registros específicos no están disponibles desde tu país o centro de datos.
- Servidor restringido sin acceso directo a Internet — VPS en un entorno cerrado, donde Internet solo está disponible a través de un gateway.
- Control del tráfico saliente de los contenedores — deseas que las aplicaciones dentro de los contenedores salgan a la red a través de una IP específica, por ejemplo, para scraping, solicitudes API o pruebas de contenido geodependiente.
- Anonimización de solicitudes — ocultar la IP real del servidor al acceder a servicios externos desde el contenedor.
- Limitación de tasa — Docker Hub limita el número de solicitudes de pull para usuarios anónimos (100 pulls cada 6 horas). A través de un proxy con rotación de IP se puede eludir esta restricción.
Es importante entender que Docker no es una sola aplicación, sino un sistema de varios componentes. El demonio (dockerd) vive en el host y realiza pulls de imágenes. Los contenedores son procesos aislados con sus propias configuraciones de red. Por lo tanto, la configuración del proxy para el demonio y para los contenedores son dos tareas diferentes que se abordan de manera distinta.
Tres niveles de proxy en Docker
Antes de meterse en las configuraciones, es necesario entender la arquitectura. En Docker hay tres niveles independientes, cada uno de los cuales requiere una configuración de proxy separada:
| Nivel | Qué hace | Dónde se configura |
|---|---|---|
| Demonio de Docker | Descarga imágenes (docker pull), accede a registros | sobrescritura de systemd o daemon.json |
| Construcción de Docker | Ejecuta comandos durante la construcción de la imagen (RUN apt-get, pip install, etc.) | ARG y ENV en Dockerfile o --build-arg |
| Contenedor en ejecución | Aplicación en ejecución que realiza solicitudes HTTP | ENV al ejecutar docker run o en docker-compose.yml |
Un error típico es configurar el proxy solo en un nivel y preguntarse por qué las imágenes aún no se descargan o la aplicación no ve el proxy. Vamos a analizar cada nivel en detalle.
Configuración del proxy para el demonio de Docker (pull de imágenes)
Esta es la configuración más importante si deseas descargar imágenes a través de un proxy. El demonio de Docker es un servicio del sistema que se inicia a través de systemd. No ve las variables de entorno de tu usuario o incluso de root. Debes pasar el proxy específicamente al entorno del servicio.
Método 1: a través de la sobrescritura de systemd (recomendado)
Crea un directorio para sobrescribir la configuración del servicio:
sudo mkdir -p /etc/systemd/system/docker.service.d
Crea un archivo /etc/systemd/system/docker.service.d/http-proxy.conf con el siguiente contenido:
[Service] Environment="HTTP_PROXY=http://username:password@proxy-host:port" Environment="HTTPS_PROXY=http://username:password@proxy-host:port" Environment="NO_PROXY=localhost,127.0.0.1,::1,your-private-registry.example.com"
Si el proxy no requiere autenticación, simplemente elimina username:password@. Después de crear el archivo, recarga la configuración de systemd y reinicia Docker:
sudo systemctl daemon-reload sudo systemctl restart docker
Verifica que la configuración se haya aplicado:
sudo systemctl show --property=Environment docker
En la salida deberían aparecer tus variables HTTP_PROXY y HTTPS_PROXY.
Método 2: a través de ~/.docker/config.json (Docker Desktop y versiones nuevas)
A partir de Docker Engine 23.0, hay una forma más conveniente: configurar el proxy a través del archivo de configuración del cliente. Crea o edita el archivo ~/.docker/config.json:
{
"proxies": {
"default": {
"httpProxy": "http://username:password@proxy-host:port",
"httpsProxy": "http://username:password@proxy-host:port",
"noProxy": "localhost,127.0.0.1,::1"
}
}
}
Este método es conveniente porque no requiere privilegios de root y no necesita reiniciar el demonio. La configuración se pasa automáticamente a los contenedores durante la construcción como argumentos de build. Sin embargo, para gestionar las solicitudes de pull del propio demonio, aún se necesita el método a través de systemd.
💡 Importante sobre NO_PROXY
Siempre agrega a NO_PROXY las direcciones de tus registros y servicios internos. De lo contrario, Docker intentará acceder a ellos a través del proxy y recibirá errores de conexión. Una lista típica: localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
Proxy dentro del contenedor durante la construcción (build-time)
Cuando Docker construye una imagen y ejecuta comandos como RUN apt-get install, RUN pip install o RUN npm install, estos comandos se ejecutan dentro de un contenedor temporal. Por defecto, no tiene acceso al proxy del host. Debes pasar explícitamente el proxy a través de argumentos de build.
Pasar el proxy a través de --build-arg
docker build \ --build-arg HTTP_PROXY=http://proxy-host:port \ --build-arg HTTPS_PROXY=http://proxy-host:port \ --build-arg NO_PROXY=localhost,127.0.0.1 \ -t my-image .
Configuración en Dockerfile
Si el proxy solo es necesario durante la construcción y no deseas que se incluya en la imagen final, utiliza ARG en lugar de ENV:
FROM ubuntu:22.04 # Declaramos los argumentos de construcción ARG HTTP_PROXY ARG HTTPS_PROXY ARG NO_PROXY # Los usamos en los comandos RUN apt-get update && apt-get install -y curl wget # Después de este bloque, el proxy no estará en ENV del contenedor
Si el proxy es necesario también en el contenedor en ejecución, utiliza ENV:
FROM python:3.11 ENV HTTP_PROXY=http://proxy-host:port ENV HTTPS_PROXY=http://proxy-host:port ENV NO_PROXY=localhost,127.0.0.1 RUN pip install requests beautifulsoup4 CMD ["python", "app.py"]
⚠️ Atención: seguridad
No codifiques el nombre de usuario y la contraseña del proxy directamente en el Dockerfile si la imagen será pública o se almacenará en un repositorio. Utiliza --build-arg y pasa los valores desde las variables de entorno del sistema CI/CD. El comando docker history puede mostrar los valores de ARG, por lo que para secretos utiliza los secretos de Docker BuildKit.
Proxy para contenedores en ejecución (runtime)
Este es el escenario más común para aplicaciones que realizan solicitudes HTTP durante su ejecución: scrapers, bots, microservicios que necesitan salir a través de una IP específica. Aquí la configuración es muy sencilla: solo necesitas pasar las variables de entorno al iniciar el contenedor.
A través de docker run
docker run \ -e HTTP_PROXY=http://username:password@proxy-host:port \ -e HTTPS_PROXY=http://username:password@proxy-host:port \ -e NO_PROXY=localhost,127.0.0.1 \ my-image
La mayoría de las bibliotecas HTTP populares capturan automáticamente las variables HTTP_PROXY y HTTPS_PROXY: Python requests, curl, wget, net/http de Go, Node.js https-proxy-agent y otras. No necesitas escribir nada adicional en el código.
A través del archivo .env
Es más conveniente almacenar la configuración en un archivo .env y pasarlo completo:
# Archivo .env HTTP_PROXY=http://username:password@proxy-host:port HTTPS_PROXY=http://username:password@proxy-host:port NO_PROXY=localhost,127.0.0.1
docker run --env-file .env my-image
Proxy SOCKS5 en el contenedor
Si utilizas un proxy SOCKS5 (por ejemplo, los proxies residenciales suelen soportar este protocolo), la sintaxis es un poco diferente:
docker run \ -e ALL_PROXY=socks5://username:password@proxy-host:port \ my-image
Ten en cuenta: no todas las bibliotecas soportan SOCKS5 a través de la variable de entorno sin dependencias adicionales. Python requests requiere la instalación de requests[socks], curl debe ser compilado con soporte para libcurl-socks.
Proxy en Docker Compose
En proyectos reales, rara vez se utiliza el simple docker run. La mayoría de las veces se trabaja con Docker Compose, donde varios servicios están descritos en un solo archivo. La configuración del proxy se realiza aquí a nivel de cada servicio o a través de un archivo de variables.
Opción 1: environment en docker-compose.yml
version: '3.8'
services:
scraper:
image: my-scraper:latest
environment:
- HTTP_PROXY=http://username:password@proxy-host:port
- HTTPS_PROXY=http://username:password@proxy-host:port
- NO_PROXY=localhost,127.0.0.1
restart: unless-stopped
api:
image: my-api:latest
# Este servicio sin proxy — solo accede a recursos internos
ports:
- "8080:8080"
Opción 2: a través del archivo .env (recomendado)
Crea un .env en el directorio con docker-compose.yml. Docker Compose captura automáticamente este archivo:
# .env PROXY_HOST=proxy-host PROXY_PORT=8080 PROXY_USER=username PROXY_PASS=password
version: '3.8'
services:
scraper:
image: my-scraper:latest
environment:
- HTTP_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
- HTTPS_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
- NO_PROXY=localhost,127.0.0.1
Proxy durante la construcción en Compose
Si en Compose hay una sección build y necesitas pasar el proxy durante la construcción:
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
HTTP_PROXY: http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
HTTPS_PROXY: http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
environment:
- HTTP_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
- HTTPS_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
Qué tipo de proxy elegir para Docker
La elección del tipo de proxy depende de la tarea. Para entornos Docker, hay tres tipos principales, cada uno con su propio nicho:
| Tipo de proxy | Velocidad | Anonimato | Mejor escenario para Docker |
|---|---|---|---|
| Data Center | ⚡ Alta | Media | Pull de imágenes, pipelines de CI/CD, eludir limitaciones de tasa de Docker Hub |
| Residenciales | 🔄 Media | Alta | Scraping de sitios con protección, API con restricciones geográficas, pruebas |
| Móviles | 🔄 Media | Máxima | Aplicaciones que trabajan con API móviles (Instagram, TikTok) |
Para pull de imágenes y CI/CD, los proxies de data center son los más óptimos, ya que ofrecen la máxima velocidad al descargar grandes imágenes (varios gigabytes) y una conexión estable para construcciones largas.
Para contenedores scraper, que necesitan eludir la protección de los sitios y parecer un usuario normal, son más adecuados los proxies residenciales. Tienen IP de usuarios reales, lo que reduce la probabilidad de bloqueo incluso con solicitudes intensivas.
Para aplicaciones que trabajan con plataformas móviles — Instagram Graph API, TikTok API, versiones móviles de servicios — vale la pena considerar los proxies móviles. Utilizan IP de operadores de telefonía móvil y generan mínimas sospechas en los sistemas anti-bot.
Protocolos: HTTP vs SOCKS5
El demonio de Docker solo soporta proxies HTTP/HTTPS — SOCKS5 no funciona para pull de imágenes. Si tienes un proxy SOCKS5 y necesitas descargar imágenes, tendrás que usar un convertidor local como privoxy o microsocks, que aceptará HTTP y lo proxeará a través de SOCKS5.
Para contenedores en ejecución, la situación es mejor: la mayoría de las bibliotecas HTTP soportan SOCKS5 directamente a través de la variable ALL_PROXY=socks5://....
Errores comunes y cómo solucionarlos
Analicemos los problemas más comunes que se encuentran al configurar un proxy en Docker:
Error 1: El proxy está configurado en el host, pero Docker no lo ve
Síntoma:
Error de respuesta del demonio: Get "https://registry-1.docker.io/v2/": dial tcp: connection refused
Causa: El demonio de Docker se ejecuta como un servicio del sistema y no hereda las variables de entorno del usuario actual, incluso si has establecido export HTTPS_PROXY=... en la terminal.
Solución: Configura el proxy a través de la sobrescritura de systemd (método 1 de la sección anterior). Asegúrate de ejecutar systemctl daemon-reload && systemctl restart docker.
Error 2: apt-get / pip / npm no funcionan durante la construcción
Síntoma:
Err:1 http://archive.ubuntu.com/ubuntu focal InRelease
No se pudo conectar a archive.ubuntu.com:80
Causa: La configuración del proxy para el demonio no se aplica automáticamente a los comandos dentro de RUN en el Dockerfile. Son dos contextos diferentes.
Solución: Pasa el proxy a través de --build-arg o utiliza ~/.docker/config.json con la sección proxies (Docker 23.0+, pasa automáticamente los argumentos durante la construcción).
Error 3: Servicios internos no accesibles a través del proxy
Síntoma:
curl: (7) Falló al conectar con internal-service.local puerto 8080: Conexión rechazada
Causa: El proxy intenta proxear solicitudes a direcciones internas que no son accesibles desde el exterior.
Solución: Agrega todos los dominios internos y subredes a NO_PROXY. Para redes corporativas, una lista típica es: localhost,127.0.0.1,::1,.internal,.local,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
Error 4: La autenticación del proxy no funciona — caracteres especiales en la contraseña
Si la contraseña contiene caracteres especiales (@, #, %), la URL se analiza incorrectamente.
Solución: Codifica la contraseña en URL-encoding. Por ejemplo, p@ss#word → p%40ss%23word. En Python, puedes obtener rápidamente la versión codificada:
python3 -c "from urllib.parse import quote; print(quote('p@ss#word', safe=''))"
# Salida: p%40ss%23word
Error 5: El proxy funciona, pero el certificado SSL no se verifica
Los proxies corporativos a menudo realizan inspección SSL (MITM) y sustituyen certificados. Docker emite el siguiente error:
x509: certificado firmado por una autoridad desconocida
Solución: Agrega el certificado raíz corporativo a los certificados de confianza en el host y reinicia Docker. En Ubuntu/Debian:
sudo cp corporate-ca.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates sudo systemctl restart docker
Error 6: Kubernetes / Docker Swarm — el proxy no funciona en todos los nodos
En entornos de clúster, debes configurar el proxy en cada nodo por separado. Para Kubernetes, también necesitas configurar las variables de entorno en los manifiestos de pods o a través de ConfigMap. Esto se puede automatizar a través de Ansible u otras herramientas de gestión de configuración.
Conclusión
Configurar un proxy en Docker no es una sola configuración, sino tres niveles independientes: el demonio para pull de imágenes, build-time para la construcción y runtime para contenedores en ejecución. Al entender esta arquitectura, podrás gestionar de manera flexible qué tráfico pasa a través del proxy y cuál va directamente.
Breve lista de verificación para una configuración correcta:
- ✅ Para pull de imágenes — configura la sobrescritura de systemd con las variables HTTP_PROXY y HTTPS_PROXY
- ✅ Para la construcción — pasa el proxy a través de
--build-argo~/.docker/config.json - ✅ Para runtime — usa variables de entorno a través de
-eo--env-file - ✅ Siempre configura NO_PROXY para direcciones internas
- ✅ No almacenes contraseñas de proxy en el Dockerfile — utiliza build-args y archivos .env
- ✅ Para pipelines de CI/CD, elige proxies de data center rápidos
- ✅ Para contenedores scraper — proxies residenciales o móviles
Si tus aplicaciones en contenedores realizan solicitudes a servicios externos, scrapean datos o trabajan con API que tienen restricciones geográficas, recomendamos utilizar proxies residenciales — ofrecen un alto nivel de confianza por parte de los servicios y un riesgo mínimo de bloqueos incluso con un trabajo intensivo.