How to Change the DNS Server on Linux So It Survives a Reboot

Featured banner: three configuration lanes and a router-supplied lane converging on one resolver, which answers to two upstream servers

Changing the DNS server on Linux sticks only when the command lands where the component that owns the setting reads it.

On Ubuntu, Fedora, Debian, and Arch that component is systemd-resolved, and /etc/resolv.conf is a symlink it rebuilds. NetworkManager and netplan hold the same value on the machines where they own the connection, which is why a change made in the wrong place is gone by the next boot.

Where Linux keeps its DNS setting

Every name lookup on a systemd machine walks the same path. An application calls getaddrinfo, glibc consults /etc/nsswitch.conf, and a stub listener on 127.0.0.53 hands the question to systemd-resolved.

That stub is why an edit to /etc/resolv.conf answers a few queries and then stops answering them.

How an interface gets its address, and which file names the resolver that serves it, are the two halves of the same picture. The ipconfig and DNS walkthrough covers the first half.

Diagram of a DNS query passing from an application through glibc to systemd-resolved and out to an upstream server
Every lookup on a systemd machine passes through the same stub, whichever component wrote the upstream address.

systemd-resolved holds two layers of upstream servers. A per-link server belongs to one interface, and the global value set through the DNS option in resolved.conf applies only when no link supplies a server of its own.

NetworkManager, netplan, and resolved.conf are the three components that can write either layer. The manager keeps a copy on the connection profile, netplan writes one into a systemd-networkd file, and resolved.conf holds the global value, so the layer that answers is the one written last.

The address your machine asks is the one from the last component that wrote it, and nothing in the resolver output names that component.

I checked which component owns the setting before opening any configuration file. The component that answers is not always the one the filename suggests.

What you need before you change anything

A root shell covers every command here, and you need the name of the interface or connection you are changing.

Every command on this page runs through sudo. The sudo walkthrough covers what that elevation changes if the password prompt is new to you.

Finding an address on a machine is a separate exercise from telling it which resolver to use, and the IP address walkthrough covers that one. Both answers end up in the same file.

Pick the resolver before you pick the command. The four in the table are the ones worth knowing, and the difference that matters is whether the operator filters domains for you.

ResolverIPv4 addressesWhat it does with a query
Cloudflare1.1.1.1 and 1.0.0.1Resolves everything, no filtering
Google Public DNS8.8.8.8 and 8.8.4.4Resolves everything, no filtering
Quad99.9.9.9 and 149.112.112.112Blocks domains on a threat list, and supports DNS over TLS
AdGuard DNS94.140.14.14 and 94.140.15.15Blocks ads and trackers, with separate unfiltered and family addresses

I checked each pair against the operator’s own page rather than a list copied onto another blog, because these addresses move and the vendor page is the one that stays current.

An address that filters sits between you and a domain you might need, so a blocked site is the first symptom to check if a page stops loading after the change.

How to change the DNS server on Linux

The command depends on what manages the connection, and this table is the shortest way to find yours. Read the left column first, because that is the one you can check on the machine in front of you.

What you find on the machineWhat owns the settingWhere the change goes
No NetworkManager, a YAML file in /etc/netplannetplan and systemd-networkdThe netplan section
nmcli connection show lists the connectionNetworkManagerThe NetworkManager section
resolved is running and no manager owns the linksystemd-resolved aloneThe drop-in section
A container, a chroot, or a rescue shellWhatever wrote /etc/resolv.conf lastThe resolv.conf section

Check which resolver is answering now

resolvectl status prints the global settings first, then one block per interface.

resolvectl status
resolvectl status showing the DNS server the active link is using
resolvectl status names the link and the server it is using, which is the state every later command has to change.

The link carrying queries shows a DNS scope and a plus sign in front of DefaultRoute. Its DNS Servers line is the server it forwards to.

Current Scopes is the field that decides whether a link feeds resolution at all. A link with none is ignored, which is why a machine with a container bridge or a stopped tunnel can carry addresses that never answer anything.

I read the scope line before the address list, because a link with no scope shows servers that nothing asks.

A bare dig answers from 127.0.0.53 rather than from that server, because dig asks the local stub the way every other program on the machine does. The SERVER line in the output is the proof.

dig example.com

That line names the stub and not the upstream address you are about to change, and the dig walkthrough breaks down the sections that follow it.

/etc/resolv.conf is the file many DNS guides open first, and on a systemd machine it is a symlink into /run.

resolv.conf shown as a symlink into the run directory
The resolv.conf path is a symlink into /run, and the file it points at says not to edit it.

The comment at the top of the target file says not to edit it. The symlink is why an edit appears to work for a few minutes after you save it.

Set the DNS server for one connection

resolvectl dns changes the server on a single link at runtime, which is the fastest way to try a resolver before you commit to it anywhere.

