SSD Nodes Learn Hosting plans →
Guides Matt ConnorBy Matt Connor

What is private DNS, and how to run your own

Private DNS encrypts Android's DNS lookups over TLS or HTTP/3. What it hides from your network, and how to run your own resolver on a VPS with Unbound.

Private DNS on Android uses DNS over TLS or HTTP/3

Private DNS is Android's encrypted DNS feature. It supports DNS over TLS, the protocol from RFC 7858 that wraps DNS lookups in a TLS (transport layer security) session on TCP port 853. Android 11 and later also support DNS over HTTP/3 for supported well-known providers. Normal DNS travels in plain text on port 53, so anyone between your phone and the resolver can read every name you look up. With Private DNS set to a hostname, the names travel inside an encrypted connection to one resolver you chose, and that resolver proves its identity with a certificate for the hostname. That is the whole feature. It changes who can read your lookups, and nothing about where your traffic goes. If DNS itself is new to you, start with what DNS is and how a lookup works.

The setting lives at Settings > Network & internet > Private DNS on Android 9 and later, and it has three states.

  • Off. System DNS lookups go in plain text to the DNS servers assigned by the current network. The network operator, meaning whoever runs the network you are on (your mobile carrier, or the café Wi-Fi), reads and can rewrite those queries. Apps that use their own encrypted DNS are separate from this setting.
  • Automatic. The default. Android tries encrypted DNS with the network's own DNS servers, using DNS over TLS or, for supported providers on Android 11 and later, DNS over HTTP/3. If encrypted DNS is unavailable, Android falls back to plain port 53 without telling you. Automatic has no configured provider hostname for authentication. It stops passive reading only.
  • Private DNS provider hostname. Strict mode. Android resolves the hostname through the network's DNS and checks the encrypted resolver's certificate against that hostname. Your own hostname in this guide uses DNS over TLS on port 853; supported well-known providers on Android 11 and later also support DNS over HTTP/3. If Android cannot establish an authenticated encrypted connection, it does not fall back to plain DNS. Name resolution stops, and the phone shows "Private DNS server cannot be accessed". This is the only mode that gives you a guarantee, and the only one that earns the word "private". Strict mode can also get in the way of hotel and café Wi-Fi login pages and of names that only resolve inside an office network, and whether Private DNS should be on or off walks through those cases and how to tell which one you have hit.

iOS has no field for this. Apple supports the same protocol from iOS 14 on, either through an installed configuration profile or through an app that saves system DNS settings with Apple's NetworkExtension framework. Either way, the user then has to turn the setting on in the Settings app. The phone-side steps below are Android.

What the network operator still sees

An encrypted resolver hides the names you look up. It hides nothing else, and it is worth being exact about the rest.

  • The resolver's name. Android uses the network's DNS to find the IP address of a configured resolver hostname, so the operator sees that lookup. Apps that bypass system DNS also expose their lookups if they send them without encryption.
  • The encrypted resolver connection, and where. A TCP connection to port 853 identifies DNS over TLS; DNS over HTTP/3 uses QUIC over UDP port 443. The operator sees the resolver's IP address and can block access to it. In strict mode, losing all authenticated encrypted connections means no system DNS, which is by design.
  • Every destination IP address. Your phone still connects directly to every site. The operator sees each IP, how much data moved, and when.
  • The server name on TLS connections without ECH. When a browser opens an HTTPS connection without encrypted client hello (ECH), it sends the hostname in the clear, in the SNI (server name indication) field, so the site can pick the right certificate. ECH hides this, but it needs support from the browser and from the site. So the operator loses your DNS log and keeps a substitute wherever ECH is not used.

Private DNS is a small, real improvement. It is not anonymity, and it is not a tunnel. If you want the operator to see nothing but one encrypted stream to one server, that job belongs to a VPN, and a WireGuard VPN on your own VPS does it in about forty lines of config.

Public hostnames people paste into the field, and what each logs

