Unbound on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of Unbound on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Unbound is a fast, lean, validating, recursive, caching DNS resolver developed by NLnet Labs. It performs full recursive resolution for the clients it serves, cryptographically validates every answer with DNSSEC, and caches aggressively for low latency, all driven by a single plain text configuration file with no database and no web console.
The image installs the maintained Unbound package from the Ubuntu 24.04 LTS archive, which is security patched by the Ubuntu security team and kept current on your running VM by unattended upgrades.
Security by design — not an open resolver. An open recursive resolver, one that will recursively resolve arbitrary names for anyone on the internet, is a well known DNS amplification and reflection DDoS vector. Unbound listens on all interfaces so it is useful inside your VNet, but its access-control policy allows recursion only from the local host and RFC1918 private ranges and refuses every public source. A recursive query from a public address is answered with REFUSED, so this server can never be abused as an amplifier. You widen the policy to your own trusted subnets when you need to (see Step 9), never to 0.0.0.0/0.
Security by design — DNSSEC validation on. Unbound validates the DNSSEC signature chain on every recursive answer against the root trust anchor (/var/lib/unbound/root.key, kept current automatically via RFC 5011). A correctly signed name is returned with the AD (Authenticated Data) flag; a name whose signatures fail to validate is rejected with SERVFAIL rather than being handed to your clients.
Security by design — no baked control credential. A DNS resolver has no admin login. The only management surface is unbound-control, and this image exposes it over a root only unix socket (/run/unbound.ctl) rather than a network TLS port, so there is no control key or certificate baked into the image and nothing to rotate. Manage the resolver locally with sudo unbound-control.
What is included:
-
Unbound (from the Ubuntu 24.04 LTS archive), run under systemd as the unprivileged
unbounduser under its AppArmor profile (unbound.service) -
DNSSEC validation on against the root trust anchor, with
harden-dnssec-strippedand aggressive NSEC caching -
A responsible not an open resolver
access-controlpolicy: recursion for127.0.0.0/8,::1and the RFC1918 private ranges only;0.0.0.0/0and::0/0are refused -
unbound-controlon a root only unix socket (/run/unbound.ctl), so no control credential ships in the image -
Privacy and performance defaults:
qname-minimisation, prefetching, cache prefetch, and per IP and per domain rate limiting to blunt reflection abuse -
Identity hardening:
hide-identityandhide-versionso the resolver does not leak its software version -
Unattended security upgrades left enabled so the resolver keeps receiving patches
Prerequisites
-
Active Azure subscription, SSH public key, VNet + subnet in the target region
-
Subscription to the Unbound listing on Azure Marketplace
-
Network Security Group rules allowing TCP 22 (admin) and both UDP and TCP 53 (DNS) from the clients that will query the resolver
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) for a small resolver. Resolvers serving heavy query volumes should use Standard_D2s_v5 or larger.
Step 1: Deploy from the Azure Portal
Search Unbound in Marketplace, select the cloudimg publisher, and click Create. Configure the Network Security Group to allow UDP 53 and TCP 53 (DNS) from your client networks, and TCP 22 for administration. DNS uses UDP 53 for most queries and falls back to TCP 53 for large responses, so open both.
Step 2: Deploy from the Azure CLI
RG="dns-prod"; LOCATION="eastus"; VM_NAME="resolver1"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/unbound-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az vm create \
--resource-group "$RG" --name "$VM_NAME" \
--image "$GALLERY_IMAGE_ID" \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values "$SSH_KEY" \
--public-ip-sku Standard
# Open the DNS and admin ports on the VM's NSG (open both UDP and TCP 53):
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 53 --priority 1001
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 22 --priority 1002
Step 3: First boot
On first boot the image resolves the VM public IP for its documentation, confirms DNSSEC validation and the not an open resolver access-control policy are in place, starts Unbound, proves the resolver is up and controllable over the unix socket, and writes /root/unbound-credentials.txt. This completes within a minute. SSH in as azureuser and read the details:
sudo cat /root/unbound-credentials.txt
Step 4: Confirm the resolver is running
unbound.service is active and unbound-control status reports the server running with the validator module loaded (DNSSEC), using the root only unix socket control channel.
systemctl is-active unbound.service
sudo unbound-control status

