Using Cloudflare as a DDNS with UniFi

Configuring a Dynamic Domain Name Service (DDNS) on Ubiquiti's UniFi network manager is easy, if you use on of the configured providers that show up in the drop-down service.

I'd been using No-IP for a number of years, but decided to switch to Cloudflare as it was free, and given Cloudflare's reputation, reliable.

Unfortunately, Cloudflare is not one of the pre-configured DDNS servers in the UniFi drop-down menu list on the Ultimate Dream Machine Pro (UDM-Pro), despite years of requests from users.

Fortunately, it's easy enough to configure from the command line, and will survive reboots and firmware upgrades.

While this is written for the UDM-Pro, this should also work on the UDM.

These instructions assume you've already set Cloudflare to be your domain's nameservers.

If you need help with that, you probably shouldn't be using these instructions . . .

This was tested on UniFi OS UDM Pro 1.11.0

Cloudflare side setup

You will only need to do this once, and it isn't that difficult.

Create an API Token from Cloudflare

  • Browse to https://dash.cloudflare.com/profile/api-tokens

  • Under API Tokens, select Create Token

  • Select Use template for Edit zone DNS

    • Under Zone Resources:
      • Include

      • Specific Zone

      • Select the domain we want to use for DDNS

      • This step is optional. If skipped, this API token will have permissions for all of your Cloudflare domains.

  • Under TTL, select Start/End dates, or leave untouched for no expiration of these permissions.

    Cloudflare API token creation

  • Once generated, copy the API token and treat it like a password.

  • Testing the API token, given the curl command that Cloudflare provides:

    curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
    -H "Authorization: Bearer NS4ZC3Y6MTJD0IXPIM4QJ3L8BCF-BRDYX7JZ0VQW2M" \
    -H "Content-Type:application/json"
    {"result":{"id":"amlf9mwd02v1t837dy3bhb1rlsuuhaw2c","status":"active"},"success":true,"errors":[],"messages":[{"code":10000,"message":"This API Token is valid and active","type":null}]}%
    

Create a Global API Key from Cloudflare

Get your domain's Zone ID from Cloudflare

  • From your Cloudflare dashboard, select your domain.

  • On the bottom right-hand side, under API, you will see your Zone ID

    Zone ID from Cloudflare dashboard

Add placeholder DNS record in Cloudflare

  • This only needs to be done if you if don't have any DNS entries in Cloudflare.

  • As I switched from No-IP to Cloudflare, I already had entries, so I skipped this step.

  • On the Cloudflare dashboard, select your domain, then select DNS on the left.

    • Type: A
    • Name: example (you can use anything)
    • IPv4 address: 127.0.0.1
    • Proxy status: uncheck . . . should say DNS only
    • TTL: Auto
    • Save this entry.

    Placeholder DNS entry in Cloudflare

Ubiquiti Dream Machine Pro setup and configuration

Once the Cloudflare configuration is done, we need to configure the UDM Pro to use what we configured.

Install udm-boot on your UDM

  • SSH into your UDM-Pro

  • Switch to the UniFi shell

    unifi-os shell
    
  • Download the boot scripts:

    curl -L https://udm-boot.boostchicken.dev -o udm-boot_1.0.5_all.deb
    
  • Install the boot scripts:

    dpkg -i udm-boot_1.0.5_all.deb
    
  • Exit out of the UniFi shell (this is important!):

    exit
    

