Overview
This guide walks you through setting up a private, secure DNS server using AdGuard Home (for ad-blocking and management) backed by Unbound (for recursive DNS resolution). This configuration enhances privacy by querying root DNS servers directly instead of relying on third-party providers.Prerequisites
VM Setup
We recommend DigitalOcean for hosting. Create a new Droplet with the following spec:- Droplet: Basic · Premium Intel
- vCPU: 1
- RAM: 2 GB
- Disk: 70 GB SSD
- Transfer: 2 TB
- OS: Debian 13 x64
- Cost: $16/mo ($0.024/hr)
- Create Droplet and reserve a Reserved IP address (Networking → Reserved IPs → assign to your Droplet)
DigitalOcean Firewall Rules
In the DigitalOcean control panel, go to Networking → Firewalls → Create Firewall and add the following Inbound Rules, then attach the firewall to your Droplet:Core DNS & Web Traffic
Add these inbound rules using the preset types where available:
| Type | Protocol | Port Range | Sources |
|---|---|---|---|
| SSH | TCP | 22 | All IPv4, All IPv6 |
| DNS TCP | TCP | 53 | All IPv4, All IPv6 |
| HTTP | TCP | 80 | All IPv4, All IPv6 |
| HTTPS | TCP | 443 | All IPv4, All IPv6 |
| Custom | TCP | 853 | All IPv4, All IPv6 |
| DNS UDP | UDP | 53 | All IPv4, All IPv6 |
| Custom | UDP | 853 | All IPv4, All IPv6 |
Update Your Droplet
SSH into your new Droplet (use the IP shown in the DigitalOcean control panel or your Reserved IP) and prepare the system:Press
Y when prompted to confirm package upgrades. This may take a few
minutes.Step 1: Install AdGuard Home
AdGuard Home provides the web interface, ad-blocking, and DNS query logging.Initial Configuration
- Navigate to
http://[YOUR_EXTERNAL_IP]:3000in your browser - Proceed through the setup wizard to Step 3
DNS Settings Configuration
Configure Settings → DNS Settings with these values:| Setting | Value | Why? |
|---|---|---|
| Upstream DNS servers | https://dns.cloudflare.com/dns-query | Temporary until Unbound is installed |
| Mode | ☑ Parallel requests | Best stability even with one upstream |
| Fallback DNS servers | https://dns.cloudflare.com/dns-query | Backup if primary fails |
| Rate limit | 15 | Limits queries per second per client (DoS protection) |
| Subnet prefix length (IPv4) | 32 | Tracks individual devices, not subnets |
| DNSSEC | ☑ Enable DNSSEC | Validates DNS responses aren’t tampered |
| IPv6 addresses | ☑ Disable resolving | Prevents timeouts if IPv6 isn’t configured |
| Setting | Value | Why? |
|---|---|---|
| Blocked response TTL | 86400 (24 hours) | How long clients cache blocked domains |
| Cache size | 20000000 bytes (≈19MB) | Initial cache; we’ll optimize later with Unbound |
| Override minimum TTL | 300 (5 minutes) | Prevents too-frequent re-queries |
| Override maximum TTL | 86400 (24 hours) | Limits stale data |
| Optimistic Caching | ☑ Enable | Serves cached data while refreshing in background |
We’ll reconfigure caching in Step 3 after installing Unbound for optimal
performance.
Add Blocklists
Go to Filters → DNS Blocklists and add these lists:- hapara.fail Blocklist
- HaGeZi Normal
- Name:
hapara.fail Blocklist - URL:
https://cdn.jsdelivr.net/gh/hapara-fail/blocklist@main/blocklist.txt
Step 2: Set Up Unbound (Recursive Resolver)
Unbound replaces third-party DNS providers by querying root servers directly, enhancing privacy and control.Install Unbound
Configure Unbound
We’ll run Unbound on port5335 to avoid conflicts with AdGuard Home (which uses port 53).
-
Create the configuration file:
- Paste this optimized configuration:
- Save and exit (
Ctrl+X,Y,Enter)
Download Root Hints
Root hints tell Unbound where the authoritative root DNS servers are:This cron job runs at midnight on the 1st of each month.
Start and Test Unbound
-
Restart the service:
-
Check service status:
✅ Success: You should see
active (running) -
Test DNS resolution:
✅ Success: Look for
status: NOERRORand an IP address in theANSWER SECTION
Step 3: Connect AdGuard Home to Unbound
Now we’ll point AdGuard Home to use your local Unbound instance instead of Cloudflare.Update Upstream DNS
- In AdGuard Home, go to Settings → DNS Settings
- Upstream DNS servers:
- Delete all existing entries
- Add only:
127.0.0.1:5335
- Parallel requests: Select Parallel requests (recommended for stability)
- Bootstrap DNS servers: These resolve IPs for DNS-over-HTTPS/TLS hostnames (not needed for our IP-based upstream, but good practice):
- Private Reverse DNS servers: (Optional) Point to your router if you want local hostname resolution (e.g.,
192.168.1.1) - DNSSEC: ☑ Enable (Unbound validates, but this provides a second check)
- Click Test Upstreams → Should show “Server is working” ✅
- Click Apply
Optimize Cache Settings
Since Unbound has superior caching with prefetching, we’ll minimize AdGuard’s cache:- Go to Settings → DNS Settings → DNS Cache Configuration
- Configure:
| Setting | Value | Why? |
|---|---|---|
| Cache size | 4194304 (4MB) | Small cache keeps UI responsive |
| Override minimum TTL | 0 (empty/default) | Let Unbound control TTL |
| Override maximum TTL | 0 (empty/default) | Let Unbound control TTL |
| Optimistic caching | ☐ Disable | Unbound’s prefetching is superior |
Step 4: Secure with SSL/TLS (DoH & DoT)
Enable encrypted DNS protocols: DNS-over-HTTPS (DoH) and DNS-over-TLS (DoT).Install Certbot
Obtain SSL Certificate
Let’s Encrypt needs port 80 to verify domain ownership. We’ll temporarily stop AdGuard Home:Follow the Certbot prompts to enter your email and agree to the Terms of
Service.
Configure Encryption in AdGuard
- Go to Settings → Encryption Settings
- Configure:
| Setting | Value |
|---|---|
| Enable Encryption | ☑ Checked |
| Server Name | example.org (your domain) |
| HTTPS Port | 443 (DNS-over-HTTPS) |
| DNS-over-TLS Port | 853 (DNS-over-TLS) |
| DNS-over-QUIC Port | 853 (DNS-over-QUIC, uses UDP) |
-
Certificate & Key Paths (⚠️ Use file paths, NOT file contents):
- Certificate path:
/etc/letsencrypt/live/example.org/fullchain.pem - Private key path:
/etc/letsencrypt/live/example.org/privkey.pem
- Certificate path:
- Click Save Settings
Automate Certificate Renewal
Because AdGuard Home occupies port 80, Certbot needs AGH to be stopped before it can complete the HTTP-01 challenge. Create a pre and post hook to handle this automatically:Certbot automatically renews certificates. These hooks ensure AdGuard reloads
the new certificate without manual intervention.
Update DigitalOcean Firewall Rules
- Delete the temporary
delete-laterrule (port 3000 is no longer needed)- In the DigitalOcean control panel, go to Networking → Firewalls, open your firewall, find the Custom TCP 3000 inbound rule, and remove it.
Setup Complete!
Your DNS server is now fully operational with:- Privacy: Direct recursive resolution via Unbound
- Security: DNSSEC validation, encrypted protocols (DoH/DoT/DoQ)
- Ad-blocking: Custom blocklists via AdGuard Home
- Automation: Auto-renewing SSL certificates
Need help? Check the AdGuard Home
documentation or the Unbound
documentation.