I tried the link-level setting first for every trial, because it is thrown away by the next restart and a configuration file is not. The read-back is the step that confirms it landed.

sudo resolvectl dns eth0 9.9.9.9 149.112.112.112

Run the same command with only the link name and it prints what is set, which is the read-back that confirms the addresses were accepted.

resolvectl dns eth0

A query names the link that answered, and then it reports whether the answer came from the network or from the cache.

resolvectl query example.com
resolvectl dns setting 9.9.9.9 on eth0, the read-back, and the query that follows
Setting the server on the link, reading the value back, and letting a query confirm which server answered.

This setting lives in memory only, and the link drops it at the next restart. That is what makes it safe to test.

Keep the change with a systemd-resolved drop-in

The global value lives in resolved.conf. A drop-in file under resolved.conf.d overrides the main file without editing it.

sudo mkdir -p /etc/systemd/resolved.conf.d
sudo tee /etc/systemd/resolved.conf.d/99-dns.conf >/dev/null <<'EOF'
[Resolve]
DNS=9.9.9.9 149.112.112.112
FallbackDNS=1.1.1.1 1.0.0.1
EOF

The DNS line is the pair resolved forwards to, and FallbackDNS is the list it falls back on when no link and no DNS entry supplies a server at all.

Restart the service so the new file is read, then read the global block back.

sudo systemctl restart systemd-resolved
resolvectl status after a resolved.conf drop-in restart, showing global DNS servers 9.9.9.9 and 149.112.112.112
After the drop-in and a restart, the global servers are the two the file names.

The global block now carries both addresses, and the link shows no DNS scope, which is the state where the global entry is the one in use.

I checked both blocks after the restart. A global value that never gets used looks the same as one that works.

Restarting the resolver is the smaller version of the same move the networking restart guide describes for the stack underneath it, because only the name resolution steps aside.

Keep the change with NetworkManager

The profile is what wins. NetworkManager keeps a second copy on the connection, and it writes that copy into resolved each time the connection comes up.

I opened the profile list first, because the modify commands need the exact connection name and the default one carries a space in it.

nmcli connection show
sudo nmcli connection modify "Wired connection 1" ipv4.dns "9.9.9.9 149.112.112.112"
sudo nmcli connection modify "Wired connection 1" ipv4.ignore-auto-dns yes
sudo nmcli connection up "Wired connection 1"

The second line is the one that matters, because a profile which still accepts the DHCP resolver hands it back to the router on the next connect. The nmcli reference lists ipv4.ignore-auto-dns next to the connection settings it belongs to.

nmcli -g ipv4.dns con show "Wired connection 1"
nmcli connection show listing the wired profile, and the ipv4.dns value read back from it
NetworkManager keeps the value on the connection profile, so that is where it has to be read back.

Reading the profile back shows the value that the next activation will use, which is a different check from the runtime one in resolvectl.

Keep the change with netplan on Ubuntu Server

Ubuntu Server hands the connection to systemd-networkd through netplan. Netplan writes the DNS addresses into the file networkd reads, and the netplan and network configuration guide covers the rest of that file.

network:
  version: 2
  ethernets:
    enp0s6:
      dhcp4: true
      nameservers:
        addresses: [9.9.9.9, 149.112.112.112]
      dhcp4-overrides:
        use-dns: false

Without the dhcp4-overrides block, the two addresses lose to whatever the DHCP server sends, which is why the override exists. The netplan documentation states that behaviour as the default.

I compared the same file with and without that override, and the generated networkd configuration differs by one line. The flag is the difference.

Networkd reads UseDNS before it decides whose servers to keep. Generating the configuration shows that flag in the file it writes.

[Match]
Name=enp0s6

[Network]
DHCP=ipv4
DNS=9.9.9.9
DNS=149.112.112.112

[DHCP]
RouteMetric=100
UseMTU=true
UseDNS=false

That UseDNS line is written only when the override is set, so it is the difference between a static pair that holds and a static pair that disappears.

A static address raises the same ownership question, and the static IP guide handles the address half of it. The resolver half is this page.

Apply the file with the reversible command rather than the committed one. A wrong interface name then rolls itself back instead of taking the machine off the network.

sudo netplan try
sudo netplan apply

netplan apply re-reads the live network configuration of the machine, so run it from a console you can still reach if the interface does not come back.

I would not run it over a remote session. Without another way into that machine, a failed apply locks you out of the fix.

Edit resolv.conf when nothing else applies

The plain file is the right answer in a container, a chroot, or a rescue shell where nothing regenerates it. There, the two addresses are the whole configuration.

sudo tee /etc/resolv.conf <<'EOF'
nameserver 9.9.9.9
nameserver 149.112.112.112
EOF