Create the Cloudflare DDSN config file on the UDM-Pro

  • Make the directory structure

    mkdir -p /mnt/data/cloudflare-ddns
    
  • Create the config file /mnt/data/cloudflare-ddns/config.json

    • API token: NS4ZC3Y6MTJD0IXPIM4QJ3L8BCF-BRDYX7JZ0VQW2M
    • API key: 5e0plq5d3s9qsxivug5ch9j7gli0fgwowai3nf
    • Zone ID: g8ehy6cdmy15blu0uhbu4cye4vly6fvf0
    {
      "cloudflare": [
        {
          "authentication": {
            "api_token": "NS4ZC3Y6MTJD0IXPIM4QJ3L8BCF-BRDYX7JZ0VQW2M",
            "api_key": {
              "api_key": "5e0plq5d3s9qsxivug5ch9j7gli0fgwowai3nf",
              "account_email": "tom@example.com"
           }
         },
         "zone_id": "g8ehy6cdmy15blu0uhbu4cye4vly6fvf0",
         "subdomains": [
           "test"
         ],
         "proxied": false,
         "ttl": 300
       }
      ]
    }
    
  • Be sure to enter the subdomain(s) for your subdomain(s).
    If you're subdomain is test.example.com, the subdomain would be test.

  • You can add multiple subdomains, separated by a comma, but be sure the last entry does not end in a comma.


Create/run the boot script on the UDM-Pro

  • The boot script /mnt/data/on_boot.d/30-cloudflare-ddns.sh

    #!/bin/sh
    CONTAINER=cloudflare-ddns
    
    # Starts a cloudflare ddns container that is deleted after it is stopped.
    # All configs stored in /mnt/data/cloudflare-ddns
    if podman container exists "$CONTAINER"; then
      podman start "$CONTAINER"
    else
      podman run -i -d --rm \
        --net=host \
        --name "$CONTAINER" \
        --security-opt=no-new-privileges \
        -v /mnt/data/cloudflare-ddns/config.json:/config.json \
        timothyjmiller/cloudflare-ddns:latest
    fi
    
  • Make the boot script executable

    chmod +x /mnt/data/on_boot.d/30-cloudflare-ddns.sh
    
  • Run the boot script

    /mnt/data/on_boot.d/30-cloudflare-ddns.sh
    Trying to pull docker.io/timothyjmiller/cloudflare-ddns:latest...
    Getting image source signatures
    Copying blob 552d1f2373af done
    Copying blob 0e41ad7c31f9 done
    Copying blob 09160073212b done
    Copying blob 31bb49234ac4 done
    Copying blob d5a39243d769 done
    Copying blob 222389e8e52c done
    Copying blob d9a028fe6cc3 done
    Copying blob 316b4a3a65c1 done
    Copying config dabde28bf4 done
    Writing manifest to image destination
    Storing signatures
    90d1089f51345f2d99b11626ac5d00821dd3fffe74611403fd353bd28edf4f34
    

Verify the script is running

podman ps
CONTAINER ID  IMAGE                                            COMMAND               CREATED        STATUS            PORTS  NAMES
90d1089f5134  docker.io/timothyjmiller/cloudflare-ddns:latest  python -u /cloudf...  2 minutes ago  Up 2 minutes ago         cloudflare-ddns

Verify the results

  • The script will run every 5 minutes via a cronjob, so wait at least 5 minutes, then check the logs:

    podman logs cloudflare-ddns
    ⚙️ Individually disable IPv4 or IPv6 with new config.json options. Read more about it here: https://github.com/timothymiller/cloudflare-ddns/blob/master/README.md
    ⚙️ No config detected for 'purgeUnknownRecords' - defaulting to False
    🕰️ Updating IPv4 (A) & IPv6 (AAAA) records every 5 minutes
    🧩 IPv6 not detected
    ➕ Adding new record {'type': 'A', 'test': '', 'content': '123.456.789.012', 'proxied': False, 'ttl': 300}
    

References

github.com - timothymiller / cloudflare-ddns https://github.com/timothymiller/cloudflare-ddns

github.com - boostchicken-dev / udm-utilities https://github.com/boostchicken-dev/udm-utilities

Cloudflare - API Tokens https://dash.cloudflare.com/profile/api-tokens

How To SSH Into Your UniFi Dream Machine https://evanmccann.net/blog/2020/5/udm-ssh