• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

Nerdgineer

One engineer's take on the world as a nerd

  • Home
  • About
  • Upcoming rides

Technologist

Build Your Own Private VPN with Ad-Blocking DNS (Pi-hole + WireGuard)

Shared on 2026-06-15

If you’ve ever wanted your own personal “internet filter” that blocks ads and trackers no matter where you are — at home, on coffee shop Wi-Fi, or roaming on your phone — this guide is for you.

We’re going to set up a small Linux server that does two things:

  1. Pi-hole — blocks ads and trackers for any device that uses it as a DNS server.
  2. WireGuard (via the friendly installer PiVPN) — creates a private, encrypted tunnel from your devices back to this server, so you can use Pi-hole’s filtering from anywhere, and the server itself isn’t directly exposed to the internet for anything except the VPN.

By the end, your server will quietly sit there doing one job: only devices connected through your VPN can use it, and everything they look up gets filtered through Pi-hole.

This guide assumes you’re starting with a fresh installation of a Debian-based Linux distribution (such as Ubuntu Server or Raspberry Pi OS) and that you can access it over SSH or a terminal.

What you’ll need

  • A small always-on computer or server (a Raspberry Pi, an old laptop, or a cheap cloud VPS all work)
  • A fresh install of a Debian/Ubuntu-based OS
  • Basic comfort typing commands into a terminal (we’ll explain each one)
  • About 30–45 minutes

Step 1: Update everything

Before installing anything new, make sure your system’s software is fully up to date. This patches security holes and avoids weird compatibility issues later.

sudo apt update
sudo apt full-upgrade -y

apt update refreshes the list of available software versions, and full-upgrade actually installs the newest versions, including any that need to remove or replace older packages.


Step 2: Create a personal user account

Most Linux servers come with a root account that can do absolutely anything — including break absolutely anything. Best practice is to create a normal user account for yourself and use sudo (which temporarily grants admin rights) only when needed.

Create a new user, replacing <username> with whatever name you’d like:

sudo adduser <username>

You’ll be asked to set a password and optionally fill in some details (these can be left blank).

Now add that user to the sudo group, which allows it to run admin commands:

sudo usermod -aG sudo <username>

Switch to your new account and test it

su - <username>
sudo apt update

If sudo apt update runs without complaining about permissions (it will ask for your password the first time), everything’s working correctly. From now on, do all your work logged in as this user, not as root.

(Optional but recommended) Disable the root account

Since you now have a sudo-capable user, there’s no need to log in as root directly. Disabling direct root logins is a simple security improvement:

sudo passwd -l root

This “locks” the root account so nobody can log in with a root password. Your <username> account can still use sudo to perform admin tasks as needed.

If you ever need to re-enable it (for troubleshooting, for example), you can run:

sudo passwd -u root

Step 3: Install Pi-hole

Pi-hole is the piece that blocks ads and trackers. It works by acting as your network’s DNS server — when a device asks “what’s the address for adserver.example.com?”, Pi-hole checks it against block lists and refuses to answer if it’s a known ad or tracker domain.

Install it with the official one-line installer:

curl -sSL https://install.pi-hole.net | bash

This launches an interactive setup wizard. Walk through the prompts — the defaults are sensible for most people. Pay attention to two things near the end:

  • The admin password it generates (or lets you set) — you’ll need this to log into the Pi-hole web dashboard.
  • The upstream DNS provider you choose — this is the DNS server Pi-hole forwards allowed requests to. If you plan to follow the optional Unbound step below, don’t worry too much about this choice now; we’ll point Pi-hole at Unbound afterward.

If you’d prefer to read through the full official instructions first, they’re available on the Pi-hole website.

Once installed, you can reach the Pi-hole dashboard at http://<your-server-ip>/admin (only from devices on your VPN once we finish setting things up).


Step 4 (Optional): Add Unbound for fully private DNS lookups

