VPS Tutorials
Docker Containers on a VPS Can't Reach the Network (2026): DNS Resolution, bridge Networks, and 127.0.0.11 Troubleshooting
When Docker containers on a VPS can't reach the internet, apt reports Temporary failure resolving, or services can't resolve each other by name, the common causes include the default bridge not supporting automatic service-name resolution, problems with Docker's built-in DNS at 127.0.0.11, interference from the host's systemd-resolved, incorrect DNS settings in daemon.json, and modified firewall or forwarding rules. This article troubleshoots layer by layer: DNS inside the container, Docker networking, host forwarding, firewalls, and Compose configuration.

"Docker containers can't reach the network" is often not one problem but three mixed together:
- Containers can't reach public IPs.
- Containers can reach IPs but can't resolve domain names.
- Containers can't reach each other by service name.
These three symptoms require completely different troubleshooting directions. Many people see Temporary failure resolving and go change the host DNS; they see containers can't ping each other and go change Nginx, and in the end it gets messier and messier. Docker networking must first be separated by layer: the container's own DNS, the Docker bridge network, host forwarding rules, firewalls, and Compose networks each sit at a different layer.
This article troubleshoots the most common Docker network failures on a VPS: starting with tests inside the container, then looking at 127.0.0.11, the default bridge versus custom bridges, docker network inspect, daemon.json DNS, firewalls, and systemd-resolved.
Enter the container:
docker exec -it app sh
If the container doesn't have sh, use instead:
docker exec -it app bash
First test a public IP:
ping -c 3 1.1.1.1
Then test a domain name:
ping -c 3 google.com
If IPs work but domain names don't, check DNS first.
If IPs also fail, check Docker networking, NAT, forwarding, and firewalls first.
If the public internet works but containers can't reach each other by service name, focus on the Docker network type: you may still be using the default bridge instead of a user-defined bridge or the default Compose network.
Run inside the container:
cat /etc/resolv.conf
If the container is on a user-defined bridge or a Docker Compose network, the common result is:
nameserver 127.0.0.11
options ndots:0
127.0.0.11 is Docker's built-in DNS. It handles two things:
- Forwarding external DNS queries for the container.
- Resolving container names and service names on user-defined networks.
If what you see is not 127.0.0.11 but some local address on the host, such as 127.0.0.53, beware of systemd-resolved-related problems. Inside the container, 127.0.0.53 points to the container itself, not the host's resolved service, and will usually fail to resolve.
This is the most easily overlooked point in Docker networking.
A container started directly like this:
docker run -d --name app nginx
joins Docker's default bridge network by default. On the default bridge, containers usually cannot automatically resolve each other by container name.
It is better to create a custom network:
docker network create app-net
Then start the container:
docker run -d --name redis --network app-net redis:7
docker run -d --name app --network app-net nginx
On the same custom bridge network, containers can reach each other by name:
redis:6379
Docker Compose also creates a custom network for the project by default, so service names in Compose can usually resolve each other. For example:
services:
app:
image: my-app
depends_on:
- redis
redis:
image: redis:7
To reach Redis from app, write:
redis:6379
Not 127.0.0.1:6379. Inside a container, 127.0.0.1 refers to the container itself, not the host and not another container.
Don't just look at docker ps. Network problems require network inspect.
List networks:
docker network ls
Inspect a network:
docker network inspect app-net
Focus on:
- Whether the containers are really on the same network.
- Whether container names and aliases exist.
- Whether the Subnet / Gateway conflicts with the host network.
- Whether
host,none, or an old defaultbridgewas used by accident.
If a container isn't attached to the right network, you can connect it temporarily:
docker network connect app-net app
Long-term configuration should be written back into the Compose file rather than maintained by manual connect.
First install a debugging tool inside the container. Debian/Ubuntu containers:
apt update
apt install -y dnsutils iputils-ping curl
Alpine containers:
apk add --no-cache bind-tools iputils curl
If apt update itself can't run because of DNS failure, you can temporarily start a test container:
docker run --rm -it --dns 1.1.1.1 debian:12 bash
Test resolution:
nslookup debian.org
nslookup debian.org 1.1.1.1
If resolution works when you specify 1.1.1.1 but fails by default, the Docker default DNS path has a problem.
You can specify DNS in /etc/docker/daemon.json:
{
"dns": ["1.1.1.1", "8.8.8.8"]
}
Then restart Docker:
sudo systemctl restart docker
Note: restarting Docker affects running containers. On production machines, assess the impact first and schedule a maintenance window if necessary.
Common on Ubuntu: the host's /etc/resolv.conf points to:
nameserver 127.0.0.53
This is the host's local systemd-resolved stub resolver. Docker usually handles this situation, but with certain images, older Docker versions, manually modified resolv.conf, or daemon configuration, containers may receive DNS settings that don't suit them.
First check on the host:
resolvectl status
ls -l /etc/resolv.conf
cat /etc/resolv.conf
If you previously modified /etc/resolv.conf by hand to fix DNS, first confirm you haven't broken the host's DNS. If the host's own DNS resolution has problems, handle that first: What to do when VPS DNS resolution fails.
If containers can't even reach public IPs, check whether the host has IPv4 forwarding enabled:
sysctl net.ipv4.ip_forward
It should normally be:
net.ipv4.ip_forward = 1
Docker usually sets this automatically. If it is 0, you can enable it temporarily:
sudo sysctl -w net.ipv4.ip_forward=1
Next, check whether Docker created NAT rules. On iptables systems:
sudo iptables -t nat -S | grep DOCKER
On nftables systems:
sudo nft list ruleset | grep -i docker
If you have installed UFW, firewalld, nftables rule scripts, or manually cleaned up iptables, you may have broken Docker's forwarding and NAT rules.
Common symptoms of this situation:
- Containers start.
- Port mappings appear to exist.
- Containers fail to reach the internet.
- Things recover briefly after restarting Docker, then get overwritten again by firewall scripts.
Many people on Ubuntu VPS use UFW to manage ports, but Docker operates directly on iptables/nftables. The result: you think UFW blocked a port, but Docker-mapped ports are still open to the outside; or you changed UFW's default forwarding policy and container egress fails.
First check UFW status:
sudo ufw status verbose
Then check the default forwarding policy:
grep DEFAULT_FORWARD_POLICY /etc/default/ufw
If it is:
DEFAULT_FORWARD_POLICY="DROP"
it may affect container forwarding. Many tutorials tell you to change it to ACCEPT, but that widens the forwarding surface, so evaluate it against your actual rules in production.
A more stable approach:
- Don't casually flush rules created by Docker.
- Check container egress after firewall scripts reload.
- Expose ports through a unified reverse proxy entry point instead of
-p 0.0.0.0:xxxx:xxxxeverywhere.
If your problem is "the service started but can't be reached from the internet," see: VPS service started but unreachable from the internet: ports, firewalls, security groups, listen addresses.
When service name resolution fails in Compose, it's usually not a broken DNS server but incorrect configuration.
Common mistake one: writing localhost.
REDIS_HOST=localhost
Inside a container, localhost is the current container. Write the service name instead:
REDIS_HOST=redis
Common mistake two: two services are not on the same network.
services:
app:
networks:
- frontend
redis:
networks:
- backend
In this case app cannot resolve redis by default. They need to share a network, or you need to plan network boundaries explicitly.
Common mistake three: treating container_name as an extension capability.
container_name is not required, and often it makes multiple replicas, project isolation, and naming conflicts more troublesome. Compose default service names are already sufficient as internal DNS names.
When containers can't reach the network or resolve domain names, check in this order:
- Test IPs inside the container first:
ping 1.1.1.1. - Then test domain names:
ping debian.orgornslookup debian.org. - Check the container's
/etc/resolv.confto confirm whether it uses127.0.0.11. - Use
docker network lsanddocker network inspectto see whether the container is on the right network. - If containers can't reach each other, switch to a custom bridge or Compose network first.
- If DNS fails, temporarily use
--dns 1.1.1.1to verify whether it's a DNS path problem. - If necessary, configure
dnsin/etc/docker/daemon.json. - Check the host's
resolvectl statusand/etc/resolv.conf. - If IPs also fail, check
net.ipv4.ip_forwardand Docker NAT rules. - Check whether UFW/firewalld/nftables is breaking Docker forwarding.
When Docker containers on a VPS can't reach the network, don't stare at a single error. First distinguish whether public IPs fail, DNS fails, or service name resolution between containers fails.
If it's container-to-container access, first confirm whether they're on the same custom bridge or Compose network; if it's external domain resolution, focus on 127.0.0.11, host DNS, and daemon.json; if IPs also fail, then check forwarding, NAT, and firewalls. Once you separate these layers, Docker network problems will no longer feel like a black box.