← Back
- Date
🌐 Self-Hosted DNS Server (Pi-hole + Unbound)

A robust, privacy-focused DNS infrastructure combining Pi-hole for ad-blocking and Unbound for recursive DNS resolution. This setup provides complete DNS independence, enhanced privacy, and network-wide ad blocking.
🚀 Overview
This project implements a self-hosted DNS resolution stack that eliminates dependence on external DNS providers. By combining Pi-hole (network-wide ad/tracker blocking) with Unbound (recursive DNS resolver), the system provides:
- Complete Privacy: No DNS query logging by third parties
- Enhanced Performance: Local caching for faster resolution
- Ad-Free Browsing: Network-wide blocking of ads and trackers
- Security: DNSSEC validation and encrypted upstream communication
⚡ Features
🔒 Privacy & Security
- Recursive DNS Resolution: Queries start from root servers
- DNSSEC Validation: Ensures DNS response authenticity
- No Logging: Self-hosted means no third-party data collection
🚀 Performance
- Local Caching: Redundant query resolution in milliseconds
- Aggressive Caching: TTL optimization for frequent domains
📖 Detailed Setup Guide
Step 1: LXC Container Setup
# Create new LXC container (adjust ID and storage as needed)
pct create 100 debian-12-standard_12.2-1_amd64.tar.zst \
--storage local-lvm \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--memory 512 \
--cores 1
# Start container and set static IP
pct start 100
pct set 100 --net0 name=eth0,bridge=vmbr0,ip=192.168.1.10/24,gw=192.168.1.1
Step 2: Pi-hole Installation
# Update system and install Pi-hole
apt update && apt upgrade -y
curl -sSL https://install.pi-hole.net | bash
Step 3: Unbound Installation & Configuration
# Install Unbound
apt install unbound unbound-anchor -y
# Download root hints
curl -o /var/lib/unbound/root.hints https://www.internic.net/domain/named.cache
# Create unbound configuration
cat > /etc/unbound/unbound.conf.d/pi-hole.conf << 'EOF'
server:
verbosity: 1
interface: 127.0.0.1
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
prefer-ip6: no
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: yes
edns-buffer-size: 1472
prefetch: yes
num-threads: 1
so-rcvbuf: 1m
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: 172.16.0.0/12
private-address: 10.0.0.0/8
private-address: fd00::/8
private-address: fe80::/10
# Root hints
root-hints: "/var/lib/unbound/root.hints"
# DNSSEC
auto-trust-anchor-file: "/var/lib/unbound/root.key"
val-log-level: 2
# Cache settings
key-cache-size: 64m
key-cache-slabs: 4
neg-cache-size: 32m
msg-cache-size: 32m
msg-cache-slabs: 4
rrset-cache-size: 64m
rrset-cache-slabs: 4
cache-min-ttl: 3600
cache-max-ttl: 86400
# Security
unwanted-reply-threshold: 10000000
val-nsec-type: "proof"
serve-expired: yes
serve-expired-ttl: 3600
aggressive-nsec: yes
EOF
# Initialize root anchor and set permissions
unbound-anchor -a /var/lib/unbound/root.key
chown unbound:unbound /var/lib/unbound/root.hints
chown unbound:unbound /var/lib/unbound/root.key
# Start and enable Unbound
systemctl enable unbound
systemctl start unbound
# Test Unbound
dig @127.0.0.1 -p 5335 google.com
Step 4: Configure Pi-hole to Use Unbound
- Access Pi-hole admin panel:
http://your-container-ip/admin
- Navigate to Settings > DNS
- Remove all upstream DNS servers
- Add custom upstream DNS:
127.0.0.1#5335
- Enable Use DNSSEC and Use conditional forwarding if needed
Step 5: Router Configuration
Configure your router to use the Pi-hole container as the primary DNS server:
- DHCP DNS Settings: Set to your container's IP (e.g., 192.168.1.10)
🐛 Troubleshooting
Common Issues & Solutions
DNS Resolution Failing
# Test Unbound resolution
dig @127.0.0.1 -p 5335 example.com
# Check Unbound logs
journalctl -u unbound -f
Service Not Starting
# Check service status
systemctl status unbound
systemctl status pihole-FTL
# Verify configuration syntax
unbound-checkconf /etc/unbound/unbound.conf.d/pi-hole.conf
📊 Monitoring & Maintenance
# Check service status
systemctl status pihole-FTL
systemctl status unbound
# Update blocklists
pihole -g
# Update Unbound root hints
curl -o /var/lib/unbound/root.hints https://www.internic.net/domain/named.cache
systemctl restart unbound
📄 License
This project is licensed under the MIT License.
🌐 Connect
Taki Sadik
- 🖥️ GitHub: TYFSADIK
- 🌍 Website: tyfsadik.org
Last updated: March 2024
Maintained with ❤️ by Taki Sadik
⭐ If this project helped you, please consider giving it a star on GitHub!