6.2.a. Set Up DuckDNS

Goal

Configure a free DNS subdomain using DuckDNS to enable SSL certificate issuance from Let’s Encrypt, which requires a domain name rather than an IP address.

What you’ll learn:

  • Why Let’s Encrypt requires domain names for certificates
  • How to set up a free dynamic DNS subdomain
  • How DNS A records map domains to IP addresses
  • How to verify DNS configuration with command-line tools

Prerequisites

Before starting, ensure you have:

  • ✓ Completed Tutorial 06 (Nginx Reverse Proxy) - understanding of reverse proxy concepts
  • ✓ Azure CLI installed and logged in (az login)
  • ✓ SSH keys configured (~/.ssh/id_rsa.pub)

Exercise Steps

Overview

  1. Understand Why You Need DNS
  2. Create a DuckDNS Account
  3. Create a Subdomain
  4. Provision a Test VM
  5. Update Your DuckDNS Subdomain
  6. Verify DNS Configuration

Step 1: Understand Why You Need DNS

Before setting up DuckDNS, it’s important to understand why Let’s Encrypt requires a domain name. This knowledge will help you troubleshoot issues and make informed decisions about DNS configuration in production applications.

Let’s Encrypt issues SSL certificates for domain names, not IP addresses. This is because:

DuckDNS is a free dynamic DNS service that provides:

ℹ️ Concept Deep Dive

DuckDNS is ideal for tutorials because it’s free, fast to set up, and designed for dynamic IP addresses. For production applications, you would typically use your own domain with a DNS provider like Cloudflare or Azure DNS.

Quick check: You understand that Let’s Encrypt needs a domain, not an IP address

Step 2: Create a DuckDNS Account

Set up your DuckDNS account to get access to free subdomains and an API token. The token is essential for Tutorial 06.2b where you’ll automate DNS updates.

  1. Navigate to https://www.duckdns.org in your browser

  2. Sign in using one of the OAuth providers:

    • GitHub
    • Google
    • Twitter
    • Reddit
  3. Locate your token on the account page:

    • Your token appears as a UUID (e.g., a1b2c3d4-e5f6-7890-abcd-ef1234567890)
    • Your subdomains list will be empty initially
  4. Save your token somewhere secure (password manager, secure note, etc.)

⚠️ Common Mistakes

  • Sharing your token publicly allows anyone to update your DNS records
  • Committing your token to git exposes it in repository history
  • Forgetting to save the token means you’ll need to regenerate it later

Quick check: You have your DuckDNS token saved securely for use in Tutorial 06.2b

Step 3: Create a Subdomain

Register a unique subdomain that will become your application’s address. Choose a name that’s memorable and relevant to your project.

  1. Enter a subdomain name in the “sub domain” field. For example:

    • hellojava-yourname
    • myapp-demo
    • learningcloud

    The name must be:

    • Unique across all DuckDNS users
    • Lowercase letters, numbers, and hyphens only
    • Between 1-30 characters
  2. Click the “add domain” button

  3. Verify your subdomain appears in the list showing:

    • The full domain: yoursubdomain.duckdns.org
    • Current IP: (may show your home IP or be empty)

ℹ️ Concept Deep Dive

The current IP shown doesn’t matter yet - we’ll update it to point to our Azure VM in the next steps. DuckDNS is designed for “dynamic DNS” scenarios where IPs change frequently, which is why updating the IP is so easy.

Quick check: Your subdomain appears in the DuckDNS list

Step 4: Provision a Test VM

To test your DuckDNS configuration, you need the public IP address of a VM. We’ll create a simple Azure VM running Nginx using the scripts in scripts/06.2a-setup-duckdns/.

cloud-init.yaml - Installs Nginx:

#cloud-config
package_update: true

packages:
  - nginx

runcmd:
  - systemctl enable nginx
  - systemctl start nginx

provision-vm.sh - Creates the Azure VM:

#!/bin/bash
set -e

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOURCE_GROUP="hellojava-duckdns-rg"
VM_NAME="hellojava-duckdns-vm"
LOCATION="northeurope"

echo "Creating resource group..."
az group create --name $RESOURCE_GROUP --location $LOCATION --output none

echo "Creating VM (this takes ~2-3 minutes)..."
az vm create \
    --resource-group $RESOURCE_GROUP \
    --name $VM_NAME \
    --image Ubuntu2404 \
    --size Standard_B1s \
    --admin-username azureuser \
    --generate-ssh-keys \
    --custom-data @"$SCRIPT_DIR/cloud-init.yaml" \
    --public-ip-sku Standard \
    --output none

echo "Opening port 80..."
az vm open-port --resource-group $RESOURCE_GROUP --name $VM_NAME --port 80 --output none

