Deployment guide

Pi-hole

Videos

Overview: Pi-hole on Ubuntu 24.04 LTS on Azure: overview (watch on YouTube)

Launch and log in: Pi-hole on Ubuntu 24.04 LTS on Azure: launch and log in (watch on YouTube)

Deep Dive: Pi-hole on Ubuntu 24.04 LTS on Azure: deep dive (watch on YouTube)

Overview

Pi-hole is an open-source, network-wide DNS sinkhole that blocks ads, trackers and malware domains for every device on your network, with no per-device software required. You point your devices, router or DHCP server at Pi-hole as their DNS server. Pi-hole answers from its bundled blocklists, sinking known ad and tracker domains and resolving everything else through an upstream resolver.

This SecureAnyCloud image installs Pi-hole 6, which ships pihole-FTL: a single service that is both a fast caching DNS resolver on port 53 and an embedded web server on port 80, hosting the admin dashboard and a REST API. There is no separate lighttpd, PHP or nginx in v6.

What you get:

  • Pi-hole 6 (pihole-FTL): a caching DNS resolver on :53 (UDP and TCP) and an embedded web server on :80, which also answers HTTPS on :443 with a self-signed certificate
  • The admin dashboard at /admin and the REST API at /api, served by pihole-FTL itself
  • Default blocklists (adlists) loaded into Pi-hole's gravity database, ready to block tens of thousands of ad and tracker domains
  • A per-VM admin password generated on first boot and recorded in a root-only file
  • pihole-FTL.service as a systemd unit, enabled and active
  • 24/7 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.

Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting point, because Pi-hole is lightweight.

Configure the network security group (NSG) to allow inbound 22/tcp from your management network, 80/tcp for the dashboard, and 53/udp and 53/tcp for DNS from the clients you want to protect. Pi-hole serves the dashboard over plain HTTP on port 80, so for production, terminate TLS in front of it with your own domain.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Pi-hole by SecureAnyCloud, and select Create.

On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80).

After creation, add inbound NSG rules for DNS (port 53, both UDP and TCP) so that your clients can use the VM as their resolver. Then select Review + create and Create.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:pi-hole-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name pi-hole \
  --image sac1647283583153:pi-hole-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard
# Admin dashboard: only from your management network
az network nsg rule create --resource-group <your-rg> --nsg-name <your-nsg> \
  --name allow-admin-http --priority 1010 --protocol Tcp \
  --source-address-prefixes <your-mgmt-cidr> \
  --destination-port-ranges 80 --access Allow --direction Inbound
# DNS: only from the clients you serve (never 0.0.0.0/0 - see Security notes)
az network nsg rule create --resource-group <your-rg> --nsg-name <your-nsg> \
  --name allow-dns-udp --priority 1020 --protocol Udp \
  --source-address-prefixes <your-client-cidr> \
  --destination-port-ranges 53 --access Allow --direction Inbound
az network nsg rule create --resource-group <your-rg> --nsg-name <your-nsg> \
  --name allow-dns-tcp --priority 1021 --protocol Tcp \
  --source-address-prefixes <your-client-cidr> \
  --destination-port-ranges 53 --access Allow --direction Inbound

Always set --source-address-prefixes to the networks you serve. Without it, Azure allows every source on the internet. If Pi-hole is set to answer every network (see Step 9), the VM then becomes an open resolver.

First boot takes a few minutes. On its first boot, the VM finishes its per-VM setup in the background: it generates this VM's own credentials and starts the services. If you connect as soon as the VM shows as running, wait for that setup to complete before you continue. Run sudo cloud-init status --wait.

It prints status: done when the VM is ready. The credentials and services described below are present only after that.

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the service is running

systemctl is-active pihole-FTL.service

It reports active. pihole-FTL is the single Pi-hole service, and it provides both the DNS resolver and the web dashboard.

The VM resolves names through Pi-hole itself, and Pi-hole's public upstream resolvers do not know the VM's own Azure host name. As a result, sudo may print unable to resolve host <name>. This is harmless but noisy. Add the name to /etc/hosts once to silence it:

grep -q "$(hostname)" /etc/hosts || echo "127.0.1.1 $(hostname)" | sudo tee -a /etc/hosts

