Mullvad Public DNS Shuts Down Nov 2: 5 Checks I Ran First
DEV Community

Mullvad Public DNS Shuts Down Nov 2: 5 Checks I Ran First

Here's a thinking process:

  1. Analyze User Input:

    • Title: "Mullvad Public DNS Shuts Down Nov 2: 5 Checks I Ran First"
    • Article: A wall of text about Mullvad DNS shutting down, migration to Quad9, checks to run, technical details, etc.
    • Task: Reformatted into clean, readable Markdown for a technical audience.
    • Rules:
    • Break into short, well-structured paragraphs separated by blank lines.
    • Add Markdown section headings (## Heading) where topic clearly shifts.
    • Use bullet/numbered lists for enumerations, inline code/fenced code blocks for code/commands/identifiers.
    • Preserve every fact, name, number, quotation exactly as given. Never invent, add, remove, or reorder information.
    • Only restructure and lightly polish wording for flow and grammar; no introduction, conclusion, opinion, or commentary.
    • Do not repeat the article title as a heading and do not wrap the whole answer in a code fence.
    • Output only the reformatted article body as Markdown, nothing else.
  2. Deconstruct the Article Text:
    Let me parse the article to identify sections, headings, lists, code blocks, etc. I need to keep all facts exactly as-is.

Key elements from the text:

  • Title line (should not be repeated as heading, but the content starts after it)
  • "On September 3, Mullvad announced it is shutting down..."
  • Lists of checks (# Check 1, # Check 2, etc.)
  • Code snippets: grep -rnE "194 \. 242 \. 2 \. [2-9]|2a07:e340|dns \. mullvad \. net", /etc/systemd/resolved.conf, etc.
  • Tables/endpoint tables
  • Inline code: 194.242.2.2, 9.9.9.9, dns.query, etc.
  • Fenced code blocks? There's a Python script using dns.message, dns.query, dns.rcode. I should format as a fenced code block with proper formatting, but preserve exactly the content. The rule says "fenced code blocks where code, commands, or identifiers appear." So I'll use ``` for the Python script.
  • Bullet points or numbered lists for enumerations.
  • Headings where topic shifts.

Let me outline the structure based on the text flow:

  • Introduction/Context (but no new intro, just restructure)
  • Check 1: find Mullvad resolver leftovers
  • What stops working on Nov 2?
  • Endpoint table from Mullvad's docs
  • Three groups have homework
  • How filtering works (NXDOMAIN)
  • Finding every device still pointing at Mullvad
  • Is pasting 9.9.9.9 actually safe? (Check 2: Windows 11 per-adapter DoH templates)
  • Apple profiles check
  • Transport trap: Mullvad vs Quad9
  • resolvectl's answer
  • DNSOverTLS=opportunistic caveat
  • What happens to ad blocking after the switch?
  • Comparison script (Check 4)
  • Realistic options for filtering back
  • Where do queries go when Mullvad goes dark?
  • Check 5: pick your own secondary deliberately
  • Should Mullvad have kept running the resolver? (argument)
  • What should you take away from this?
  • Over to you questions

I need to map the headings properly. The user said "Add Markdown section headings (## Heading) where the topic clearly shifts, so the article scans well." I should create ## headings for major sections, but not repeat the article title. I'll use descriptive headings based on content shifts.

Important: Preserve every fact, name, number, quotation exactly. So I must not change any numbers, IP addresses, hostnames, etc. I must keep quotations exactly as given (single or double quotes as in original). I need to be careful with formatting code/identifiers.

Let me go through the text and extract/structure:

Original text has some formatting like # Check 1: ... which I can turn into a numbered list or keep as heading with code. The rules say "Use bullet or numbered lists for enumerations, and inline code or fenced code blocks where code, commands, or identifiers appear." So # Check 1 could become a numbered list item, or I can keep it as a sub-heading with code block. I think using ## Check 1: find Mullvad resolver leftovers then a code block for the grep command is fine. But I need to ensure I don't add/remove content.

Let me re-read the rules: "Only restructure and lightly polish wording for flow and grammar; do not add an introduction, conclusion, opinion, or commentary of your own." "Preserve every fact, name, number, and quotation exactly as given." So I can change sentence structure for flow, but must keep all data intact.

I'll structure it like this:

## Check 1: find Mullvad resolver leftovers in the usual config spots

Then the grep command as a code block or inline code. The original has grep -rnE "194 \. 242 \. 2 \. [2-9]|2a07:e340|dns \. mullvad \. net" \ /etc/resolv.conf /etc/systemd/resolved.conf \ /etc/NetworkManager/conf.d /etc/dnsmasq.conf 2>/dev/null. I'll put it in a fenced code block to preserve spacing and backticks, but inline code might also work. Fenced code block is safer for multi-word commands with spaces and backslashes. However, the rule says "inline code or fenced code blocks where code, commands, or identifiers appear." I'll use fenced code blocks for multi-line or complex commands, inline for single identifiers.

Actually, looking at the original, the grep command is on one line with backslash-continued arguments. I'll format it as a code block, maybe with syntax highlighting context, but plain fenced code is fine.

Let me plan the headings structure:

  • Check 1: find Mullvad resolver leftovers in the usual config spots

  • What exactly stops working on November 2?

  • Mullvad endpoint table (from docs)

  • Three groups have homework

  • How Mullvad filtering works

  • Finding every device still pointing at Mullvad

  • Check 2: Windows 11 per-adapter DoH templates pointing at Mullvad

  • Apple profiles without guessing

  • Is pasting 9.9.9.9 actually safe? (the transport trap)

  • systemd-resolved: pin DoT with the hostname, not just the IP

  • Windows 11: set the IP, then set DNS over HTTPS to "On (manual template)"

  • resolvectl's answer honestly

  • DNSOverTLS=opportunistic caveat

  • What happens to your ad blocking after the switch?

  • Comparison: Mullvad adblock vs Quad9 secure (Check 4)

  • Realistic options for filtering back

  • Where do your queries go when Mullvad goes dark?

  • Check 5: pick your own secondary deliberately

  • Should Mullvad have kept running the resolver? (argument)

  • What should you take away from this?

  • Over to you

But I need to be careful not to invent headings that change meaning. The user said "Add Markdown section headings (## Heading) where the topic clearly shifts, so the article scans well." I'll identify natural topic shifts and use concise headings.

Let me re-read the article fully text to map exactly. I'll copy it here for reference:

"On September 3, Mullvad announced it is shutting down the public encrypted DNS servers it has run since 2022 and sponsoring Quad9 instead. If you never typed one of their addresses into anything, you can stop reading. I did, more than once: a DoT endpoint in my laptop's systemd-resolved config and an adblocking variant on a travel router. Those six addresses, 194.242.2.2 through 194.242.2.9, stop answering on November 2, 2026. The migration looks like a one-line change and it is not. Five of the six Mullvad endpoints filtered ads. Quad9, the designated successor, filters ads in zero of its four service variants. Mullvad's endpoints only ever spoke DNS over HTTPS or TLS. Quad9 answers plaintext on port 53. Swap the address without checking those two facts and you lose your filtering quietly, then your encryption quietly. Here are the five checks I ran or queued before touching any config. # Check 1: find Mullvad resolver leftovers in the usual config spots grep -rnE "194 . 242 . 2 . [2-9]|2a07:e340|dns . mullvad . net" \ /etc/resolv.conf /etc/systemd/resolved.conf \ /etc/NetworkManager/conf.d /etc/dnsmasq.conf 2>/dev/null The kind of hit you are looking for: /etc/systemd/resolved.conf:27:DNS=194.242.2.4#base.dns.mullvad.net What exactly stops working on November 2? Only the public resolver. Mullvad VPN users keep using the internal DNS inside the tunnel, and that was always the recommended setup. The public service existed for two cases: Mullvad Browser users outside the VPN, and anyone who pointed a device at it directly. Here is the full endpoint table from Mullvad's docs, because the announcement never spells out what each address actually did: Hostname IPv4 What it filtered dns.mullvad.net 194.242.2.2 nothing adblock.dns.mullvad.net 194.242.2.3 ads, trackers base.dns.mullvad.net 194.242.2.4 ads, trackers, malware extended.dns.mullvad.net 194.242.2.5 + social media family.dns.mullvad.net 194.242.2.6 + adult, gambling all.dns.mullvad.net 194.242.2.9 every list Three groups have homework. Anyone who hand-configured one of these on a router, OS, or browser. Anyone running Mullvad's iOS or macOS profile, which Mullvad states will simply stop working and needs replacing with Quad9's. And Mullvad Browser users who customized their DoH, because Mullvad explicitly will not touch customized settings; only the default and bundled-adblock configurations auto-migrate to Quad9. One mechanism detail worth knowing: Mullvad's filtering works by answering NXDOMAIN for blocked domains. Their docs say it plainly: the resolver "simply lies to the client and says that the hostname does not exist". The blocklists live on GitHub, so you can rebuild the same behavior yourself later if you want to. How do I find every device still pointing at Mullvad? My first grep only matched the IP addresses. It missed two configs that referenced the hostnames, including the systemd-resolved IP#hostname syntax you can see in the example above. The corrected pattern matches both, plus the IPv6 prefix, which is how I found a stale 2a07:e340::4 line in a VM clone that had been silently dead for months anyway. Browsers do not store their DoH settings in the files above. Firefox keeps its custom provider in about:config under network.trr.uri , and Chromium browsers show the secure DNS provider in Settings, Privacy and security, Security. There is no grep for that; it is a two-minute manual pass per machine. Windows needs its own check, because Windows 11 stores per-adapter DoH templates separately from the resolver addresses: # Check 2: Windows 11 per-adapter DoH templates pointing at Mullvad Get-DnsClientDohServerAddress | Where-Object DohTemplate -match "mullvad" Checking Apple profiles without guessing On iOS and macOS, Mullvad's profiles came from their encrypted-dns-profiles GitHub repo. Open Settings, then General, then VPN and Device Management on iOS (Device Management on macOS), and look for anything named Mullvad. Those profiles are not auto-updated; Mullvad says they stop working on November 2. Quad9 publishes equivalent profiles for iOS and macOS in their setup guides, and swapping them takes about a minute per device. Is pasting 9.9.9.9 into your old Mullvad slot actually safe? This is the check I would have skipped, and it is the one that matters most. Mullvad's docs state that their IPs "can only be used with DNS resolvers that support DoH or DoT, not with DNS over UDP/53 or TCP/53". Their endpoints never answered plaintext queries on port 53, apart from a limited listener just to bootstrap their own hostnames. Your old Mullvad slot guaranteed encrypted transport by accident of the endpoint. Quad9 is a normal public resolver. It answers plaintext on port 53 by design, with 9953 as an alternate port for restrictive networks. Paste 9.9.9.9 into a plain DNS field that used to hold 194.242.2.4 , and resolution works on day one. The transport silently changed from TLS to cleartext, and nothing will ever warn you. The fix is to configure the transport explicitly, per slot: # systemd-resolved: pin DoT with the hostname, not just the IP # in /etc/systemd/resolved.conf under [Resolve] DNS=9.9.9.9#dns.quad9.net DNSOverTLS=yes Domains=~. # Windows 11: set the IP, then set DNS over HTTPS to "On (manual template)" # Preferred DNS: 9.9.9.9 # DNS over HTTPS template: https://dns.quad9.net/dns-query Reading resolvectl's answer honestly resolvectl status should show your Quad9 server under the global or per-link section with "DNS Over TLS: yes". One caveat I learned from Mullvad's own docs and confirmed carries over: DNSOverTLS=opportunistic means the resolver may fall back to plaintext when TLS fails. If your config says opportunistic, you have an encryption preference, not a guarantee. And dig @9.9.9.9 example.com will happily return answers over port 53, because that is what Quad9 serves. With Mullvad's filtering endpoints, the same dig would have gotten no answer at all. That difference is the whole trap in one command. What happens to your ad blocking after the switch? Nothing, by design. Quad9's own documentation lists four service variants, and not one of them filters ads or trackers: Quad9 variant IPv4 What it does Secure 9.9.9.9, 149.112.112.112 DNSSEC validation, malware blocking No Threat Blocking 9.9.9.10 pure recursive, no filtering Secure + ECS 9.9.9.11 Secure, plus EDNS Client Subnet for CDN routing No Threat Blocking + ECS 9.9.9.12 unfiltered with ECS Malware and phishing blocking is Quad9's scope, and their FAQ states they have no plans to add content filtering. So anyone who ran one of Mullvad's five filtering endpoints loses ad blocking in the migration, and pages just get busier a few weeks later with nothing visibly broken. That is the quietest failure mode in this whole story. The comparison I plan to run before the cutover, since both services are still up: # Check 4: compare resolver behavior over DoH (pip install dnspython) import dns.message , dns . query , dns . rcode RESOLVERS = { " Mullvad adblock (still up today) " : " https://adblock.dns.mullvad.net/dns-query " , " Quad9 secure (the successor) " : " https://dns.quad9.net/dns-query " , } DOMAINS = { " doubleclick.net " : " ad canary " , " malware.testcategory.com " : " Quad9 ' s own malware test domain " , } for label , url in RESOLVERS . items (): print ( label ) for domain , note in DOMAINS . items (): query = dns . message . make_query ( domain , " A " ) answer = dns . query . https ( query , url ) print ( f " { domain : 26 s } { dns . rcode . to_text ( answer . rcode ()) : 9 s } { note } " ) Based on each provider's documented behavior, the expected shape is: Mullvad adblock answers NXDOMAIN for both domains, Quad9 secure resolves the ad canary normally and returns a block response for the malware test domain. To be honest about my process: I have not run this on a bench yet, so treat those expectations as a hypothesis, and the script as the thing that turns it into data before November 2. If you want the filtering back, the realistic options are uBlock Origin in the browser (which Mullvad itself recommends, and which handles ads better than DNS filtering ever did), a Pi-hole or AdGuard Home box on your LAN with Quad9 as its upstream, or a different managed resolver like AdGuard DNS that still offers filtering tiers. Where do your queries go when Mullvad goes dark? Devices rarely have exactly one resolver. When the primary stops answering, the secondary takes over, and the secondary is usually whatever DHCP handed out: your ISP's resolver. Everything keeps working. Your queries are just going to the party you configured encrypted DNS to avoid in the first place. Two individually reasonable defaults compose into a bad outcome here: a secondary resolver exists for reliability, and a primary resolver dies on a fixed date. It is the same lesson as two CVSS 9.8 agent sandbox CVEs landing the same day : review the defaults as a system, not line by line. Check 5 is cheap: pick your own secondary deliberately (149.112.112.112 if you stay on Quad9), then before November 2 point your primary at a dead address for one minute and watch resolvectl status to see where your queries actually went. Better to learn that on a Tuesday afternoon than on November 3. Should Mullvad have kept running the resolver anyway? Here is the argument I want to have. Mullvad's stated reason is that a privacy-focused public resolver is a specialized undertaking and Quad9 is, in their words, the undisputed leader in the field, so funding it beats duplicating a fraction of it. I find that defensible. Running anycast infrastructure, curating blocklists, and answering abuse mail is real operational work, and a half-maintained resolver is worse than a well-funded one. But two things bother me. First, the auto-migration: Mullvad Browser users on a filtered default wake up with a resolver that filters differently and were never asked. The real-world impact there is small, to be fair, because Mullvad Browser ships uBlock Origin, so the browser's ad blocking does not live in DNS anyway. The people genuinely affected are manual users of the five filtering endpoints, and they at least got a deadline. Second, concentration: more of the privacy-minded internet's DNS now runs behind one Swiss foundation, partly funded by the vendors whose users depend on it. Is one excellent Quad9 better than a dozen scrappy resolvers? I genuinely do not know. It rhymes with a pattern I keep meeting, from stateless session handles that became credentials to resolver slots that quietly stopped meaning what they said: trust boundaries that drift while the config text stays identical. DNS infrastructure has been having a week, with a CVSS 9.8 Windows DNS Server RCE on the server side and this on the resolver side. My take from covering both: the resolver is infrastructure , and infrastructure you configured once and never re-verified is a liability with a friendly name. What should you take away from this? Six Mullvad endpoints go dark on November 2, 2026; five of them filtered ads, and no Quad9 variant filters ads at all. The transport trap is real: Mullvad's endpoints forced DoH/DoT, Quad9 answers plaintext on port 53, so configure the DoT hostname or the Windows DoH template explicitly instead of just swapping IPs. Audit in this order: config files, Windows DoH templates, Apple profiles, browser settings, then pick your own secondary resolver before the fallback picks your ISP for you. So, over to you: are you migrating to Quad9, jumping to AdGuard DNS for the filtering tiers, or self-hosting unbound at this point? And the sharper question underneath: should a privacy vendor ever change what a service filters, even via auto-migration, without asking first? My lean is that the Quad9 handoff was the right call and the silent filtering downgrade is the part worth arguing about. Primary sources: Mullvad announcement (Sept 3) · Mullvad DoH/DoT docs, endpoint tables · Quad9 service addresses and features · Quad9 setup guides · PacketNebula's migration analysis (Sept 5) · mullvad/dns-blocklists"

Now, I need to reformat this into Markdown. I'll go paragraph by paragraph, group by topic, add headings where shifts occur, use code blocks for commands/snippets, and preserve all exact text.

Let me outline the reformatted structure:

Intro/Context (first paragraph)
Then Check 1, What stops working, Endpoint table, Three groups have homework, How filtering works, Finding every device, Check 2 (Windows DoH), Apple profiles, Transport safety check, systemd-resolved fix, Windows 11 fix, resolvectl caveat, Ad blocking after switch, Comparison script (Check 4), Realistic options, Where queries go, Check 5, Should Mullvad have kept running, What to take away, Over to you.

I need to ensure I don't miss any exact facts. I'll copy text exactly, maybe fixing some run-on sentences lightly for grammar but preserving meaning and all facts. The rule: "Only restructure and lightly polish wording for flow and grammar; do not add an introduction, conclusion, opinion, or commentary of your own." So I can adjust phrasing for readability but must keep all content.

Let me start reconstructing. I'll use ## headings for major sections. I'll avoid using the article title as a heading.

Important: The article starts with "On September 3, Mullvad announced..." That's the first content. I'll make that a paragraph or lead-in.

I need to handle the # Check 1: etc. Those are enumerated checks. I can keep them as ## Check 1: ... headings, and inside put the grep command as a code block. The original has # Check 1: find Mullvad resolver leftovers in the usual config spots then the command. I'll make the heading ## Check 1: find Mullvad resolver leftovers in the usual config spots and then a fenced code block with the grep command. But the rule says "Do not repeat the article title as a heading and do not wrap the whole answer in a code fence." So using code fences for code blocks is fine, as long as the whole answer isn't wrapped in one.

I'll also use inline code for IP addresses, hostnames, etc., where appropriate, but fenced code blocks for multi-line or command sequences.

Let me plan each section's content exactly as in the text, but formatted.

I'll begin:

On September 3, Mullvad announced it is shutting down the public encrypted DNS servers it has run since 2022 and sponsoring Quad9 instead. If you never typed one of their addresses into anything, you can stop reading. I did, more than once: a DoT endpoint in my laptop's systemd-resolved config and an adblocking variant on a travel router. Those six addresses, 194.242.2.2 through 194.242.2.9, stop answering on November 2, 2026. The migration looks like a one-line change and it is not. Five of the six Mullvad endpoints filtered ads. Quad9, the designated successor, filters ads in zero of its four service variants. Mullvad's endpoints only ever spoke DNS over HTTPS or TLS. Quad9 answers plaintext on port 53. Swap the address without checking those two facts and you lose your filtering quietly, then your encryption quietly.

Then heading for Check 1:

## Check 1: find Mullvad resolver leftovers in the usual config spots

Then the grep command in a code block:

grep -rnE "194\.242\.2\.[2-9]|2a07:e340|dns\.mullvad\.net" \
  /etc/resolv.conf /etc/systemd/resolved.conf \
  /etc/NetworkManager/conf.d /etc/dnsmasq.conf 2>/dev/null

But the original has spaces around the dots in the regex: "194 \. 242 \. 2 \. [2-9]|2a07:e340|dns \. mullvad \. net". I need to preserve exactly as given. The rule says "Preserve every fact, name, number, and quotation exactly as given." So I must keep the regex exactly as in the original, including spaces. I can put it in a code block, and the spaces will be preserved. I'll copy the exact line: grep -rnE "194 \. 242 \. 2 \. [2-9]|2a07:e340|dns \. mullvad \. net" \ /etc/resolv.conf /etc/systemd/resolved.conf \ /etc/NetworkManager/conf.d /etc/dnsmasq.conf 2>/dev/null. I'll format it as a code block, maybe with backticks, but since the line has spaces in the regex, a fenced code block with ``` will preserve them. However, the original has escaped spaces? Actually it's just spaces in the string. I'll just paste it as-is inside a code block. To be safe, I'll use triple backticks and the line as is.

But the rule says "Use bullet or numbered lists for enumerations, and inline code or fenced code blocks where code, commands, or identifiers appear." So I can use inline code for some things, but for the grep line, a fenced code block is fine.

I'll also need to handle the "The kind of hit you are looking for:" line. That's text, I'll keep it as a paragraph.

Let me systematically go through and extract sections, ensuring no content loss.

I think the best approach is to write the Markdown manually, section by section, copying all text verbatim where needed, and only

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.