PUBLIC_IP=$(az vm show -d -g $RESOURCE_GROUP -n $VM_NAME --query publicIps -o tsv)
echo ""
echo "VM provisioned successfully!"
echo "Public IP: $PUBLIC_IP"
  1. Navigate to the scripts directory:

    cd scripts/06.2a-setup-duckdns
    
  2. Run the provision script:

    ./provision-vm.sh
    
  3. Note the public IP when the script completes:

    Public IP: 20.xxx.xxx.xxx
    

    Copy this IP address - you’ll need it in the next step.

ℹ️ Concept Deep Dive

The script performs exactly 3 operations: creates a resource group, creates a VM with Nginx via cloud-init, and opens port 80. This minimal setup is perfect for testing DNS resolution without the complexity of a full application deployment.

⚠️ Common Mistakes

  • Running the script from the wrong directory causes “cloud-init.yaml not found” errors
  • Not having SSH keys configured results in authentication failures
  • Forgetting to note the IP means you’ll need to look it up in Azure Portal

Quick check: Script completes successfully and displays a public IP address

Step 5: Update Your DuckDNS Subdomain

Point your subdomain to the VM’s IP address by updating the A record in DuckDNS. This tells DNS resolvers that your domain should route traffic to your Azure VM.

  1. Return to https://www.duckdns.org (you should still be logged in)

  2. Find your subdomain in the list

  3. Replace the current IP with your VM’s public IP from Step 4

  4. Click the “update ip” button

  5. Wait 1-2 minutes for DNS propagation

ℹ️ Concept Deep Dive

An A record (Address record) is the most fundamental type of DNS record - it maps a domain name directly to an IPv4 address. When someone types your domain in a browser, DNS resolvers look up this A record to find which IP to connect to. DuckDNS updates are typically instant, but DNS caches may take a minute to refresh.

Quick check: DuckDNS shows your VM’s IP address next to your subdomain

Step 6: Verify DNS Configuration

Confirm that your subdomain correctly resolves to your VM and that the Nginx welcome page is accessible. This verification ensures everything is ready for Let’s Encrypt in Tutorial 06.2b.

  1. Check DNS resolution:

    dig +short yoursubdomain.duckdns.org
    

    Replace yoursubdomain with your actual subdomain name.

    Expected output:

    20.xxx.xxx.xxx
    

    This should match your VM’s public IP.

  2. Test HTTP access via domain:

    curl http://yoursubdomain.duckdns.org
    

    Expected output: Nginx welcome page HTML starting with:

    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    ...
    
  3. Test in browser (optional):

    Open http://yoursubdomain.duckdns.org in your browser to see the “Welcome to nginx!” page.

Success indicators:

  • dig returns your VM’s IP address
  • curl returns Nginx welcome page HTML
  • Browser displays “Welcome to nginx!” page

Final verification checklist:

  • ☐ DuckDNS account created with token saved
  • ☐ Subdomain registered and showing in list
  • ☐ VM provisioned with public IP
  • ☐ A record updated to point to VM IP
  • ☐ DNS resolution verified with dig
  • ☐ HTTP access verified with curl

Common Issues

If you encounter problems:

DNS not resolving (dig returns nothing):

  • Verify the IP is correct in DuckDNS dashboard
  • Wait 2-3 minutes for DNS propagation
  • Check for typos in subdomain name (remember it’s .duckdns.org not .com)

curl: Connection refused:

  • Check VM is running: az vm show -d -g hellojava-duckdns-rg -n hellojava-duckdns-vm --query powerState -o tsv
  • Check Nginx status: ssh azureuser@<your-vm-ip> 'systemctl status nginx'
  • Verify port 80 is open in NSG

Permission denied running script:

  • Add execute permission: chmod +x provision-vm.sh

Still stuck? SSH to the VM and check Nginx logs: sudo tail -f /var/log/nginx/error.log

Summary

You’ve successfully configured DuckDNS which:

Key takeaway: DNS is the foundation for SSL certificates because Let’s Encrypt needs to verify domain ownership. DuckDNS provides a free, easy way to get started with domain-based deployments. You’ll use this pattern whenever you need a domain name for development or learning purposes.

Going Deeper (Optional)

Want to explore more?

  • Research how the DuckDNS API works for automated IP updates
  • Compare A records with CNAME, TXT, and MX records
  • Investigate how DNS propagation works across global resolvers
  • Try setting up a custom domain with Cloudflare for production use

Clean Up

You have two options for the test VM:

Option A: Keep for Tutorial 06.2b

If continuing to the Let’s Encrypt tutorial, you can reuse this VM or create a fresh one.

Option B: Delete resources now

az group delete --name hellojava-duckdns-rg --yes --no-wait

ℹ️ Note

Deleting the Azure VM does not affect your DuckDNS account. Your subdomain remains registered and you can update its IP to point to new VMs anytime.

Done! 🎉

Excellent work! You’ve learned how to configure DNS for cloud deployments and can now use domain names instead of IP addresses. This foundation is essential for obtaining SSL certificates and building production-ready applications.