Connecting to External Services
LEVEL 0
The Problem
Your containers don’t exist in isolation. They need to connect to the outside world:
- External APIs: Stripe for payments, Twilio for SMS, AWS services
- Cloud databases: RDS, MongoDB Atlas, Firebase
- Third-party services: CDNs, monitoring services, analytics
- The general internet: Downloading packages, fetching data
A container running in a private Docker network (172.17.x.x) can’t be directly routed on the public internet. So how does a container with a private IP make HTTPS requests to Google? How does it connect to an external database?
LEVEL 1
The Concept — The Corporate Proxy
The Concept
Imagine you work in a large corporation with a private internal network.
Your computer has a private IP address (10.0.5.42). You can’t connect directly to the internet—your IP isn’t routable outside the company.
When you browse to google.com, your request goes through a proxy server. The proxy:
- Receives your request from your private IP
- Forwards it to google.com using the company’s public IP
- Receives Google’s response
- Sends it back to you
From Google’s perspective, the request came from the company’s public IP, not your private IP.
Docker does the same thing using NAT (Network Address Translation). When a container makes an outbound connection, the host acts as the proxy, translating the container’s private IP to the host’s public IP.
LEVEL 2
The Mechanics — How NAT Works for Containers
The Mechanics
Outbound connections (container → internet):
- Container (172.17.0.2) makes request to api.stripe.com:443
- Packet leaves container, hits docker0 bridge
- Host’s iptables NAT rules translate: source IP from 172.17.0.2 → host’s public IP
- Packet is sent to internet with host’s IP as source
- Stripe responds to host’s IP
- Host translates back: destination IP from host’s IP → 172.17.0.2
- Packet forwarded back to container
From Stripe’s perspective: The request came from the host’s public IP. They don’t know a container exists.
See the NAT rules:
sudo iptables -t nat -L -n -v
Look for the POSTROUTING chain with MASQUERADE rules—these perform NAT for outbound container traffic.
LEVEL 3
Connecting to External Databases and Services
Scenario: Container connecting to AWS RDS database
Your application runs in a container. Your database is on AWS RDS (not in a container).
docker run -d \
--name api \
-e DATABASE_HOST=my-db.abc123.us-east-1.rds.amazonaws.com \
-e DATABASE_PORT=5432 \
-e DATABASE_USER=admin \
-e DATABASE_PASSWORD=secret \
my-api-image
The container resolves the RDS hostname via DNS and connects directly. The connection is NATed through the host.
Scenario: Container connecting to external HTTP API
docker run -d \
--name payment-processor \
-e STRIPE_API_KEY=sk_live_... \
-e STRIPE_API_URL=https://api.stripe.com \
payment-service
The container makes HTTPS requests to Stripe. NAT handles the translation.
DNS resolution for external services:
By default, containers use the host’s DNS settings.
View container’s DNS configuration:
docker exec container-name cat /etc/resolv.conf
You’ll see Docker’s DNS server (127.0.0.11) listed first, followed by the host’s DNS servers as fallbacks.
Custom DNS servers:
If you need specific DNS servers for external resolution:
# Per container
docker run --dns 8.8.8.8 --dns 1.1.1.1 my-image
# Globally in /etc/docker/daemon.json
{
"dns": ["8.8.8.8", "1.1.1.1"]
}
LEVEL 4
Accessing Host Services from Containers
What if the container needs to connect to a service running on the host itself (not in a container)?
Problem: From inside the container, localhost refers to the container itself, not the host.
Solution: Use a special DNS name that resolves to the host:
On Linux: host.docker.internal (requires Docker 20.10+)
On Mac/Windows: host.docker.internal works by default
# Container connecting to a service on the host
docker run --add-host=host.docker.internal:host-gateway my-app
# Inside the container, connect to host:
curl http://host.docker.internal:8080
Alternative: Use the host’s actual IP address on the Docker network:
# Find the docker0 bridge IP (usually 172.17.0.1)
ip addr show docker0 | grep inet
# Connect to that IP from container
curl http://172.17.0.1:8080
LEVEL 5
Handling Firewalls and Network Restrictions
Corporate proxies:
If your Docker host is behind a corporate HTTP proxy:
Set proxy environment variables for containers:
docker run \
-e HTTP_PROXY=http://proxy.corp.com:8080 \
-e HTTPS_PROXY=http://proxy.corp.com:8080 \
-e NO_PROXY=localhost,127.0.0.1,::1 \
my-image
Or configure Docker daemon globally in /etc/docker/daemon.json:
{
"proxies": {
"default": {
"httpProxy": "http://proxy.corp.com:8080",
"httpsProxy": "http://proxy.corp.com:8080",
"noProxy": "localhost,127.0.0.1"
}
}
}
Accessing services in a VPN:
If your host is connected to a VPN and containers need to access VPN-only resources:
Containers automatically use the host’s routing table. If the host can reach the VPN, containers can too (via NAT).