By default, Pi-hole forwards the DNS lookups it doesn’t block to an upstream provider like Cloudflare or Google. That’s fine, but it means those providers can still see which sites you’re visiting (even if your ISP can’t).

Unbound is a small program that lets your server look up domain names directly from the source — talking straight to the authoritative DNS servers for each website, rather than going through a third-party DNS provider. This step is entirely optional, but it’s a nice privacy upgrade.

Install Unbound

sudo apt install unbound

Create a configuration file for Pi-hole to use

Create a new file at /etc/unbound/unbound.conf.d/pi-hole.conf:

sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf

Paste in the following configuration, then save and exit (in nano, press Ctrl+O to save and Ctrl+X to exit):

server:
    # If no logfile is specified, syslog is used
    # logfile: "/var/log/unbound/unbound.log"
    verbosity: 0

    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-udp: yes
    do-tcp: yes

    # May be set to no if you don't have IPv6 connectivity
    do-ip6: yes

    # You want to leave this to no unless you have *native* IPv6. With 6to4 and
    # Teredo tunnels your web browser should favor IPv4 for the same reasons
    prefer-ip6: no

    # Use this only when you downloaded the list of primary root servers!
    # If you use the default dns-root-data package, unbound will find it automatically
    #root-hints: "/var/lib/unbound/root.hints"

    # Trust glue only if it is within the server's authority
    harden-glue: yes

    # Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
    harden-dnssec-stripped: yes

    # Don't use Capitalization randomization as it is known to cause DNSSEC issues sometimes
    use-caps-for-id: no

    # Reduce EDNS reassembly buffer size to avoid issues with fragmented UDP packets
    edns-buffer-size: 1232

    # Perform prefetching of close-to-expired cache entries for frequently queried domains
    prefetch: yes

    # One thread is sufficient for most small setups
    num-threads: 1

    # Ensure the kernel buffer is large enough to avoid losing messages during traffic spikes
    so-rcvbuf: 1m

    # Keep local network addresses private
    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

    # Prevent reverse lookups for non-public IP ranges (per RFC6303)
    private-address: 192.0.2.0/24
    private-address: 198.51.100.0/24
    private-address: 203.0.113.0/24
    private-address: 255.255.255.255/32
    private-address: 2001:db8::/32

Now restart Unbound so it picks up the new configuration:

sudo systemctl restart unbound

You can do a quick test to make sure it’s working:

dig pi-hole.net @127.0.0.1 -p 5335

If you get back a response with an ANSWER SECTION, Unbound is working correctly.

Point Pi-hole at Unbound