Any of these works in the hostname field. Choosing one means choosing who gets your lookup history instead of the network operator. What follows is what each provider's own published policy said when read on 1 November 2026. Policies change; read the current one before you commit.

  • dns.google (Google Public DNS). Temporary logs hold your full IP address and enter deletion processes within 24 to 48 hours, with longer retention permitted solely to resolve security and abuse issues. Permanent logs keep the requested name, the query type and a city or region-level location, with the IP address removed.
  • one.one.one.one (Cloudflare 1.1.1.1). Source IPs are truncated (the last octet dropped for IPv4) and deleted within 25 hours. Resolver logs are deleted within 25 hours except for limited sampled data, excluding truncated IP addresses, used to generate aggregations; aggregated data can be stored indefinitely. Separately, sampled network packets used for troubleshooting and attack mitigation can contain source IPs. Cloudflare shares query names and types, without client IPs, with APNIC for research, and states it will not sell resolver data or use it for advertising.
  • dns.quad9.net (Quad9, a Swiss foundation). States it does not record user IP addresses under normal conditions: the reply address stays in memory until the UDP reply is sent or the stateful connection closes. Its separate policy for anomalous conditions permits collecting IP addresses and permanently retaining diagnostic data associated with triggering queries. It keeps aggregate counters per region, and no region is smaller than 10,000 people unless it is a whole country. The default hostname blocks domains on its malware lists.
  • dns.adguard-dns.com (AdGuard DNS, a company registered in Cyprus). States it collects no user-specific information except where necessary to provide the service, keeps aggregate server metrics, and stores an anonymous list of domains requested in the last 24 hours. The default hostname blocks advertising and tracking domains; unfiltered.adguard-dns.com does not.

Every one of these is an organisation that can be compelled or breached. That is the case for the rest of this guide.

The other thing called private DNS

Cloud providers use the same two words for something unrelated. A Route 53 private hosted zone on AWS, or an Azure Private DNS zone, is a set of names available within associated virtual networks, including multiple VPCs on AWS or multiple linked virtual networks on Azure, so db.example.com can point at a private address unavailable on the public internet. It has nothing to do with encryption or with your phone. If you arrived here from a cloud console, that is the feature you want, and the rest of this page is about the other one.

Why run your own private DNS resolver

Pasting dns.quad9.net into the field moves your lookup history from the network operator to Quad9. Running your own resolver on a VPS moves it to you. State the trade plainly before you build it.

What you gain: no third party holds a query log with your name on it, because the resolver is a recursive one. It asks the root servers, then the .com servers, then the site's own servers, and no single upstream sees the full list. Unbound also does QNAME minimisation by default, which normally sends each server only the part of the name it needs, so the .com servers learn example.com rather than mail.example.com; its default best effort mode falls back to the full name for some upstream errors.

What you take on: the VPS provider can see the encrypted connection from your phone and the plain-text queries the resolver sends out to the authoritative servers, because the last hop of DNS is still unencrypted. Your VPS has one IP address and one user, so a lookup from that address is a lookup by you, where a public resolver mixes you into millions. You also now own uptime and patching. If the VPS is down, your phone has no DNS at all in strict mode.

This is a resolver, not a VPN. Your web traffic still leaves the phone directly. If you already run WireGuard, the better design is to put the resolver on the tunnel address and skip the public certificate entirely, and fixing DNS inside a WireGuard tunnel covers that layout and its failure modes.

Run your own: the parts both paths share

Two paths follow. Unbound is a validating recursive resolver with no interface and about ten lines of config. AdGuard Home is a forwarder with blocklists and a web UI. Pick one, since both want port 853. Both need the same preparation, done once: a hostname that points at the VPS, and a certificate for that hostname that Android will accept. Both also need the firewall opened for 853, plus 80 for the certificate challenge, and nothing else. These steps run on your VPS, because the resolver needs outbound reach to the whole DNS tree and a certificate issued for a name that points at its public address.

Create an A record, dns.example.com, pointing at the VPS's public IP, and wait until dig +short dns.example.com returns it from your laptop. Certbot's standalone HTTP challenge requires the name to resolve to a server that answers its challenge on port 80.

