This step is optional in the strict sense — Unbound alone is already a private resolver — and almost everyone wants it anyway. AdGuard Home gives you blocklists, per-client policies, a readable query log while you are debugging, and, most usefully, native DNS-over-TLS and DNS-over-HTTPS listeners so that phones and browsers can talk to your resolver from networks where a VPN is not practical.
The arrangement is a chain, and getting the ports right is the whole trick: AdGuard Home owns 53 and faces the clients, Unbound sits on 127.0.0.1:5335 and faces the internet. Nothing else on the box listens on 53.
curl -fsSL https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v
# Web interface: http://<vps>:3000 — reach it over the SSH tunnel below,
# never by opening 3000 to the internet.
ssh -N -L 3000:127.0.0.1:3000 root@<vps>
Do the first-run wizard through that tunnel. Set the admin listener to 127.0.0.1 or to the tunnel address, never to 0.0.0.0, and give it a long password — this interface can rewrite every name your network resolves, which makes it as sensitive as the resolver itself.
Then set exactly one upstream: in Settings → DNS settings, clear the default list and enter your Unbound instance. One line, nothing else. If a public resolver is left in that box, everything in step 01 has been undone.
127.0.0.1:5335
Leave "Parallel requests" off — with a single upstream it does nothing but duplicate traffic — and turn "Use private reverse DNS resolvers" off unless you actually run an internal zone. Set the bootstrap servers to something you trust; they are used only to resolve the hostnames of upstream servers, and with a plain IP upstream they will never be consulted at all.
Blocklists: start narrow. The instinct is to enable everything on the list page and enjoy the counter. Resist it. Every list is a set of judgement calls made by somebody else about what your devices are allowed to reach, and the failures are quiet and confusing — a half-loaded page, a button that does nothing, a bank app that refuses to open. Begin with the default AdGuard DNS filter, or with a single well-maintained aggregate such as OISD, live with it for a fortnight, and only then add more. Keep an allowlist and use it without guilt.
The query log is the awkward part
You have just built a machine whose purpose is to stop somebody else from keeping a list of every name your devices resolve — and the first thing AdGuard Home does is keep exactly that list, on by default, for ninety days. It is genuinely useful while you are tuning blocklists, and it is the single most sensitive file on the server. Decide deliberately: 24 hours is enough to answer "what did that device just ask for?", zero is the honest setting once the tuning is done, and ninety days is a decision nobody should make by leaving a default alone. Settings → General → Query log, and set statistics retention while you are there.
Point the firewall at the new arrangement — AdGuard Home now owns port 53 on the tunnel interface, and Unbound is only reachable on loopback. Nothing about the public interface changes: it still has no DNS on it at all.