Step 5: Resolve a name with DNSSEC validation
Query a DNSSEC signed name against the loopback address. The AD flag in the header confirms the answer was cryptographically validated against the root trust anchor. A second identical query is served from cache, which you can see as a much lower query time.
dig @127.0.0.1 +dnssec cloudflare.com
dig @127.0.0.1 cloudflare.com

Step 6: Confirm DNSSEC rejects bad data
A name whose DNSSEC signatures deliberately fail to validate must be rejected with SERVFAIL rather than returned to clients. dnssec-failed.org is a well known test domain with intentionally broken signatures. unbound-checkconf also validates the whole configuration (no error output means valid).
dig @127.0.0.1 dnssec-failed.org
sudo unbound-checkconf

Step 7: Verify it is not an open resolver
This is the key security property. The access-control policy in /etc/unbound/unbound.conf.d/00-cloudimg.conf refuses 0.0.0.0/0 and ::0/0, and allows recursion only from the local host and the RFC1918 private ranges. No public source can use this server to recursively resolve arbitrary names.
grep -E 'access-control|control-interface|control-use-cert' /etc/unbound/unbound.conf.d/00-cloudimg.conf
sudo unbound-control stats_noreset | grep -E 'total.num.queries|total.num.cachehits'

Step 8: Point your clients at the resolver
From a VM inside the same VNet (an allowed private range), set the resolver's private IP as the nameserver, or test directly against the resolver:
dig @<vm-ip> +dnssec example.com
To make a Linux client use the resolver permanently, set it as the DNS server on the client's network interface (through netplan or systemd-resolved), pointing at the resolver's private IP.
Step 9: Widen access-control to your own networks (optional)
By default recursion is offered to the local host and the RFC1918 private ranges. To serve additional trusted client networks, add an access-control allow line to /etc/unbound/unbound.conf.d/00-cloudimg.conf inside the server: block:
access-control: 203.0.113.0/24 allow
Replace 203.0.113.0/24 with your own trusted client ranges. Never use access-control: 0.0.0.0/0 allow — that turns the server back into an open resolver. Apply the change with:
sudo unbound-checkconf
sudo unbound-control reload
Step 10: Manage Unbound with unbound-control
The control channel is a root only unix socket, so unbound-control works locally with no credential to manage. Common operations:
sudo unbound-control status
sudo unbound-control stats
sudo unbound-control reload
To inspect or flush the cache:
sudo unbound-control dump_cache | head
sudo unbound-control flush_zone example.com
Step 11: Security recommendations
-
Restrict the NSG. Allow UDP and TCP 53 only from the client networks that need to query the resolver, and TCP 22 for administration only.
-
Keep recursion scoped. Only widen
access-controlto networks you trust (Step 9) — never0.0.0.0/0. Even with the NSG open, theaccess-controlpolicy is your second layer of defence against open resolver abuse. -
Leave DNSSEC validation on. It protects your clients from spoofed or tampered answers. The root trust anchor updates itself via RFC 5011.
-
Consider forwarders if required. If your environment mandates resolving through an upstream (for example a corporate resolver), configure a
forward-zone, but note that recursive resolution direct to the authoritative servers is the more private default. -
Keep the OS and Unbound patched. Unattended security upgrades remain enabled on the running VM.
-
Monitor with the statistics.
unbound-control statsexposes query counts, cache hit ratio and validation failures for your monitoring system.
Step 12: Support and Licensing
Unbound is developed by NLnet Labs and distributed under the BSD 3 Clause license. This cloudimg image bundles the unmodified Ubuntu Unbound package. cloudimg provides the packaging, the not an open resolver hardening, the DNSSEC validation defaults, the root only unix socket control channel, and 24/7 support with a guaranteed 24 hour response SLA.
cloudimg is not affiliated with or endorsed by NLnet Labs. Unbound is a mark of its respective owner and is used here only to identify the software.
Deploy on Azure
Find Unbound on Ubuntu 24.04 LTS on the Azure Marketplace, published by cloudimg. Deploy from the Portal or the Azure CLI as shown above.
Need Help?
Email support@cloudimg.co.uk for deployment help, configuration questions, or licensing enquiries.