Step 5 - Retrieve your admin password

The admin password is generated uniquely on the first boot of your VM and written to a root-only file:

sudo cat /root/pi-hole-credentials.txt

This file contains PIHOLE_URL, PIHOLE_API_URL and pihole.admin.pass. Store the password somewhere safe.

With a Standard SKU public IP (the Azure default), the two URLs can show the VM's private address, for example http://10.0.0.4/admin. From your browser, use the VM's public IP as in Step 7.

Step 6 - Confirm the DNS resolver and web server are listening

ss -tlnH | grep ':80 ' && ss -lnuH | grep ':53 '

You should see a listener on :80 (the dashboard) and on :53/udp (DNS). Then confirm that Pi-hole resolves names:

dig +short @127.0.0.1 github.com A

The command returns one or more IP addresses, which proves that the resolver is working.

Step 7 - Open the admin dashboard

Browse to http://<vm-public-ip>/admin and sign in with the password from Step 5. The login page is served by pihole-FTL itself.

After you sign in, the dashboard shows total queries, queries blocked, the percent blocked, the number of domains on your blocklists, and live query and client activity graphs.

The Query Log lists recent DNS queries with their client, type, response time and whether each was blocked (red) or allowed (green).

Under Lists, manage the subscribed blocklists (adlists) that Pi-hole uses to sink ad and tracker domains. The image ships with a default blocklist already enabled.

Step 8 - Verify the admin API from the command line

Pi-hole v6's authentication API rejects a wrong password (HTTP 401) and accepts the per-VM admin password (HTTP 200 with a session). Read the password into a variable, then authenticate:

PW=$(sudo grep '^pihole.admin.pass=' /root/pi-hole-credentials.txt | cut -d= -f2-)
curl -s -X POST -H 'Content-Type: application/json' -d "{\"password\":\"$PW\"}" http://localhost/api/auth | head -c 200; echo

The response contains a "session" object with "valid":true and a session ID (sid). The session ID authenticates subsequent API calls.

Step 9 - Point your clients at Pi-hole

To protect your devices, set this VM's public (or private) IP as their DNS server. You have two options:

  • A single device: set its DNS server to the VM's IP in the OS network settings.
  • Your whole network: set the VM's IP as the DNS server in your router's DHCP settings, so every device on the network uses Pi-hole automatically.

Make sure that ports 53/udp and 53/tcp are open in the VM's NSG from the clients you want to serve. To confirm blocking, run nslookup doubleclick.net <vm-ip> from any client. A blocked domain resolves to 0.0.0.0.

Pi-hole's listening mode decides which clients it answers. Check it:

sudo pihole-FTL --config dns.listeningMode
  • LOCAL (Pi-hole's own default, and the setting in current SecureAnyCloud images): Pi-hole answers only clients on networks the VM is directly attached to, such as other VMs in its subnet. Queries from anywhere else are ignored and logged as ignoring query from non-local network.
  • ALL: Pi-hole answers every client that reaches port 53. Use this to serve devices across the internet, a VPN or peered networks, and only together with the NSG source restriction from Step 2:
sudo pihole-FTL --config dns.listeningMode ALL

The change applies at once, with no restart. To go back, set LOCAL the same way.

Step 10 - Update blocklists and Pi-hole

Refresh the blocklists (the "gravity" database) at any time:

sudo pihole -g

Pi-hole and its blocklists are maintained in the SecureAnyCloud image. You can update the Pi-hole software yourself with sudo pihole -up.

Security notes

  • Dashboard transport. The admin dashboard is served over plain HTTP on port 80. pihole-FTL also listens for HTTPS on port 443 with a self-signed certificate, and your browser warns until you install your own certificate. Neither port is reachable unless you open it in the NSG.
  • Restrict and encrypt the dashboard. Before exposing the dashboard beyond a trusted network, restrict port 80 to your management CIDR in the NSG and front the dashboard with TLS and your own domain.
  • Limit DNS exposure. Keep port 53 reachable only from the clients you intend to serve. An open public resolver can be abused for DNS amplification.
  • Per-VM admin password. The admin password is unique per VM and stored only in the root-only credentials file.

All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.

Deploy on Azure

Launch Pi-hole on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure