Networking Azure

CoreDNS on Ubuntu 24.04 on Azure User Guide

| Product: CoreDNS 1.14.6 on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of CoreDNS on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. CoreDNS is a fast, flexible, plugin based DNS server and a CNCF graduated project, best known as the default DNS server for Kubernetes. It is a single self contained Go binary whose entire behaviour is defined by a plain text Corefile, so you compose exactly the DNS pipeline you need from a chain of plugins rather than wiring together several daemons.

The image installs CoreDNS 1.14.6 from the official prebuilt release tarball, run under systemd as an unprivileged coredns user with just the CAP_NET_BIND_SERVICE capability so it can bind the standard privileged DNS port. CoreDNS answers DNS on port 53 over both UDP and TCP, publishes Prometheus metrics on port 9153, and exposes a loopback health endpoint on port 8080. There is no web interface and no database: the Corefile (/etc/coredns/Corefile) plus a zone file are the whole configuration.

Secure by default — not an open resolver. A naively deployed public resolver can be abused for DNS amplification attacks. This image avoids that: the shipped Corefile has two server blocks. An authoritative block serves the demonstration zone cloudimg.internal with the authoritative answer flag on all interfaces, and a recursive cache and forward block whose acl plugin allows recursion only from the local host and answers every other source with REFUSED. On the first boot of every VM the instance IP is baked into the demo zone so DNS answers immediately. CoreDNS has no login, so no shared or default credential of any kind ships in the image.

What is included:

  • CoreDNS 1.14.6 from the official prebuilt release binary, run under systemd as the unprivileged coredns user (coredns.service)

  • A DNS endpoint on 0.0.0.0:53 (UDP and TCP), a Prometheus metrics endpoint on :9153, and a health endpoint on :8080

  • An authoritative demonstration zone cloudimg.internal, served with the AA flag on all interfaces, whose records point at this instance's IP (baked in on first boot)

  • A recursive cache and forward resolver restricted by the acl plugin to loopback only, so the image is not an open resolver until you deliberately open it to your trusted subnets

  • The whole configuration in one readable file, /etc/coredns/Corefile, that you version control and reload with a single command

  • The systemd-resolved stub listener disabled so CoreDNS owns port 53 cleanly, while the OS keeps resolving names through the upstream resolver list

Prerequisites

  • Active Azure subscription, SSH public key, VNet + subnet in target region

  • Subscription to the CoreDNS listing on Azure Marketplace

  • Network Security Group rules allowing TCP 22 (admin), UDP 53 and TCP 53 (DNS) from the clients that will query the server, and optionally TCP 9153 from your monitoring network for metrics

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is ample for a small authoritative server or a departmental resolver. CoreDNS is extremely light; larger sizes are only needed for very high query volumes.

Step 1: Deploy from the Azure Portal

Search CoreDNS in Marketplace, select the cloudimg publisher, and click Create. Configure the Network Security Group to allow UDP 53 and TCP 53 from the clients that will query the server, and TCP 22 for administration. If you host public authoritative zones, 53 must be reachable from the internet; if you run a recursive resolver for your own networks, keep 53 restricted to your internal subnets.

Step 2: Deploy from the Azure CLI

RG="dns-prod"; LOCATION="eastus"; VM_NAME="coredns1"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/coredns-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
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 this VM's IP, renders the demonstration zone /etc/coredns/zones/db.cloudimg.internal from the shipped template (baking the IP into the demo, www and ns1 records and setting a fresh SOA serial), writes the informational note /root/coredns-info.txt, and starts coredns. This completes within seconds. SSH in as azureuser and read the note:

sudo cat /root/coredns-info.txt

The note records the demo zone name, the demo record, this VM's IP, the metrics URL, and the paths to the Corefile and zone file. CoreDNS has no password, so the note holds no secret.

Step 4: Confirm the service is running

coredns.service is active, coredns -version reports 1.14.6, and ss confirms it is listening on :53 for DNS (UDP and TCP), :9153 for metrics and :8080 for health.

systemctl is-active coredns.service
/opt/coredns/coredns -version
sudo ss -tulnp | grep -E ':53 |:9153 |:8080 '

coredns.service reports active, the coredns binary reports version CoreDNS-1.14.6, and ss shows the server listening on port 53 for DNS over both TCP and UDP, port 9153 for Prometheus metrics and port 8080 for health on all interfaces

Step 5: Query the authoritative demo zone

The demonstration zone cloudimg.internal is served authoritatively. Query its SOA and the demo A record on the loopback address. The answer carries the aa (authoritative answer) flag, and demo.cloudimg.internal resolves to this VM's IP. DNS over TCP works too, which is what zone transfers and large responses use.

dig +noall +answer @127.0.0.1 SOA cloudimg.internal
dig @127.0.0.1 A demo.cloudimg.internal
dig +tcp +short @127.0.0.1 A www.cloudimg.internal

In the second command, look for flags: qr aa rd in the header — the aa flag confirms CoreDNS is answering authoritatively for this zone rather than recursing.

