Loading page...Загружаем страницу...正在加载页面...
AtlasRepo

My self-hosted ad blocker poisoned Apple's push servers and killed iMessage

Description and use

After adding allowlist rules for Apple push domains and trimming filter lists, iMessage works again. courier.push.apple.com resolves correctly through the local resolver. The DoH upstream swap remains unapplied, so intermittent Quad9 failures on CNAME-chain domains are still present.

Problem

A self-hosted AdGuard Home DNS proxy caused iMessage to stop sending/receiving messages on a Mac, with symptoms of messages queuing after ~30 minutes despite BlueBubbles API returning 200 and chat.db showing error 4 is_delivered=0.

Outcome

After adding allowlist rules for Apple push domains and trimming filter lists, iMessage works again. courier.push.apple.com resolves correctly through the local resolver. The DoH upstream swap remains unapplied, so intermittent Quad9 failures on CNAME-chain domains are still present.

Workflow

  • Identify that iMessage stops working after ~30 minutes; restart temporarily fixes it for 30 minutes
  • Check DNS resolution: nslookup courier.push.apple.com returns 0.0.0.0; same lookup against 8.8.8.8 returns real IP
  • Identify blocking rules: ||mask.icloud.com^ and ||mask-h2.icloud.com^ in privacy filter list, plus aggressive list catching Apple push hostnames
  • Add allowlist rules in user_rules for Apple push and identity stack domains with $important override
  • Reduce filter lists from 18 to 1 (AdGuard's own DNS filter)
  • Swap DoH upstreams: remove Quad9, add Google DoH next to Cloudflare
  • Edit config using Python yaml.safe_load/yaml.dump instead of sed to avoid stripping control characters
  • Restart Docker container, wait ~5 seconds before testing DNS proxy readiness
  • Verify with two lookups: local resolver vs public resolver, plus grep container logs for exchange failures

Evidence

nslookup courier.push.apple.com returned 0.0.0.0. The same lookup against 8.8.8.8 returned 17.57.144.152.

IDS keeps a DNS cache. At boot it resolves the push hostnames once, gets real IPs while the cache is cold, connects, and messages work. Thirty minutes later the cache expires, IDS re-resolves through the resolver that answers 0.0.0.0, and every connection after that fails.

The rules doing it were explicit, ||mask.icloud.com^ and ||mask-h2.icloud.com^, sitting in a filter list I had turned on for privacy

dnsproxy: exchange failed upstream=https://dns10.quad9.net:443/dns-query question=";app.evenrealities.com. IN A" duration=3.6s err="unexpected EOF"

@@||courier.push.apple.com^$important
@@||init.push.apple.com^$important
@@||mask.icloud.com^$important

I piped a base4 Python script through SSH, something on the way in stripped every control character including the ones YAML needs, and the file came back unreadable

AdGuard reads config at startup, so docker restart on the container, wait about five seconds before testing, the DNS proxy is not ready the instant the process is up

  • Record date: 2026-10-21T14:56:22.939Z