Open exactly two ports. Port 80 is for the certificate challenge and 853 is for DNS over TLS. Port 53 stays closed to the internet on purpose: an open UDP resolver is an amplification source, since a small spoofed query can trigger a much larger answer aimed at whoever the attacker chose as the victim. TLS on TCP cannot be spoofed that way, because the handshake has to complete first.

sudo ufw allow 80/tcp
sudo ufw allow 853/tcp
sudo ufw status

ufw status should list 80/tcp and 853/tcp as ALLOW and nothing for 53. If ufw is new to you, the ufw firewall basics for a VPS covers the default deny policy and the SSH rule you must add before enabling it. Most providers also have a separate network firewall in the control panel, and 853 has to be open there too.

Now the certificate. Nothing else is listening on port 80 on a resolver box, so certbot's standalone mode is the simplest option.

sudo apt update && sudo apt install -y certbot
sudo certbot certonly --standalone -d dns.example.com
sudo systemctl list-timers certbot.timer

Certbot writes fullchain.pem and privkey.pem under /etc/letsencrypt/live/dns.example.com/ and installs a systemd timer that renews them. sudo certbot renew --dry-run proves renewal works before the real one is due. If certbot fails with a challenge error, the usual cause is the A record still pointing somewhere else, or port 80 blocked at the provider's firewall. The certbot on Ubuntu 24.04 guide goes through the challenge types and the errors each one produces.

Android accepts this certificate because Let's Encrypt's root is in Android's trust store. A self-signed certificate is not, and there is no field on the phone to add one, so the public certificate is not optional.

Path A: Unbound as a validating recursive resolver with TLS

sudo apt install -y unbound

On Ubuntu 24.04 the package enables DNSSEC validation out of the box through /etc/unbound/unbound.conf.d/root-auto-trust-anchor-file.conf, and starts listening on localhost port 53: 127.0.0.1 and, with IPv6 enabled, ::1. It runs as the unbound user, but reads the TLS key while it still has root privileges, before chroot and dropping privileges. A deploy hook copies them into place on every renewal and restarts the service. Copying is deliberate: it keeps the certificate files in a separate directory without changing the permissions under /etc/letsencrypt/live.

sudo install -d -o root -g unbound -m 750 /etc/unbound/tls
sudo tee /etc/letsencrypt/renewal-hooks/deploy/unbound.sh >/dev/null <<'EOF'
#!/bin/sh
set -e
LIVE=/etc/letsencrypt/live/dns.example.com
install -o root -g unbound -m 640 "$LIVE/fullchain.pem" /etc/unbound/tls/fullchain.pem
install -o root -g unbound -m 640 "$LIVE/privkey.pem" /etc/unbound/tls/privkey.pem
systemctl restart unbound
EOF
sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/unbound.sh
sudo /etc/letsencrypt/renewal-hooks/deploy/unbound.sh

Run the hook by hand once, as the last line does, because certbot only runs deploy hooks after a renewal, and the first issuance already happened.

Now the listener. Debian and Ubuntu load every file in /etc/unbound/unbound.conf.d/, so add one rather than editing the main file.

sudo tee /etc/unbound/unbound.conf.d/dot.conf >/dev/null <<'EOF'
server:
  interface: 127.0.0.1
  interface: 0.0.0.0@853
  tls-port: 853
  tls-service-pem: "/etc/unbound/tls/fullchain.pem"
  tls-service-key: "/etc/unbound/tls/privkey.pem"
  access-control: 0.0.0.0/0 allow
  ip-ratelimit: 100
  hide-identity: yes
  hide-version: yes
  prefetch: yes
EOF
sudo unbound-checkconf
sudo systemctl restart unbound
sudo ss -tlnp | grep 853