dig returns the SOA for the authoritative demo zone cloudimg.internal, and a query for demo.cloudimg.internal returns an A record with the header flags including aa, the authoritative answer flag, resolving to the VM IP address 10.0.0.14

Step 6: Prometheus metrics and health

CoreDNS exposes a Prometheus metrics endpoint on port 9153 (every server, plugin and response code is a labelled series) and a plain health endpoint on port 8080 that returns OK while the server is up. Point your Prometheus scraper at :9153 and your load balancer or uptime monitor at :8080/health.

curl -s http://127.0.0.1:9153/metrics | grep '^coredns_' | head
curl -s http://127.0.0.1:8080/health; echo

the Prometheus metrics endpoint on port 9153 returns coredns_build_info reporting version 1.14.6, coredns_dns_requests_total and coredns_dns_responses_total counters labelled by server and response code, and the health endpoint on port 8080 returns OK

Step 7: Confirm the image is not an open resolver

This is the security posture that matters for a public DNS server. The default .:53 block forwards and caches recursive queries, but its acl plugin allows recursion only from loopback and answers every other source with REFUSED. Read the acl block, then send a recursive query from this VM's own non loopback address and confirm it is refused.

sudo grep -A3 'acl {' /etc/coredns/Corefile
IP=$(hostname -I | awk '{print $1}')
dig @"$IP" A probe.invalid | grep -E 'status:'

The recursive query from the non loopback source returns status: REFUSED, proving the image cannot be abused for DNS amplification out of the box. Authoritative answers for cloudimg.internal are still served to everyone (Step 5) — only recursion is restricted.

the acl block in the Corefile shows allow net 127.0.0.0/8 and colon-colon-1/128 followed by block, and a recursive dig for probe.invalid sent from the VM non loopback address returns a header with status REFUSED, proving the server is not an open resolver

Step 8: Serve your own authoritative zone

You add authoritative zones by dropping a zone file into /etc/coredns/zones and adding a matching server block to the Corefile. The example below uses <your-domain> as a placeholder for a domain you own. Create the zone file, add the block, and reload.

# 1) create a zone file for <your-domain> in /etc/coredns/zones/db.<your-domain>
#    (use the shipped db.cloudimg.internal.template as a starting point)
# 2) add a server block to /etc/coredns/Corefile:
#      <your-domain>:53 {
#          file /etc/coredns/zones/db.<your-domain>
#          log
#          errors
#      }
# 3) apply the change:
sudo systemctl reload coredns

CoreDNS validates the Corefile on reload; if there is a syntax error it keeps serving the previous configuration. To publish the zone on the internet, point your registrar's NS records at this instance's public IP.

Step 9: Open recursion to your trusted networks

To let your own clients use the VM as a recursive resolver, add their subnets to the acl allow net line in the default .:53 block, then restart. Keep the list to networks you control — never open recursion to the whole internet.

# edit /etc/coredns/Corefile and extend the acl allow line in the .:53 block, e.g.
#   allow net 127.0.0.0/8 ::1/128 10.0.0.0/8 192.168.0.0/16
sudo systemctl restart coredns

The upstreams used for forwarding are Quad9 (9.9.9.9) and its secondary (149.112.112.112); change the forward line in the Corefile if you prefer different resolvers or want DNS over TLS upstreams.

Step 10: Review the configuration and info note

Everything CoreDNS does is in the Corefile, and the per instance details are in the info note. Both are readable with sudo.

sudo cat /etc/coredns/Corefile
sudo cat /root/coredns-info.txt

The info note is 0600 root:root and holds no secret — CoreDNS has no login. It simply records the demo zone, this VM's IP and the metrics URL for convenience.

Step 11: Security recommendations

  • Restrict the NSG to your use case. For an internal recursive resolver, allow UDP 53 and TCP 53 only from your client subnets. For public authoritative zones, 53 must be internet reachable, but keep recursion loopback only (or scoped by the acl) so the server is never an open resolver.

  • Keep the acl tight. Only add subnets you control to the allow net line. An overly broad allow list turns the server back into an open resolver.

  • Restrict the metrics port. Port 9153 exposes query statistics. Open TCP 9153 only to your monitoring network, not the public internet.

  • Enable DNSSEC or DNS over TLS as needed. CoreDNS ships the dnssec, tls and forward tls:// plugins; enable them in the Corefile for signed zones and encrypted upstreams.

  • Keep the OS patched. Unattended security upgrades remain enabled on the running VM.

Step 12: Support and Licensing

CoreDNS is an open source project distributed under the Apache License 2.0. This cloudimg image bundles the unmodified official CoreDNS release binary. cloudimg provides the packaging, the systemd hardening, the secure by default Corefile and acl posture, the per instance demo zone automation, the paired deploy guide, and 24/7 support with a guaranteed 24 hour response SLA.

cloudimg is not affiliated with or endorsed by the CoreDNS project or the CNCF. CoreDNS is a mark of its respective owner and is used here only to identify the software.

Deploy on Azure

Find CoreDNS 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.