You SSH into your VPS, run apt update or curl a URL, and get slapped with "Temporary failure in name resolution." The domain records are fine, the site works from your browser — but the server itself can't look anything up. This is an OS-level DNS resolver problem, not a domain zone problem, and it's one of the more confusing errors on a fresh Linux VPS because the fix depends entirely on which resolver stack your distro is running.
Symptom
You'll see one of these, usually right after a reboot, a network config change, or moving to a new VPS image:
curl: (6) Could not resolve host: github.com
ping: google.com: Temporary failure in name resolution
E: Failed to fetch ... Temporary failure resolving 'archive.ubuntu.com'Meanwhile ping 8.8.8.8 works fine — so the network itself is up, only name resolution is broken. That's the tell.
Why This Happens
A handful of causes account for almost every case:
- Broken
/etc/resolv.confsymlink. Modern Ubuntu/Debian manage DNS throughsystemd-resolved, and/etc/resolv.confis supposed to be a symlink to a stub file it generates. If that symlink gets deleted or replaced with a static file (common after a cloud-init run or a manual edit), resolution breaks silently. systemd-resolvednot running. A crashed or disabled service means nothing is listening on the stub resolver at127.0.0.53.- Upstream resolver unreachable. Your provider's default DNS (often the gateway IP) is down, rate-limiting you, or blocked by a network change.
- Outbound port 53 blocked. UFW, iptables, or a cloud firewall rule that only opened 80/443/22 will silently kill DNS lookups since they ride on UDP/TCP port 53.
- Netplan misconfiguration. A netplan YAML with no
nameserversblock, or a syntax error that fails silently, leaves the interface without a resolver. - Docker's own resolver. Containers get their DNS from the Docker daemon, not the host directly — a host-level fix doesn't always fix containers, and vice versa.
Quick Diagnosis
Run these in order — each one narrows down where the chain breaks:
| Command | What it tells you |
|---|---|
ping -c2 8.8.8.8 | Is raw network connectivity even up? If this fails too, it's not a DNS problem. |
cat /etc/resolv.conf | Is it a symlink to /run/systemd/resolve/stub-resolv.conf, or a stale static file? |
systemctl status systemd-resolved | Is the resolver service actually running? |
resolvectl status | Which DNS servers is each interface actually using? |
dig google.com @8.8.8.8 | Does resolution work against a known-good resolver directly (bypasses local config)? |
sudo ufw status verbose | Is outbound port 53 allowed? |
If dig @8.8.8.8 works but plain dig google.com doesn't, the problem is 100% local — broken resolv.conf, dead resolved, or a firewall rule. If even the direct query fails, it's upstream or the firewall.
Fix 1: Repair the resolv.conf Symlink
On Ubuntu/Debian with systemd-resolved, check what /etc/resolv.conf actually is:
ls -la /etc/resolv.confIf it's a regular file instead of a symlink (or points to the wrong target), fix it:
sudo rm /etc/resolv.conf
sudo ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolvedTest immediately with curl -I https://github.com. This alone fixes the majority of "worked yesterday, broken today" cases after a reboot or an unattended-upgrades run.
Fix 2: Restart or Re-enable systemd-resolved
sudo systemctl enable --now systemd-resolved
sudo systemctl restart systemd-resolved
resolvectl statusIf the service refuses to start, check journalctl -u systemd-resolved -n 50 for the actual error — a corrupted config in /etc/systemd/resolved.conf is the usual culprit.
Fix 3: Set Reliable Resolvers via Netplan
Provider-default DNS can be flaky, especially on budget VPS plans. Point the interface at known-good public resolvers directly. Edit your netplan file (typically /etc/netplan/50-cloud-init.yaml or similar):
network:
version: 2
ethernets:
eth0:
dhcp4: true
nameservers:
addresses: [1.1.1.1, 8.8.8.8]Apply it:
sudo netplan applyOn distros without netplan (older Debian, CentOS-based systems using NetworkManager), the equivalent is setting DNS in /etc/sysconfig/network-scripts/ifcfg-eth0 (DNS1=1.1.1.1) or via nmcli con mod eth0 ipv4.dns "1.1.1.1 8.8.8.8".
Fix 4: Open Outbound Port 53
If UFW or iptables is scoped too tight, DNS gets silently dropped. For UFW:
sudo ufw allow out 53/udp
sudo ufw allow out 53/tcp
sudo ufw reloadFor cloud-provider firewalls (security groups, network ACLs) check that outbound isn't restricted to a whitelist that forgot DNS — this is easy to miss when someone locks down a server following a hardening checklist and only thinks about inbound rules.
Fix 5: Docker Containers Still Failing?
Fixing the host doesn't always fix containers. If docker exec -it mycontainer ping google.com fails while the host resolves fine, restart Docker after the host fix so it re-reads /etc/resolv.conf:
sudo systemctl restart dockerOr set explicit DNS for a container/compose stack:
services:
app:
dns:
- 1.1.1.1
- 8.8.8.8Prevention
- Pin explicit public resolvers (
1.1.1.1,8.8.8.8, or your own) in netplan instead of relying on whatever the provider hands out over DHCP — it's one less thing that can silently rot. - Never hand-edit
/etc/resolv.confdirectly on a systemd-resolved system — edits get overwritten on the next network event and leave you confused later. Change netplan or resolved.conf instead. - Add a simple cron or monitoring check that alerts if
dig +short getwebup.inreturns empty — this catches resolver breakage before it takes down cron jobs, backups, or Let's Encrypt renewals that silently depend on working DNS. - After any firewall change, explicitly re-test outbound DNS, not just inbound web traffic — it's the rule people forget to verify.
Prevention Checklist
- Confirm
/etc/resolv.confis a symlink to the systemd-resolved stub (not a static file). - Set explicit nameservers in netplan rather than trusting DHCP defaults.
- Allow outbound UDP/TCP 53 in every firewall layer (UFW, cloud security group).
- Restart Docker after any host-level DNS fix.
- Add a scheduled
digcheck so a broken resolver gets caught before certbot or cron jobs fail.