unbound-checkconf should print no errors in /etc/unbound/unbound.conf, and ss should show unbound listening on 0.0.0.0:853. Line by line: the two interface lines replace Unbound's default, so the first keeps plain DNS on localhost for your own tests, and the second binds the public address on 853. tls-port marks 853 as the TLS port, and only interfaces bound with @853 get TLS. access-control: 0.0.0.0/0 allow is the line that makes people nervous, and it is required: a phone changes IP address every time it changes network, so there is no source range to restrict to. The firewall keeps plain DNS off the internet. Plain 53 is reachable only from the box itself, and 853/tcp, the only thing the firewall lets through, requires a completed TLS handshake before Unbound reads a query. ip-ratelimit: 100 sets a threshold of 100 queries a second per client IP, but the default ip-ratelimit-factor: 10 still lets one in ten queries through above that threshold, so it is not a hard cap or a guarantee against public use at volume. If your VPS has an IPv6 address, add interface: ::0@853 and access-control: ::0/0 allow as well.

If Unbound fails to start after this, read journalctl -u unbound -n 30. Error for server-cert-file: /etc/unbound/tls/fullchain.pem means Unbound could not load the certificate; check the file path, PEM contents and permissions. With the copy commands above, ls -l /etc/unbound/tls should show group unbound and mode -rw-r----- on both files. can't bind socket: Address already in use on 853 means another service holds the port, which on a fresh box is usually an earlier AdGuard Home install.

Test it from your laptop with dig, which speaks DNS over TLS from BIND 9.18 on, and then check the certificate chain and hostname with openssl.

dig @dns.example.com +tls +tls-ca example.com
openssl s_client -connect dns.example.com:853 -servername dns.example.com -verify_hostname dns.example.com </dev/null 2>/dev/null | grep 'Verify return'
dig @dns.example.com +tls dnssec-failed.org

The first command should end with ;; SERVER: 203.0.113.10#853(dns.example.com) (TLS), with your address in place of the example one. +tls-ca makes dig validate the certificate against the system store, which is the same check Android performs. The second should print Verify return code: 0 (ok). The third should return status: SERVFAIL, because dnssec-failed.org is published with a deliberately broken signature and a validating resolver refuses it. A resolver that answers it is not validating.

Path B: AdGuard Home if you want blocking and a web UI

AdGuard Home serves DNS over TLS on the same port 853, adds blocklists and a query log you can read, and is configured from a browser. The difference underneath: it is a forwarder, not a recursive resolver. Every uncached query goes to the upstream listed under Settings > DNS settings, so read the logging notes above when you pick that upstream, or point it at a local Unbound on a second port for full recursion.

Install it with the script from the project README. Download it first if you prefer to read what a script does before it runs as root, which is a reasonable habit.

curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v

The script installs the binary under /opt/AdGuardHome, registers it as a systemd service, and starts the first-run wizard on port 3000. Do not open 3000 in the firewall. Reach the wizard through an SSH tunnel instead, so the admin page never faces the internet.

ssh -L 3000:127.0.0.1:3000 ubuntu@203.0.113.10

Then open http://127.0.0.1:3000 in the laptop's browser. In the wizard, set the admin web interface to listen on 127.0.0.1 port 3000, and the DNS server to all interfaces on port 53. On Ubuntu the DNS step fails with bind: address already in use, because systemd-resolved holds port 53 on 127.0.0.53. The fix from the AdGuard Home FAQ tells resolved to stop listening and to use AdGuard Home itself.

sudo mkdir -p /etc/systemd/resolved.conf.d
printf '[Resolve]\nDNS=127.0.0.1\nDNSStubListener=no\n' | sudo tee /etc/systemd/resolved.conf.d/adguardhome.conf
sudo mv /etc/resolv.conf /etc/resolv.conf.backup
sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl reload-or-restart systemd-resolved

Between this step and the end of the wizard, the server itself cannot resolve names, because its resolver now points at an AdGuard Home that is not yet serving. Finish the wizard and it comes back. Port 53 is then open on the box, and the firewall still drops it from the internet, which is the arrangement you want.

