Skip to content

AdGuard Home on OPNsense

AdGuard Home is the client-facing DNS server for the network — every device queries it on port 53, it filters ads/trackers/malware, then forwards clean queries upstream to Unbound. This page covers installing and configuring the AdGuard Home plugin; for how it slots into the wider resolver chain see the DNS Stack guide.

Where AdGuard fits

Client → AdGuard Home :53 → Unbound :5335 → dnsmasq (.<YOUR_LOCAL_DOMAIN>) / root DNS
         (filtering)         (recursion/DNSSEC)   (local names)

AdGuard only filters and forwards — it never resolves recursively itself. That's Unbound's job.

There's also a thorough external write-up worth keeping handy: windgate.net — AdGuard Home on OPNsense.

1. Install the plugin

The AdGuard Home plugin lives in the community repository.

  1. System → Firmware → Settings → set Community (unsupported) plugins and save.
  2. System → Firmware → Plugins → search os-adguardhome-maxit → click + to install.
  3. After it installs, a new Services → AdGuardHome menu appears.
# (Alternative) install from the console / SSH
pkg install os-adguardhome-maxit

2. Free up port 53

AdGuard needs to bind port 53 on the interfaces clients use. Two things commonly squat on 53:

  • Unbound — move it to 5335 (Services → Unbound DNS → General → Listen Port).
  • dnsmasq — if used for DHCP, move its DNS to 53053 and let AdGuard own 53.

See the DNS Stack page — this repo runs AdGuard on 53, Unbound on 5335, dnsmasq on 53053 so nothing collides.

3. Enable and run the setup wizard

  1. Services → AdGuardHome → General → Enable, then Apply.
  2. Open the dashboard (a link is on the plugin page, or http://<opnsense-ip>:3000).
  3. Run the first-time wizard:
  4. Admin web interface: an unused port (e.g. 3000) on All interfaces.
  5. DNS server: port 53 on the interfaces clients query (your VLAN gateways, or All interfaces).
  6. Create the admin account.

4. Point AdGuard at Unbound

Settings → DNS settings → Upstream DNS servers:

127.0.0.1:5335

That's it — a single upstream. Everything (public and .<YOUR_LOCAL_DOMAIN>) goes to Unbound, which decides whether to resolve recursively or hand .<YOUR_LOCAL_DOMAIN> to dnsmasq.

Bootstrap DNS servers (used only to look up upstreams at startup):

127.0.0.1
9.9.9.9

Test upstreams

Use the Test upstreams button. If it fails, Unbound isn't listening on 5335 or a firewall rule is blocking loopback — fix that before going further.

5. Filtering (blocklists)

Filters → DNS blocklists → Add blocklist. AdGuard ships with AdGuard DNS filter enabled. Sensible additions:

  • AdGuard DNS filter (default) — general ads/trackers
  • OISD Big — large, well-maintained, low false positives
  • HaGeZi Multi Pro — aggressive ads + tracking + malware

Don't over-block

Stacking many aggressive lists breaks logins, shortened links, and shopping sites. Start with one or two, then add per-domain allowlist entries under Filters → Custom filtering rules (@@||domain^) as things break.

6. Local domains — DNS Rewrites

To make internal services (<SERVICE>.<YOUR_LOCAL_DOMAIN>, served by Nginx Proxy Manager) resolve on the LAN and over Tailscale, one wildcard rewrite does it:

Filters → DNS Rewrites → Add DNS rewrite:

Field Value
Domain *.<YOUR_LOCAL_DOMAIN> (wildcard covers every subdomain)
Answer NPM's LAN IP (e.g. <YOUR_SERVER_IP>)

A new service then only needs a proxy host in NPM. Pair it with a certificate from a private CA so the names get real HTTPS: Internal TLS.

Keep service names on their own sub-zone

Put services under a sub-zone (e.g. *.lab.home.arpa) rather than straight under the domain your DHCP host names live in (server1.home.arpa). A wildcard rewrite answers before Unbound/dnsmasq ever see the query, so a wildcard on the host-name domain would send every host name to NPM.

See Tailscale → DNS for the tailnet for using this to resolve local names remotely.

6a. Rate limit: raise it for server subnets

AdGuard Home rate-limits per client subnet by default (20 queries per second for a whole /24). Every server and container on a server VLAN shares that one bucket, and queries over the limit are dropped without an answer. A reverse proxy that resolves its upstreams per request hits that quickly and starts returning random 502s.

Settings → DNS settings → Rate limit: raise it (I use 300) or set 0 to disable. Don't rely on the rate-limit allowlist to exempt servers; test that it actually works in your version first. The full story and a burst test: DNS rate limiting a whole server subnet.

7. Per-client settings (optional)

Settings → Client settings lets you treat devices differently by IP/MAC — e.g. stricter blocking for kids' devices, or disabling filtering for a smart TV that misbehaves. Clients show up by IP; give them names here for readable logs.

8. Enforce it — Force DNS via NAT

Filtering only works if clients actually use AdGuard. A device with a hardcoded 8.8.8.8 bypasses everything. Redirect all rogue DNS back to AdGuard with a NAT port-forward per VLAN — see DNS Stack → Force DNS via NAT.

Maintenance

  • Query Log (dashboard) — see exactly what each client asks for and what got blocked; the fastest way to diagnose "site won't load."
  • Clear cache — Settings → DNS settings → Clear cache after changing rewrites or when stale records linger.
  • Back it up separately. The plugin keeps its config in /usr/local/AdGuardHome/AdGuardHome.yaml, not in OPNsense's config.xml, so a normal OPNsense config backup doesn't include it. Copy that file off the box on a schedule (I pull it nightly together with config.xml), see Maintenance.

Sources & further reading