Now tell Pi-hole to send its lookups to Unbound instead of an external provider:

  1. Open the Pi-hole dashboard (http://<your-server-ip>/admin) and log in.
  2. Go to Settings → DNS.
  3. Under Upstream DNS Servers, untick any pre-selected providers (like Google or Cloudflare).
  4. Tick Custom 1 (IPv4) and enter: 127.0.0.1#5335
  5. Scroll down and click Save.

Pi-hole will now use Unbound for any lookups that aren’t blocked, keeping your DNS queries private end-to-end.


Step 5: Install WireGuard using PiVPN

WireGuard is a modern, fast, and secure VPN protocol. PiVPN is a friendly installer script that sets up WireGuard for you and handles generating client configurations — without it, configuring WireGuard by hand involves a fair bit of manual key management.

Install it with:

curl -L https://install.pivpn.io | bash

This also launches an interactive installer. A few notes on the prompts:

  • It will automatically detect your existing Pi-hole installation and offer to configure your VPN clients to use Pi-hole for DNS automatically — say yes to this.
  • You’ll be asked to confirm the public IP address or domain name your devices will use to reach this server. If your server doesn’t have a static IP (most home internet connections don’t), consider setting up a free dynamic DNS hostname (e.g. via DuckDNS or No-IP) so the address doesn’t change on you.
  • You’ll choose a port for WireGuard — the default (51820/UDP) is fine unless you have a reason to change it.

Once installation finishes, you can create a configuration file for each device (phone, laptop, etc.) with:

pivpn add

This generates a .conf file you can transfer to your device, or — even easier — generate a QR code for mobile devices with:

pivpn -qr

Scan the QR code with the WireGuard app on your phone, and it’s instantly configured.


Step 6: Lock down the firewall

This is the most important step from a security standpoint. The goal is:

  • Allow WireGuard VPN connections in from the internet (so you can connect from anywhere).
  • Allow SSH so you can keep managing the server (ideally only from trusted networks, or via your VPN once it’s set up).
  • Block everything else from the outside world — especially the DNS, HTTP, and HTTPS ports that Pi-hole and its dashboard use. These should only ever be reachable through the VPN tunnel, never directly from the internet.

We’ll use UFW (Uncomplicated Firewall), which is a much friendlier front-end for Linux’s firewall rules than editing raw iptables files.

Install UFW (if it isn’t already)

sudo apt install ufw

Allow the essentials

sudo ufw allow OpenSSH
sudo ufw allow 51820/udp

The first command keeps your SSH access working (so you don’t lock yourself out!). The second opens the WireGuard port (adjust the number if you chose a different port during the PiVPN setup) so your devices can connect to the VPN.

Confirm DNS, HTTP, and HTTPS are NOT open to the world

By default, UFW blocks everything that isn’t explicitly allowed, so as long as you haven’t added rules for ports 53 (DNS), 80 (HTTP), or 443 (HTTPS), they’re already inaccessible from the public internet. You can double check with:

sudo ufw status verbose

You should only see rules for SSH and your WireGuard port. If you see anything for ports 53, 80, or 443, remove those rules — Pi-hole’s DNS and dashboard should only be reachable from inside the VPN’s private network range (PiVPN sets this up automatically as part of the WireGuard interface).

Enable the firewall

sudo ufw enable

You’ll be warned that this might disrupt existing SSH connections — as long as you’ve allowed OpenSSH above, you’ll be fine. Type y to confirm.


Step 7: Test everything

  1. Connect to your VPN from a phone or laptop using the config or QR code from pivpn -qr.
  2. Once connected, visit http://<your-server-ip>/admin — you should be able to reach the Pi-hole dashboard.
  3. Try browsing to a known ad-heavy site and check the Pi-hole dashboard’s Query Log — you should see blocked requests rolling in.
  4. Disconnect from the VPN and confirm you can no longer reach the Pi-hole dashboard or resolve DNS through the server’s public IP — this confirms it’s properly locked down.

Wrapping up

You now have a private, ad-blocking DNS server that’s only reachable through an encrypted VPN tunnel. A few ideas for where to go next:

  • Set up dynamic DNS if your home IP address changes periodically, so your VPN connection details don’t go stale.
  • Explore Pi-hole’s block lists (Settings → Blocklists) to fine-tune how aggressive the filtering is.
  • Consider enabling automatic security updates (unattended-upgrades) so your server stays patched without manual effort.

Happy (and ad-free) browsing!

Filed Under: Technologist

Reset Nginx Proxy Manager Password

Shared on 2025-02-21

I run Nginx Proxy Manager, or NPM for short, at home for the media stack. I haven’t logged in for a while, and clearly had forgotten my password for my NPM admin account. So how does one reset it?

I found a tutorial or two to follow, but neither of those seemed to work for me, and added a step or two that I thought unnecessary, and have since discovered were unnecessary.

So NPM uses SQLite3 by default, but can be setup with PostgreSQL and MariaDB/MySQL. My setup uses SQLite, and within that they use the BCrypt hashing algorithm of passwords for security.

I found a BCrypt generator online, and opted for the default setting of 12 rounds of hashing of my password. Initially I tried one as I wasn’t sure how that would go, but I found that 12 worked for me.

Once I had the BCrypt hash of my password, I used SQLite Manager to open my database, and ran the following SQL command to insert the password hash into the database.

UPDATE auth SET secret="<BCrypt-Hash>";

Once I had run that code, I restarted the NPM Docker container to ensure it was loading the database, and tried logging in. It worked like a charm.

So updating a forgotten password for NPM running on SQLite is as simple as updating the password hash through the backend. Assuming you have access to it obviously.

There you go, hope that works for you!

Filed Under: Technologist

Install Canon EOS Utility 2.x

Shared on 2024-11-15

I purchased a Canon EOS 500D DSLR in 2010 as a reward to myself for graduating from university and being employed in my first full-time graduate engineering role.

Fourteen years later, I have taken the next step to upgrading my camera technology.

However I am curious what the shutter count is on the 500D. There are websites that read the shutter count from a photo taken with the camera, however it seems that the 500D does not embed this information into the images that it takes.

So how do I get the shutter count, through the Canon EOS Utility. However the only version I can download is an updater, and when I run the updater it says that it requires the original install files. However I was not able to locate the necessary install file.

One fairly quick Google search had a solution that seemed very plausible. Add something to the Windows Registry, and the EOS Utility updater becomes a full install.

To create a new install of the EOS Utility 2.x, run the following from the the command line as an Administrator.

REG ADD "HKLM\SOFTWARE\WOW6432Node\Canon\EOS Utility"

Afterwards run the EOS Utility updater and it will treat the file as the original install files.

Filed Under: Technologist

Legacy Keyring Deprecated

Shared on 2024-05-12

In the process of setting up a new Raspberry Pi router, I discovered that I had a repository key stored in the wrong location on PiOS Bookworm.

The key is stored in an old location that has since been updated, however it is flagged as an issue when updating software through Apt.

Identifying the Error

After running the following:

sudo apt update

I received the following line saying that a key was deprecated.

41 packages can be upgraded. Run 'apt list --upgradable' to see them.
W: http://raspbian.raspberrypi.com/raspbian/dists/bookworm/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.

The updating of software will still continue, but everytime I run the update command, I received the error.

Digging around the internet archives and numerous websites, I came across the following solution. While this isn’t my solution, I’m sharing here for when I need the solution in the future.

What is the problem?

Keys were previously stored in:

/etc/apt/trusted.gpg

They are now stored in:

/etc/apt/trusted.gpg.d/

In Bookworm, most of the keys have been migrated to the new location, however one key seems to remain in the old location.

Steps to Solve

Since the old location is only deprecated, it is still checked when running any of the apt commands. So we need to move the key to the new location, however if we simply copy it to the new location, then remove the key, both keys will be removed and we will be further behind than where we are.

Step One

We need to list the key so we know which one to copy.

sudo apt-key list | grep -A4 "trusted.gpg$"

you will receive something similar to the following:

Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).
/etc/apt/trusted.gpg
--------------------
pub   rsa2048 2012-04-01 [SC]
      A0DA 38D0 D76E 8B5D 6388  7281 6591 938D FD90 EE2D