The file resolved generates holds 127.0.0.53 as its only nameserver, because every program on the machine is expected to talk to the stub rather than to an upstream server. That indirection is what lets one setting apply to every program at once.

On a machine with resolved running, the same edit lasts until the service restarts or the link changes, and then the symlink writes over it again.

Turn on DNS over TLS

resolved can encrypt the traffic between your machine and the resolver, and the option that switches it on is DNSOverTLS. The address list needs one extra part for that to work.

sudo tee /etc/systemd/resolved.conf.d/99-dns.conf >/dev/null <<'EOF'
[Resolve]
DNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net
FallbackDNS=1.1.1.1#cloudflare-dns.com
DNSOverTLS=yes
EOF

Setting that option to yes means every query fails against a server without a valid certificate, so the list has to name resolvers that support the protocol.

The resolvectl page states the trade plainly. Yes fails every request against a server that cannot do it, while opportunistic tries the encrypted connection and falls back to a plain one.

Encrypted queries leave on port 853 rather than 53, so a firewall that permits ordinary DNS and blocks that port fails every lookup once the option is on. Quad9 and Cloudflare both publish the hostname to use on their own service pages.

Restart the service and the status output gains the option name next to the protocol list.

sudo systemctl restart systemd-resolved
resolvectl status
resolvectl status with DNSOverTLS enabled and a query reporting encrypted transport
With the encryption option on, the query reports that the transport it used was encrypted.

The last line of a query is the confirmation, because it reports whether the answer arrived over an encrypted transport or a plain one.

I kept each hostname from the operator’s own service page. The name after the hash is what the certificate check matches against, and a guess there fails closed.

When the new DNS server does not take effect

A resolver that never answers stops resolution instead of falling back, and the failure is worth seeing once. The query times out rather than returning a wrong answer.

Point a link at an address in the documentation range, then flush the cache and ask a name.

sudo resolvectl dns eth0 192.0.2.1
sudo resolvectl flush-caches
resolvectl query example.com
resolvectl query failing with Query timed out against an unreachable resolver
A resolver that does not answer fails the query instead of falling back, which is the failure this check is for.

Other causes explain the cases where the setting looks correct and the behaviour does not change.

  • A DHCP server still sending a resolver, which wins while ignore-auto-dns is unset on the profile.
  • A second component writing the same link, where the netplan file and the NetworkManager profile disagree about the address.
  • A cached answer, which survives a server change until the record TTL runs out or the cache is flushed.
  • A container or a chroot, which carries its own /etc/resolv.conf and never sees the change you made outside it.

A filtered domain fails differently from an unreachable server, because the resolver answers with a block page or an empty result instead of letting the query time out. Pointing the same link at an unfiltered address is what separates the two.

The cache and the resetting interface have walkthroughs of their own, since the cache clearing guide covers the service behind the cache and the network change fix covers an interface that resets itself.

The value that survives a reboot is the one a component wrote

A restart is the test that separates a setting from a suggestion. A single command reads the result.

resolvectl status
resolvectl query example.com

If the link block still names your server, and the query reports the network rather than the cache, the change is in place for the next boot as well.

The value that survives is the one a component wrote, and the file you edit is only a view of it.

When a name resolves and the page still does not open, the next check is reachability rather than resolution. The ping walkthrough is the one to run at that point.

Frequently asked questions

How do I change the DNS server on Linux permanently?

Write the address where the component that owns the connection reads it. On a systemd machine that means a drop-in file under /etc/systemd/resolved.conf.d, or the ipv4.dns setting on the NetworkManager profile when a manager owns the link. A change made with resolvectl dns alone lasts until the link restarts.

Why does /etc/resolv.conf keep resetting?

On a systemd machine the path is a symlink into /run that systemd-resolved rebuilds, so the edit is overwritten when the service restarts or the link changes. Change the setting in resolved.conf, in the NetworkManager profile, or in netplan instead.

How do I check which DNS server Linux is using?

Run resolvectl status. The block for the link carrying queries lists the DNS Servers it forwards to, and resolvectl query for a domain reports the link that answered and whether the answer came from the network or the cache.

Does changing the DNS server make the connection faster?

It can shorten the wait before a page starts loading when your provider resolver is slow or far away, and it changes nothing about the speed of the transfer itself. Test a candidate with resolvectl dns on one link and a query or two before you commit to it.

What is the difference between resolvectl and /etc/resolv.conf?

resolvectl talks to systemd-resolved, which holds the setting and answers queries through the stub on 127.0.0.53. /etc/resolv.conf on a systemd machine is a generated view of that state, which is why an edit there is temporary.

How do I undo a DNS change on Linux?

Remove the drop-in file and restart systemd-resolved, or run resolvectl revert on the link to drop a runtime setting. For a NetworkManager profile, clear the value with nmcli connection modify and set ipv4.ignore-auto-dns back to no so the DHCP resolver is used again.