Introduction
Greetings! Today we will install both Pi-Hole and Unbound in a single Virtual Machine (VM) on a Proxmox Virtual Environment (VE). After that, we will configure this VM to function as both a Network Ad Blocker and Local Area Network (LAN) Recursive Caching Domain Name Service (DNS).
I attempted to run Pi-Hole and Unbound in a Linux Container (LXC) mainly because it is more resource-efficient than a Virtual Machine (VM). This approach generally works well for applications like Plex, which do not rely on system calls from the host system. However, both Pi-Hole and Unbound depend on these system calls. An LXC is lighter than a VM since these calls are part of the host system, while a VM functions with its own independent system calls. This independence is crucial for the operation of both applications.
Therefore, we will proceed with a traditional VM setup. By not including a desktop or window manager, we can significantly reduce the size and resource requirements of the VM. Although it won’t be as lightweight as an equivalent LXC, it will still be resource-friendly.
For the base operating system, we will use Debian 13 (Trixie). While Unbound has a package available in the Debian repository, we will need to install Pi-Hole directly from its website. Although this adds a few extra steps, it’s a manageable process.
Some may wonder why not use AdGuard instead. While AdGuard is a valid alternative, I prefer Pi-Hole due to my familiarity with it.
Full disclosure, I will be following, for the most part, the process found in the Unbound guide on the Pi Hole server site, at this address: https://docs.pi-hole.net/guides/dns/unbound/. I have also published a video that closely mirrors this post. It can be found here: https://youtu.be/SAzCj1owXhA?si=9NnNvMr1yaJ-l8ZM.
About DNS
Before we start building the virtual machine (VM), it’s important to briefly discuss your router, often provided by your Internet Service Provider (ISP). For instance, when I signed up with Converge ICT, I received a Huawei EG145VS, complete with custom Converge ICT software installed. While it may not be the most advanced router, its performance is adequately.
When you access your router’s settings (Log in instructions are usually indicated on the router ID sticker), you can locate the DNS server configuration. In my case, this setting was found on the DHCP page. Initially, this field was empty, indicating that it defaulted to a Converge ICT DNS resolver.
You have the option to change this setting to any number of third-party DNS Resolvers like Google or Cloud-flare. Many tutorials claim this can speed up your internet connection. It can speed up name resolution and start pages loading earlier. But speed up the internet, sorry that’s not what DNS does in any way shape or form.
One thing that is essential to remember that both ISPs and third-party providers are commercial entities. They often monetize their services by selling DNS history to businesses interested in tracking our online activities for various reasons.
While I’m not suggesting that the measures we’ll discuss here will completely eliminate tracking, they can significantly reduce it. Ultimately, there’s a limit to how much privacy can be achieved, as you will still need to access an IP address directly at some point, and that will be visible. Still, there’s no reason to make it easier.
For further privacy enhancement, using a personal VPN (as opposed to a third-party one) is a possibility, though you may reach a point of diminishing returns where the effort no longer justifies the outcome.
DNS Server Hierarchy
Because lots of numbers can be hard for some people to remember, a DNS resolver converts a domain name, like google.com, to an actual IP address, like 142.251.220.206. The DNS Resolver is the first step in finding the IP address that matches a domain name. Hence Domain Name Server.
The various DNS server types cache data, meaning they remember previous DNS lookup queries. An Authoritative Nameserver can specify a Time to Live (TTL) for Domain to IP address query results. Other DNS server types will hold onto query records in a data cache based on the TTL. This can result in faster DNS resolution for common sites. If a DNS server has a site cached it can return the answer straight instead of querying another DNS server.
DNS Recursive Resolver: These are DNS servers operated by your Internet Service Provider (ISP) or other commercial entities. They represent the lowest tier of the DNS hierarchy. Technically we will be replacing this tier with our own DNS Server.
DNS Root Server: There is a limited number of DNS Root Nameservers, managed by the Internet Corporation for Assigned Names and Numbers (ICANN), a nonprofit organization. Because there is a limited number it is not hard to maintain a list of Root Nameserver IP addresses. Root Nameservers essentially direct the DNS Recursive Resolver to query the next level or Top-Level Domain (TLD) Nameservers.
Top-Level Domain (TLD) Nameservers: These servers maintain records for various extensions, such as .com, .org, and .net. They are also indirectly managed by ICANN. A TLD Nameserver knows the location of the Authoritative Nameservers. When queried, they provide the DNS Recursive Resolver with the IP address of the proper Authoritative Name Server.
Authoritative Nameservers: These servers contain the information that translates domain names into IP addresses. They are operated by domain registrars, the companies that sell you domain names. They provide the DNS Recursive resolver with the actual IP address which is then sent back to the requesting local computer.
The Pi Hole Unbound Data flow
Let’s examine the flow of information within the Local Area Network (LAN), involving computers, routers, Pi-Hole, and Unbound:
1: A local computer wants to access a specific website, for example mahal.com. It generates a DNS request to the gateway (ISP Router), seeking the IP address for mahal.com.
2: Typically, the gateway would forward this request to a commercial DNS Resolver. However, we have redirected DNS queries to Pi-Hole.
3: Pi-Hole examines the DNS request. Since it’s a DNS request, ad blocking is bypassed. Pi-Hole then checks its local DNS records. If there is no match, it forwards the request to Unbound.
4: Unbound compares the request against its local cache and returns the IP address if a match is found. If not, it forwards the request to the appropriate DNS Root NameServer.
5: Because Unbound already knows the IP address of the DNS Root Server, it sends the request directly to the IP address rather than using a domain name, eliminating the need for an additional DNS resolution. Unbound traverses the DNS hierarchy until it finds the IP address corresponding to the domain name. This address is then returned to the original local computer, which can now access mahal.com at 209.4.185.1.
What is Ad Blocking
Before we proceed with the build, let’s briefly explore how Pi-Hole ad blocking functions. Once a domain is resolved to an IP address, the local computer directly accesses the remote server. In this context, we will focus specifically on web browsing.
The remote server sends a web page to the local computer, which is then parsed and displayed in the local browser. This web page contains links to other Fully Qualified Domain Names (FQDN) that the browser will attempt to resolve. Consequently, a DNS request is generated and sent through the gateway router to Pi-Hole.
Pi-Hole enables users to maintain lists of questionable domain names. These questionable domains typically serve advertisements, track user activity, and may distribute malware. The default list that we will pre-install includes approximately 70,000 domains, with additional lists available for users.
Pi-Hole examines the embedded links within the requested web page that it is trying to resolve. If it identifies a match from its list of questionable sites, that link is blocked immediately, preventing further access. If there’s no match, Pi-Hole will forward the request for DNS resolution. As a result, any questionable links in the web page are dropped and do not get resolved, leading to empty spaces where content was intended to appear.
LXC vs VM
As outlined in the introduction, we will use a Virtual Machine (VM) rather than a Linux Container (LXC). Understanding the rationale behind this decision is essential.
A VM differs from an LXC, as it comes equipped with a complete Operating System (OS) and relies on the host for container management and hardware emulation only, operating independently of the host system.
In contrast, LXCs do not have a complete OS, and are dependent on the host system. While this dependency improves resource efficiency, it requires read access to the host OS. This can create challenges for applications needing both read and write permissions. For example, Pi-Hole’s NTP function and Unbound’s system-level DNS access requirment.
Given these factors, there are three potential paths to consider, each with distinct advantages and drawbacks:
- Modify the Application for LXC: This approach is the most resource-efficient. However, adapting applications to run within an LXC requires advanced technical expertise, which not everyone possesses. Furthermore, updates may disrupt any changes made.
- Run the Application in a VM: This option allows the application to function with its own system resources, independent of the host. However, it consumes more resources compared to an LXC, which might not be feasible for systems with limited capabilities.
- Elevate LXC Privileges: This entails granting read and write access to the host system, which compromises the isolation that LXCs provide. This approach also poses security risks and could interfere with other containers that rely on shared system resources.
Each of these options presents unique challenges and benefits, highlighting the necessity for careful consideration based on specific application needs and operational contexts.
VM Debian 13 Setup
Our next task is to gather the necessary information for creating the Virtual Machine (VM) and install the operating system (OS). Keep in mind that your configuration may vary from mine, depending on your hardware and specific needs. For a basic Debian 13 installation, running Pi-Hole and Unbound, I recommend the following specifications: 2 CPU Cores, 2 GB of Memory, 10 GB of Storage
You will also need to provide additional information, including: A name, domain, ID number, static IP address, root user password, and non-privileged user name and password
After the setup is complete, we’ll install sudo and curl. Sudo, along with the OpenSSH-server (which is part of my default installation), enables us to log in via SSH and operate as an unprivileged user. Curl is essential for the Pi-hole installation.
Since the primary focus of this video is not on the VM creation and configuration, we’ll move through this quickly. I have additional videos available if you require more detailed information on this subject.
Pi Hole Install
Following some previous examples, like the ExpressVPN Linux GUI, our first task is to download and review the Pi-Hole installation script without executing it. By examining the script, you will find sections that identify your hardware and system type. The script will request sudo permissions to complete the installation, allowing it to be run by an unprivileged user.
At the end of the script, you will receive a IP address, port number, and password for accessing the Pi-Hole GUI. Make sure to write these down to avoid losing them.
The Pi-Hole installation instructions suggest adding the unprivileged user to the Pi-Hole group. Although I haven’t found any benefit in doing so, it doesn’t present any issues either.
Before transitioning from the terminal to the web GUI, you can change the default password assigned to you with the command: pihole setpassword. You may also want to update the Pi-Hole software using the command: pihole updatePihole.
Since the installer typically fetches the latest version, this update command might not have any immediate effect. Nonetheless, you should establish a regular schedule for checking updates.
Finally, I recommend rebooting your system before attempting to access the web interface.
Unbound Install
With root permissions run “apt install unbound”. Once unbound is installed, we need to create the configuration file /etc/unbound/unbound.conf.d/pi-hole.conf. The root configuration file in the unbound folder runs every file in unbound.conf.d, so this file will be picked up automatically when restart.
I am using a variation of the configuration file found at https://docs.pi-hole.net/guides/dns/unbound. Some items are not necessary. There are also some items I will not be adding, as I am unsure of their effect at this point. They are for later testing. A couple of examples of this are hide-version, hide-identity, and some of the commands affecting time to live (TTL) of DNS resolutions in the cache file.
server: logfile: "/var/log/unbound/unbound.log" verbosity: 0 interface: 127.0.0.1 port: 5335 do-ip4: yes do-udp: yes do-tcp: yes do-ip6: no prefer-ip6: no harden-glue: yes harden-dnssec-stripped: yes use-caps-for-id: no edns-buffer-size: 1232 prefetch: yes num-threads: 1 so-rcvbuf: 1m private-address: 172.16.0.0/12
Connect Pi-Hole to Unbound
The next step is to link Pi-Hole and Unbound. Since both applications are installed on the same virtual machine, this process is quite straightforward.
Recall that we previously configured Unbound to listen on the internal IP address 127.0.0.1 and port 5335 (using a non-standard DNS port to avoid any confusion with other DNS on port 53).
To proceed, access the Pi-Hole interface and navigate to the DNS section. If you followed the default installation, it’s likely set to use Google DNS. Uncheck the default options and scroll down to find the custom DNS box. Here, you will enter 127.0.0.1#5335 (note that the # is intentional instead of a colon).
That’s all there is to it! Now, simply restart all services, and your local computer should be fully operational with this new setup.
Redirect Router
The final step is to redirect your router DNS pointer to point at Pi-Hole. Since most consumer devices automatically point at the router for DNS service, this ensures the Pi-Hole will be active for all devices on the LAN.
Note that every router has many differences in its interface so a comprehensive step by step is not possible.
Basically the DNS entries will be within the DHCP page within the LAN section. But you may have to look around a bit to find them, depending on the interface setup.
We want to point the DNS entry to our Pi-Hole at 172.20.30.11 (on my LAN, this will be different on your LAN, depending on your setup).
Once that is done, restart your router to make sure the settings take effect. Then go to the Pi-Hole web interface and check that everything is working.
And that is it, we are done.
Conclusion
That is it. It can be a little hard to follow just reading it, so I suggest reviewing the video after reading this.
In today’s Internet, it is becoming increasingly important to take control of your network access. With the latest versions of Windows embedding advertising and AI into the operating system, your computer seems to no longer belong to you. So any steps to reduce the increasing levels of intrusion are a good thing.
I have not used any Macintosh computers in over twenty years, so cannot speak to the state of that OS. I did ditch my Iphone once I retired, so I do have some rather strong opinions there.
In don’t want to appear to be a Fan boy and say I use Linux (oops I just did!). But I do get exposed to Windows, usually to fix a computer borked by an update, malware, virus, or bug.