uid           [ unknown] Mike Thompson (Raspberry Pi Debian armhf ARMv6+VFP) <mpthompson@gmail.com>

We need to note down the last 8 hexadecimal characters of the key (in this instance FD90EE2D) for use in the next couple of commands.

Step Two

We copy the key to a temporary location to let us remove the original using appropriate commands.

sudo apt-key export FD90EE2D | sudo gpg --dearmor -o /tmp/raspi.gpg

Double check that the key has been copied:

file /tmp/raspi.gpg 

You should receive something similar to the following. If not, double check you typed the last 8 characters of the key correctly. I made this mistake first time. It is relatively easy to do if you type it out.

/tmp/raspi.gpg: OpenPGP Public Key Version 4, Created Sun Apr 1 21:02:33 2012, RSA (Encrypt or Sign, 2048 bits); User ID; Signature; OpenPGP Certificate

Now delete the old key:

sudo apt-key del FD90EE2D

Step Three

The third and final step is to move the exported key into the correct location.

sudo mv /tmp/raspi.gpg /etc/apt/trusted.gpg.d/

That’s all folks

Once you have completed all the above steps, I run the ”’apt update”’ command again to check that the key has been moved, and I am no longer reciving the ”’DEPRECATED”’ comments.

You should been good to go.

Filed Under: Technologist

