SmartDNS
Overview
This guide covers the deployment and configuration of SmartDNS on Ubuntu 24.04 on Azure, using SecureAnyCloud Azure Marketplace images. SmartDNS is a local DNS server. It takes each lookup, sends it to several upstream resolvers concurrently, measures how quickly each returned address responds, and hands your client the fastest one.
SmartDNS caches results aggressively for low latency and speaks encrypted upstream protocols (DNS over TLS, DNS over HTTPS and QUIC). It can also apply per-domain rules, such as pinning an address or blocking a name outright.
A name like github.com or a large CDN hostname resolves to many different addresses depending on which resolver you ask. The address you are given matters as much as how fast you were given it. SmartDNS is built around that idea: it does not just forward, it compares and chooses.
The image installs the official SmartDNS Release 48.4 package published by the upstream project. The package is pinned to an exact release and verified against a known SHA256 checksum during the build, so you always know precisely what is on the image.
Security by design: not an open resolver. A DNS resolver that answers arbitrary lookups for anyone on the internet is a well-known DNS amplification and reflection DDoS vector. SmartDNS listens on all interfaces, so it is useful across your whole VNet. Unlike some resolvers, it has no admission control of its own, because its client-rules directive changes per-client behaviour, not who is allowed in.
SecureAnyCloud therefore enforces the boundary where it cannot be bypassed, in the kernel with nftables. DNS on UDP and TCP 53, and the web interface on TCP 6080, are accepted from the local host and private ranges only, and dropped from every other source. The image ships an audit tool that proves this functionally rather than by assertion (see Step 7).
Security by design: no published default password. The SmartDNS web interface documents a default login of admin / password. That credential is never written into this image. In the shipped image, the web interface is not even loaded, because its configuration include is blank. There is no window in which a known credential is live.
At first boot, your instance generates a 32-character random password for itself, writes the configuration, and starts the interface. The in-browser terminal feature is deliberately disabled, because a remote shell on a network appliance is a remote root surface.
What you get:
- SmartDNS Release 48.4 (official upstream package, checksum pinned), run under systemd as
smartdns.service - Fastest address selection:
response-mode fastest-ipwith a TCP-based speed check (tcp:443,tcp:80). TCP is chosen over ICMP ping because outbound ICMP is unreliable in cloud networks, and a silently failing check would degrade the very measurement the product exists for - Six upstream resolvers, encrypted first: DNS over TLS to Cloudflare, Google and Quad9 with hostname verification, plus plain UDP equivalents as a fallback for networks that block port 853
- Aggressive caching with prefetch and stale-answer serving, so repeat lookups are answered in well under a millisecond
- A web interface on port 6080 with live query logs, per-upstream response times, cache statistics and client breakdown
- A kernel-level nftables boundary that restricts DNS and the web interface to private networks, plus
smartdns-openresolver-check.sh, an audit tool that proves it - A per-instance web password generated at first boot and stored root-only at
/root/smartdns-credentials.txt - Unattended security upgrades left enabled, so the underlying OS keeps receiving patches
Prerequisites
- An active Azure subscription, an SSH public key, and a VNet and subnet in the target region
- A subscription to the SmartDNS listing on Azure Marketplace
- Network Security Group rules that allow TCP 22 for administration, and both UDP and TCP 53 from the client subnets that will query the resolver
- Recommended virtual machine size:
Standard_B2s(2 vCPU, 4 GB RAM) is ample for a resolver serving a VNet. UseStandard_D2s_v5or larger for very heavy query volumes - Keep port 53 closed to the internet in your NSG. The image already drops public DNS traffic in the kernel, but for defence in depth, your NSG should not offer it either
Deploy from the Azure Portal
Search for SmartDNS in Marketplace, select the SecureAnyCloud publisher, and click Create. Configure the Network Security Group to allow UDP 53 and TCP 53 from your client subnets only, and TCP 22 for administration. DNS uses UDP 53 for most queries and falls back to TCP 53 for large responses, so open both.
Do not add an NSG rule that exposes port 6080 to the internet. Reach the web interface over an SSH tunnel or from inside your VNet.
Deploy from the Azure CLI
RG="dns-prod"; LOCATION="eastus"; VM_NAME="smartdns1" az group create --name "$RG" --location "$LOCATION" az vm image terms accept --urn sac1647283583153:smartdns-ubuntu-24-04:default:latest az vm create \
--resource-group "$RG" \
--name "$VM_NAME" \
--image sac1647283583153:smartdns-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard \
--location "$LOCATION"
# Open DNS to your CLIENT SUBNET only - never to the internet:
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 53 --priority 1001First boot
On first boot, the instance generates its own web interface password, writes the resolver configuration, loads the kernel firewall, and verifies that it is not an open resolver. This takes a few seconds. Connect over SSH and read the details, which are unique to your instance:
sudo cat /root/smartdns-credentials.txtThe file records the DNS endpoint, the web interface URL, the generated username and password, and the result of the open-resolver audit. The password shown in the screenshot above is masked. On your instance, it is a real 32-character value.
Confirm the resolver is running
systemctl is-active smartdns.service nftables.serviceBoth must report active. Then check that the resolver and web interface are listening, and that both services are enabled so that they survive a reboot:
sudo ss -tulnp | grep -E ':53 |:6080' systemctl is-enabled smartdns.service nftables.serviceResolve a name
Query the resolver directly:
dig @127.0.0.1 github.com A +shortYou should get one or more addresses back. Next, look at the timing behaviour that makes SmartDNS different. Run the same lookup twice:
dig @127.0.0.1 cloudflare.com A | grep -E 'status:|Query time' dig @127.0.0.1 cloudflare.com A | grep -E 'Query time'The first query is resolved upstream. The second is answered from cache, typically in 0 ms.
See the fastest address selection at work
The resolver configuration sets the selection strategy and the upstream pool:
grep -E '^(response-mode|speed-check-mode|server|server-tls)' /etc/smartdns/conf.d/10-sac-resolver.confresponse-mode fastest-ip tells SmartDNS to return the address that responded fastest, and speed-check-mode tcp:443,tcp:80 is how it measures. The six server and server-tls lines are the resolvers that it asks in parallel. You can see the measurements it has collected for each upstream in the web interface (Step 9).
Verify that it is not an open resolver
This is the most important check on a DNS appliance, and the image can prove the result rather than just claim it. Run the bundled audit:
sudo /usr/local/sbin/smartdns-openresolver-check.shThe audit builds two temporary network namespaces attached to the host. One holds a public source address and the other holds a private source address, and the audit queries the resolver from each. Because SmartDNS listens on all interfaces, it is reachable from both. The only thing that can make one succeed and the other fail is the firewall. The audit requires that:
- the query from the private source is answered, so the resolver is usable inside your VNet
- the query from the public source is dropped, so the resolver cannot be abused as an amplifier
The audit fails closed. If the boundary is missing or weakened, or if a blanket accept rule has been added, the audit exits non-zero. You can inspect the rules directly:
sudo nft list table inet sac_smartdns | grep -E 'dport (53|6080)'Point your clients at the resolver
From another VM in the same VNet, query the resolver by its private address:
dig @<SMARTDNS_HOST> github.com A +shortTo make it the system resolver on an Ubuntu client, set the DNS server on the interface. Do not edit /etc/resolv.conf directly, because that file is managed:
sudo resolvectl dns eth0 <SMARTDNS_HOST>
sudo resolvectl domain eth0 '~.' resolvectl status eth0In production, set the DNS server on the Azure VNet itself (Virtual network, DNS servers, Custom), so that every VM in the network picks it up automatically.
Open the web interface
The web interface is bound to private addresses only. Reach it from your workstation over an SSH tunnel:
ssh -L 6080:127.0.0.1:6080 azureuser@YOUR_VM_PUBLIC_IPWith the tunnel open, browse to http://127.0.0.1:6080/ and sign in with the username and password from /root/smartdns-credentials.txt.
The dashboard shows live totals: how many queries the resolver has answered, the cache hit rate, and the average query time.
Upstream Servers is where the product's behaviour becomes visible. Each configured resolver is listed with its measured average response time, its success rate and how many queries it has handled. This is the comparison that SmartDNS uses to decide what to return. The tls rows are the encrypted upstreams.
Query Log shows every lookup with its client, its type, and the measured response time for the answer that was chosen.
You can confirm that the interface is authenticated, and that the published default credential does not work, without leaving the shell:
curl -s -o /dev/null -w 'unauthenticated: HTTP %{http_code}\n' http://127.0.0.1:6080/api/stats/overview
curl -s -o /dev/null -w 'upstream default admin/password: HTTP %{http_code}\n' \
-X POST http://127.0.0.1:6080/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"password"}'Both must report 401. Next, sign in with your own instance's credential:
curl -s -X POST http://127.0.0.1:6080/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"<SMARTDNS_UI_USER>","password":"<SMARTDNS_UI_PASSWORD>"}' \
| head -c 80This returns a JSON web token, which the browser interface uses for subsequent calls.
Serve additional networks
If you need to serve a network that is not in the private ranges that the image allows by default, add its CIDR to the nftables set rather than removing the boundary. Edit /etc/nftables.conf and add your range to sac_private_v4. Then reload and re-audit:
sudo nft -f /etc/nftables.conf
sudo /usr/local/sbin/smartdns-openresolver-check.shNever replace these rules with a blanket accept on port 53. That turns your instance into an open resolver, and the audit above will fail.
Customise the resolver
All SecureAnyCloud configuration lives in /etc/smartdns/conf.d/, and /etc/smartdns/smartdns.conf simply includes that directory. To extend or override behaviour, add your own *.conf file there. Then restart the service:
sudo systemctl restart smartdns.serviceThese directives are useful to explore:
address /ads.example.com/#blocks a namenameserver /internal.example.com/officesends a domain to a specific upstream groupcache-sizetunes the cache
The upstream project documents the full configuration reference.
Rotate the web interface password
The password is stored in /etc/smartdns/conf.d/50-sac-webui.conf, which is readable by root only. Change the smartdns-ui.password line, then restart the service:
sudo systemctl restart smartdns.serviceUpdate /root/smartdns-credentials.txt as well, so that your record stays accurate.
Security recommendations
- Keep port 53 closed to the internet in your NSG. The kernel boundary already drops public DNS, but your NSG should agree with it.
- Never expose port 6080 to the internet. Use an SSH tunnel, or reach it from inside the VNet.
- Re-run
smartdns-openresolver-check.shafter any firewall or configuration change, and treat a failure as a production incident. - Rotate the web interface password if it has ever been shared.
- Leave unattended security upgrades enabled, so that the underlying OS receives later security updates.
- Restrict SSH (TCP 22) to known administrative addresses.
Support and Licensing
SmartDNS is open source software licensed under the GPL-3.0 licence. The licence text is included on the image at /usr/share/doc/sac/smartdns/LICENSE.
SecureAnyCloud provides the packaged, hardened Azure image and 24/7 support for the image itself. SecureAnyCloud is not affiliated with or endorsed by the SmartDNS project. SmartDNS is the name of the upstream project and is used here only to identify the software that this image packages.
Deploy on Azure
Find SmartDNS on Ubuntu 24.04 LTS by SecureAnyCloud on the Azure Marketplace.
Need Help?
Contact SecureAnyCloud support at sales@secureanycloud.com.
Deploy on Azure
Launch SmartDNS Local DNS Resolver 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