Container in VM Can't Reach Internet? Fix NAT and DNS
Your container inside a VM can't reach the internet. The fix is usually a missing NAT rule or DNS setting. We'll show you the exact steps.
I know how frustrating this is. You set up a container inside a VM, and it just sits there with no internet. Ping fails, apt update fails, everything fails. But don't worry — I've seen this dozens of times. The fix is usually one of two things: a missing NAT rule or a broken DNS config. Let's fix it.
Quick Fix — Enable NAT on the VM
Most of the time, the VM's network adapter is set to "bridged" or "NAT" in your hypervisor. But the guest OS inside the VM doesn't forward traffic from the container. You need to turn on IP forwarding and set up a NAT rule. Here's how.
- First, log into your VM (not the container). Open a terminal as root or use sudo.
- Check if IP forwarding is on. Run this command:
If it returnscat /proc/sys/net/ipv4/ip_forward0, turn it on with:
I preferecho 1 | sudo tee /proc/sys/net/ipv4/ip_forwardteebecause it's cleaner than redirecting with>>. - Now, make it permanent. Edit
/etc/sysctl.confand add this line:
Save and exit.net.ipv4.ip_forward = 1 - Apply the change without rebooting:
You should seesudo sysctl -pnet.ipv4.ip_forward = 1in the output. - Now, add the NAT rule using iptables. This makes the VM act like a router for the container's traffic. Run:
Replacesudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADEeth0with your VM's external interface. Find it withip aorifconfig. On most Linux VMs, it'seth0orens33. - Save the iptables rules so they survive a reboot. On Ubuntu/Debian, use
iptables-save:
On CentOS/RHEL/Fedora, usesudo apt install iptables-persistent sudo netfilter-persistent saveservice iptables saveoriptables-save > /etc/sysconfig/iptables. - Test from inside the container. Run:
Or if it's an LXC container:docker exec -it your-container-name ping google.comlxc-attach -n container-name -- ping google.com. You should see replies now.
But Wait — Check DNS Too
If ping to an IP works (like ping 8.8.8.8) but ping to a domain fails, it's DNS. The container is using the wrong DNS server. Fix it by setting a known good DNS server in the container's config.
For Docker, edit /etc/docker/daemon.json and add:
{
"dns": ["8.8.8.8", "1.1.1.1"]
}Then restart Docker: sudo systemctl restart docker. For LXC, edit /etc/resolv.conf inside the container or set lxc.net.0.flags: up and lxc.net.0.link: lxcbr0 in the config file.
Why This Works
When you run a container, it usually gets a private IP (like 172.17.0.2 for Docker or 10.0.3.x for LXC). The VM's host sees this IP, but your router and the internet don't. The host VM needs to do network address translation (NAT) — it rewrites the container's private IP to the VM's own IP before sending traffic out. Without IP forwarding and a MASQUERADE rule, the packets from the container just vanish. The router receives them but doesn't know how to send replies back to the container's private address. Enabling forwarding and adding NAT fixes that.
Less Common Variations
Firewall Blocking Inside the VM
Some VM images come with firewalls that block forwarded traffic. On Ubuntu, ufw is common. Check with sudo ufw status. If it's active and blocking, allow forwarding:
sudo ufw default allow FORWARDOr add a rule for your container subnet: sudo ufw allow in on docker0 from 172.17.0.0/16 (adjust subnet to match your container's range).
Hyper-V Virtual Switch Issue
If you're on Hyper-V, the external virtual switch might not allow traffic from the container's subnet. Right-click the VM, go to Settings > Network Adapter, and check "Enable virtual LAN identification" — leave it blank unless you need VLANs. Also, make sure the VM's network adapter in the guest OS is set to "Automatic" or "DHCP."
Podman or Rootless Docker
Rootless containers don't use the docker0 bridge by default. They use slirp4netns or pasta for networking. If you're using rootless Docker, you need to enable --net=host or use a CNI plugin. Simpler fix: switch to rootful Docker for this VM. Run sudo dockerd and keep your containers there.
Preventing This from Happening Again
Set up your VM's network once and forget about it. On the VM host, add the iptables rule and IP forwarding to startup scripts. Create a file /etc/rc.local with:
#!/bin/bash
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
echo 1 > /proc/sys/net/ipv4/ip_forward
exit 0Make it executable: sudo chmod +x /etc/rc.local. On systemd systems, enable it: sudo systemctl enable rc-local. For Docker, always set DNS in the daemon.json. For LXC, use a static bridge config. Test internet from inside a new container right after you create it — saves you hours later.Was this solution helpful?