Install Driver for TP-Link TL-WN823N on Raspberry Pi OS

Shared on 2023-05-22

I’m looking to create a WiFi hotspot for when we travel. My current idea is to create a WiFi router using a Raspberry Pi as a bridge between our devices (Laptops, Phones, iPads, etc.) and the accomodation’s WiFi (especially where they have captive portals with number of device limitations).

I have a spare Raspberry Pi 2 V1.1 lying around at home, and looking to utilise that going forward. Clearly the RPi 2 does not have onboard WiFi, so a pair of USB dongles is required. However the number of dongles is broad and varied, and driver compatibility can be somewhat limited, finding suitable devices can be a challenge. Hence my search for a suitable dongle.

I have downloaded the latest version of Raspberry Pi OS, and updated with the following:

sudo apt update
sudo apt upgrade 

At the time of writing this article, RPi OS was reporting it was using version 6.1.24-v7+ of the Linux kernel.

The WiFi dongle that I purchased is a TP-Link TL-WN823N wireless USB adapter. I purchased it from Officeworks here in Australia.

The TL-WN823N wireless adapter is based on the RealTek rtl8192eu drivers. These are not present within the Linux kernel at present to my knowledge, and I had to compile them to get the dongle recognised by the operating system.

My first port of call was Google. There are clearly numerous sources, however they all point back to one main source of knowledge, a blog post from Junaid’s Blog.

My first attempt at the following the steps from Junaid’s Blog resulted in an install that was recognised, but I could not get a working connection. Junaid’s Blog points to a rtl8192eu linux driver that allegedly works with the kernel up to version 4.9.35. Clearly I am beyond that running version 6.1.24.

I thought I was shit out of luck at that point. However I did some further digging on a number of forums and found something a little more recent.

My searching directed me towards a Github repository containing a more recent rtl8192eu driver that works with the Linux Kernel up to version 6.4.

Now I followed the following steps to get the driver up and running, and actually working.

sudo apt install linux-headers-generic build-essential dkms git
cd ~
mkdir drivers && cd drivers
git clone https://github.com/clnhub/rtl8192eu-linux.git 
cd rtl8192eu-linux

Inside this folder is the file Makefile, we need to edit the contents of this for Raspberry Pi. We need to find the following line:

CONFIG_PLATFORM_I386_PC = y

and change it to

CONFIG_PLATFORM_I386_PC = n

Then find this line

CONFIG_PLATFORM_ARM_RPI = n

and change it to

CONFIG_PLATFORM_ARM_RPI = y

Now the easiest option from here is to run the included shell script file which does all the hard work for us. Run the following command:

./install-wifi.sh

Let the Raspberry Pi do its thing for a few minutes, and Hey Presto! you now have a working TL-WN823N USB dongle that can connect to any IEEE 802.11b/g/n network.

I am yet to test whether I can run a WiFi hotspot from it, and I am yet to install Pi-Hole as a local Domain Name Server to maintain some control over the kids and their devices, but they are the next steps in putting together a WiFi router for when we are travelling.

Filed Under: Technologist

Primary Sidebar

About the author

Hello everyone, my name is Andrew. I am a nerd and an engineer. So welcome to Nerdgineer, the blog where I share the details of my journey called life as I attempt to understand life as a whole.

Categories

Copyright © 2026 · Metro Pro on Genesis Framework · WordPress · Log in