Build a Network-Wide Ad Blocker from Scratch
The Master Guide: Build a Network-Wide Ad Blocker from Scratch
If you're exhausted by popup ads, invasive tracking scripts, and video commercials slowing down your Wi-Fi, you’re in the right place.
Browser extensions are great for your laptop, but they can't help your smart TV, your phone apps, or your smart home devices. The ultimate solution is to block these ads at the source—before they ever reach your devices.
In this guide, we are going to build a network-wide ad blocker (Pi-hole). We’ll host it on a Fedora Linux machine, and then we’ll configure your Tenda router so that every single device in your house automatically uses it.
Even if you have never touched Linux or a terminal before, don't worry. I've broken this down into simple, copy-and-paste steps. If you follow along carefully, you'll have a blazing-fast, ad-free network in about 15 minutes.
Let’s dive in.
The Game Plan
Here is exactly what we are going to do:
Gather our tools: Install the necessary software on your machine.
Prep the host: Run a script that configures Fedora's(I'm using) network ports and sets up Pi-hole.
Automate the router: Create a Python script that physically logs into your router and updates the settings for you.
Turn it on: Execute the scripts and set up your admin dashboard.
Plug the leaks: Tweak your phone/browser settings so they don't bypass your new blocker.
Block anything: Learn how to nuke specific sites (like TikTok) from your entire home.
Part 1: Install the Required Tools
First, we need to install the background engines that will run our ad blocker and automate our router.
Open your Fedora terminal (press the Windows / Super key, type Terminal, and press Enter).
Copy the block of text below, paste it into your terminal, and press Enter. It will ask for your computer's password.
Bash: I'm using Fedora, so I'll show exactly what I did.
# 1. Update Fedora's software list and install Podman (to run Pi-hole) and Python Pip
sudo dnf install -y podman python3-pip
# 2. Install Playwright (a tool that lets Python click buttons on web pages)
pip install playwright
# 3. Download the automated Chromium browser for Playwright to use
python3 -m playwright install chromium
Why are we installing this?
Podmanis Fedora's native, lightweight engine for running background apps like Pi-hole.PythonandPlaywrightwill be used later to act as a "ghost user" that logs into your router and changes the settings for you!
Part 2: Create the Setup Script (deploy_pihole.sh)
By default, your Fedora computer uses a specific "door" (Port 53) for its own internet routing. Pi-hole needs to use that exact same door to listen to your whole network.
Instead of making you type out twenty different commands to fix this, open the firewall, and configure Pi-hole, I've bundled it all into one script.
In your terminal, copy and paste this entire block and hit Enter. It will create a file named deploy_pihole.sh:
Bash
cat << 'EOF' > deploy_pihole.sh
#!/usr/bin/env bash
set -euo pipefail
echo "[INFO] Starting Pi-hole deployment on Fedora Workstation..."
# 1. Check Root Privileges
if [[ $EUID -ne 0 ]]; then
echo "[ERROR] This script must be run as root or with sudo."
exit 1
fi
# 2. Detect Fedora Host Static IP
HOST_IP=$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{print $7; exit}')
if [[ -z "$HOST_IP" ]]; then
HOST_IP=$(hostname -I | awk '{print $1}')
fi
echo "[INFO] Detected Host IP Address: ${HOST_IP}"
# 3. Reconfigure systemd-resolved to release Port 53
echo "[INFO] Reconfiguring systemd-resolved drop-in..."
mkdir -p /etc/systemd/resolved.conf.d
cat << 'RESOLV_EOF' > /etc/systemd/resolved.conf.d/pihole.conf
[Resolve]
DNS=1.1.1.1 8.8.8.8
DNSStubListener=no
RESOLV_EOF
# Update /etc/resolv.conf symlink
rm -f /etc/resolv.conf
echo "nameserver 1.1.1.1" > /etc/resolv.conf
echo "nameserver 8.8.8.8" >> /etc/resolv.conf
# Restart systemd-resolved
systemctl restart systemd-resolved
echo "[SUCCESS] systemd-resolved reconfigured."
# 4. Open Firewalld Ports
echo "[INFO] Configuring firewalld rules..."
if ! systemctl is-active --quiet firewalld; then
systemctl enable --now firewalld
fi
firewall-cmd --permanent --add-port=80/tcp || true
firewall-cmd --permanent --add-port=53/tcp || true
firewall-cmd --permanent --add-port=53/udp || true
firewall-cmd --reload
echo "[SUCCESS] Firewalld rules updated."
# 5. Create Podman Quadlet Service Definition
echo "[INFO] Registering Podman Quadlet container..."
mkdir -p /etc/containers/systemd
mkdir -p /etc/pihole /etc/dnsmasq.d
cat << QUADLET_EOF > /etc/containers/systemd/pihole.container
[Unit]
Description=Pi-hole DNS Ad Blocker Container
After=network-online.target systemd-resolved.service
Wants=network-online.target
[Container]
Image=docker.io/pihole/pihole:latest
ContainerName=pihole
Environment=TZ=UTC
Environment=FTLCONF_LOCAL_IPV4=${HOST_IP}
Environment=PIHOLE_DNS_=1.1.1.1;1.0.0.1
Volume=/etc/pihole:/etc/pihole:Z
Volume=/etc/dnsmasq.d:/etc/dnsmasq.d:Z
PublishPort=53:53/tcp
PublishPort=53:53/udp
PublishPort=80:80/tcp
Network=host
[Service]
Restart=always
TimeoutStartSec=300
[Install]
WantedBy=multi-user.target default.target
QUADLET_EOF
# 6. Start Pi-hole Service via Systemd
echo "[INFO] Reloading systemd daemon and starting pihole service..."
systemctl daemon-reload
systemctl start pihole
echo "=================================================================="
echo "[SUCCESS] PI-HOLE DEPLOYED SUCCESSFULLY!"
echo " Web Admin URL: http://${HOST_IP}/admin"
echo " Primary DNS IP: ${HOST_IP}"
echo "=================================================================="
EOF
chmod +x deploy_pihole.shPart 3: Create the Router Automation Script (tenda_dns_updater.py)
For Pi-hole to work, your Tenda router needs to tell every phone and laptop in your house to use the Fedora machine for DNS lookups. Normally, you'd have to log into your router, dig through the settings, and type this in manually.
Instead, this Python script uses Playwright to open an invisible browser, log into your router's web portal, find the exact setting, type in your Fedora computer's IP address, and hit save.
Copy and paste this into your terminal:
Bash
cat << 'EOF' > tenda_dns_updater.py
#!/usr/bin/env python3
import argparse
import sys
import time
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s')
logger = logging.getLogger("TendaUpdater")
def update_router(router_ip, password, pihole_ip):
from playwright.sync_api import sync_playwright
url = f"http://{router_ip}"
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={"width": 1280, "height": 800})
logger.info(f"Connecting to Tenda Router at {url}...")
page.goto(url, wait_until="domcontentloaded", timeout=15000)
page.wait_for_timeout(3000)
# 1. Login
pwd_sel = 'input[placeholder="Login Password"]'
if page.is_visible(pwd_sel, timeout=3000):
page.fill(pwd_sel, password)
page.click('button:has-text("Login")')
page.wait_for_timeout(4000)
logger.info("Logged into Tenda router successfully.")
def get_contexts():
ctxs = [page]
for f in page.frames:
if f != page:
ctxs.append(f)
return ctxs
# 2. Navigate to Administration / LAN Parameters
menu_items = ['text="Administration"', 'text="Advanced"', 'text="Internet Settings"']
dns_input = None
target_ctx = None
for m in menu_items:
try:
if page.is_visible(m, timeout=2000):
page.click(m)
page.wait_for_timeout(3000)
except Exception:
continue
for ctx in get_contexts():
for sel in ['#sys_dns1', '#preferredDns', '#dns1', '#lanDns1', 'input[name*="dns" i]']:
try:
if ctx.is_visible(sel, timeout=1000):
dns_input = sel
target_ctx = ctx
break
except Exception:
continue
if not dns_input:
try:
loc = ctx.locator('tr:has-text("Preferred DNS") input, div:has-text("Preferred DNS") input').first
if loc.is_visible(timeout=1000):
dns_input = loc
target_ctx = ctx
break
except Exception:
continue
if dns_input:
break
if dns_input:
break
if not dns_input or not target_ctx:
logger.error("Could not find Preferred DNS field on router web page.")
browser.close()
return False
# 3. Update DNS Field
logger.info(f"Setting Preferred DNS to {pihole_ip}...")
if isinstance(dns_input, str):
target_ctx.fill(dns_input, "")
target_ctx.fill(dns_input, pihole_ip)
else:
dns_input.fill("")
dns_input.fill(pihole_ip)
# 4. Click Save
for save_btn in ['#submit', '#save', 'button:has-text("Save")', 'input[value="Save"]']:
try:
if target_ctx.is_visible(save_btn, timeout=1000):
target_ctx.click(save_btn)
logger.info("Saved router DNS configuration.")
break
except Exception:
continue
page.wait_for_timeout(4000)
browser.close()
return True
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--router-ip", default="192.168.0.1")
parser.add_argument("--password", default="admin")
parser.add_argument("--pihole-ip", required=True)
args = parser.parse_args()
if update_router(args.router_ip, args.password, args.pihole_ip):
logger.info("[SUCCESS] Tenda router Primary DNS updated to Pi-hole!")
sys.exit(0)
else:
logger.error("[FAILURE] Could not auto-update router DNS. Please configure manually.")
sys.exit(1)
EOF
Part 4: Let's Fire It All Up!
You now have the tools and the scripts. It's time to actually run them. Just run these three commands one by one.
1. Run the host setup script:
Bash
sudo bash deploy_pihole.sh(You will see a bunch of text fly by, ending with a green success message showing your Fedora IP address).
2. Create your dashboard password:
You need a password to view your network statistics. Run this command (feel free to change 123456 to whatever password you want):
Bash
sudo podman exec -it pihole pihole setpassword '123456'3. Run the router automation script:
Finally, let's tell the router to use your new setup. Run this exact block of code:
Bash
HOST_IP=$(ip -4 route get 1.1.1.1 | awk '{print $7; exit}')
python3 tenda_dns_updater.py \
--router-ip "192.168.0.1" \
--password "admin" \
--pihole-ip "${HOST_IP}"Important Note: If you previously changed your Tenda router's admin password to something else, make sure you replace
"admin"in the command above with your real router password!
Part 5: The "Gotcha" — Fixing Device Settings
Here is where a lot of beginners trip up. You’ve set everything up perfectly, but ads are still loading on your phone. Why?
Modern phones and web browsers have a feature called "Private DNS" or "Secure DNS" (DoH). This feature encrypts your web traffic and sends it straight to Google or Cloudflare—completely bypassing your router and your new Pi-hole.
To force your devices to use your ad blocker, you must turn this feature off.
Android Phones: Go to Settings -> Search for Private DNS -> Set it to Off.
iPhones / iPads: Go to Settings -> Tap your Apple ID name at the top -> iCloud -> Private Relay -> Turn it Off.
Chrome / Edge Browsers: Go to Settings -> Privacy and Security -> Security -> Find Use secure DNS and toggle it Off.
Part 6: How to Block Any Domain (e.g., TikTok) Network-Wide
Pi-hole blocks standard ads out of the box. But the real power is that you can completely ban specific apps or websites from your entire house.
Let's say you want to block TikTok.
Method A: The Easy Visual Way
Open your web browser and go to the address the first script gave you (usually something like
[http://192.168.0.109/admin](http://192.168.0.109/admin)).Log in with the password you set (
123456).Click Domains on the left menu.
Type in
tiktok.com.Check the box that says Add domain as wildcard (this ensures it blocks
[www.tiktok.com](https://www.tiktok.com),video.tiktok.com, etc., not just the main page).Click Add to Blocklist.
Method B: The Hacker Way (Terminal)
Apps like TikTok are sneaky and load videos from secondary servers. To completely nuke it in one go, just paste this into your terminal:
Bash
sudo podman exec -it pihole pihole deny --wild tiktok.com tiktokcdn.com byteoversea.com ibyteimg.com musical.ly ttwstatic.comFinal Verification Checklist
You're done! Let's make sure it's working:
Check the Dashboard: Open your browser to
[http://192.168.0.109/admin](http://192.168.0.109/admin)(or whatever IP the script gave you). It should load a beautiful dashboard showing an "Active" green status.Watch the Matrix: Click Query Log in the left menu of the dashboard. Pick up your phone and open a website. You will literally see your phone's requests popping up on your computer screen in real-time.
The Ultimate Test: Go visit a messy, ad-heavy news website on your phone or laptop. Enjoy the clean, lightning-fast, ad-free experience!