Encryption is under Settings > Encryption settings. Turn it on, enter dns.example.com as the server name, leave the DNS over TLS port at 853, and choose the file path option for both the certificate and the key: /etc/letsencrypt/live/dns.example.com/fullchain.pem and /etc/letsencrypt/live/dns.example.com/privkey.pem. AdGuard Home runs as root, so it can read them where they are, and it reloads them on its own when certbot renews. The page reports whether the chain and the key parsed, and the expiry date. The same dig and openssl tests from the Unbound section apply.

Point the phone at it

On the phone, open Settings > Network & internet > Private DNS, pick Private DNS provider hostname, and enter dns.example.com. Load a page. On the VPS, an established connection from the phone's address to port 853 is the proof.

sudo ss -tn state established '( sport = :853 )'

"Private DNS server cannot be accessed" on the phone means strict mode failed and Android refused to fall back. Check in this order. The A record does not resolve on the network the phone is on; test with dig +short dns.example.com from that network. Port 853 is closed on the VPS firewall or the provider's firewall, which ss -tlnp on the box and openssl s_client from outside separate. Or the certificate is not valid for the name you typed, which the openssl s_client command above checks with -verify_hostname dns.example.com; -servername alone only sets SNI and does not check the hostname. Android checks whether the certificate covers the configured hostname, so dns.example.com in the field and a certificate covering only example.com is a failure.

Leave the mode on the hostname permanently, and be ready for the consequence: when the VPS is down, the phone has no DNS at all, on every network. That is strict mode working as designed. Android offers no fallback, and Automatic is not one, since it silently returns you to plain text.

FAQ

Is Private DNS on Android the same as a VPN?

No. Private DNS encrypts one thing, the lookups that turn names into addresses, and sends them to your chosen resolver over DNS over TLS or, for supported providers on Android 11 and later, DNS over HTTP/3. Your actual traffic still leaves the phone directly, so the network sees every destination IP address and the site name on HTTPS connections without ECH. A VPN wraps all of it in one tunnel. The two combine well, and a resolver on the VPN's private address needs no public certificate.

What does the Automatic setting actually do?

Android tries encrypted DNS with the DNS servers the current network assigned, using DNS over TLS or, for supported providers on Android 11 and later, DNS over HTTP/3. If encrypted DNS is unavailable, Android uses plain port 53 and does not tell you. Automatic has no configured provider hostname for authentication. Only the hostname mode guarantees authenticated encrypted system DNS without falling back to plain DNS.

Why does my phone say "Private DNS server cannot be accessed"?

Strict mode failed and Android refused to fall back to plain DNS. In order: the hostname does not resolve to your VPS (dig +short dns.example.com), port 853 is blocked on the VPS or the provider's network firewall, or the certificate does not cover the hostname you typed. openssl s_client -connect dns.example.com:853 -servername dns.example.com -verify_hostname dns.example.com from a laptop checks the connection, certificate chain and hostname, and a healthy server ends with Verify return code: 0 (ok).

Should I open port 53 on my VPS too?

No. Port 53 supports both UDP and TCP; UDP has no handshake, so an open UDP resolver answers spoofed queries and becomes an amplification source aimed at whoever the attacker chose. Port 853 is TCP with a completed TLS handshake before any query is read, which cannot be spoofed. Keep 53 bound to localhost for your own tests and let the firewall drop it from the internet. How ports work on Linux explains why binding to 127.0.0.1 keeps a port off the network entirely.

Which public resolver should I use if I do not want to run my own?

Pick by what the policy says and where the operator is based. Quad9 states it does not record user IP addresses under normal conditions, with a separate policy permitting diagnostic collection during anomalous conditions. Cloudflare deletes truncated IPs within 25 hours and resolver logs within 25 hours except for limited samples without truncated IPs used for aggregations; aggregated data can be retained indefinitely, and separately sampled network packets can contain source IPs. Google normally deletes temporary logs containing full IPs within 24 to 48 hours, with longer retention for security and abuse issues, and keeps anonymised query logs permanently. AdGuard states it collects no user-specific information except where necessary to provide the service and keeps a 24-hour anonymous domain list. These are the providers' own statements as read on 1 November 2026, and none of them replaces reading the current policy.

Was this article useful?