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