How to Set Up DuckDNS for Free Dynamic DNS (Raspberry Pi and Linux)

How to Set Up DuckDNS for Free Dynamic DNS (Raspberry Pi and Linux)

Before I set up proper internal DNS, DuckDNS was the glue holding my remote access together. I had a Raspberry Pi running a handful of services at home, a residential IP address that changed every few days, and no interest in paying for a static IP from my ISP. DuckDNS solved the problem in about ten minutes, and it cost exactly nothing.

If you are self-hosting anything, even a single service behind your home router, you need a way for the outside world to find your network when your IP address changes. That is the problem dynamic DNS solves. DuckDNS is the simplest way to get started, and for many setups it is genuinely all you will ever need.

I will be upfront: if you own a domain and you are comfortable with Cloudflare, you will eventually outgrow DuckDNS. I did. But DuckDNS got me running in minutes while I was still figuring out the rest of the stack, and it is still the right answer for anyone starting out with self-hosting or running a straightforward Pi project.

From the field: Dynamic DNS is one of those invisible pieces of infrastructure that makes everything else work. I have been running DuckDNS for years — it keeps my homelab accessible even when my ISP changes my IP. Takes 5 minutes to set up and then you never think about it again.

Network patch panel with ethernet cables connected, representing home network infrastructure for dynamic DNS

What Is DuckDNS and Why It Works

DuckDNS is a free dynamic DNS service. You get a subdomain like yourname.duckdns.org, and a small script on your machine keeps that subdomain pointed at your current public IP address. When your ISP changes your IP, the script updates the DNS record. Anyone connecting to yourname.duckdns.org always reaches your network.

There are dozens of dynamic DNS providers. I have used a few over the years, and here is my honest take on the landscape:

  • No-IP – Works fine, but the free tier requires you to manually confirm your hostname every 30 days. Miss one confirmation email and your hostname disappears. I lost access to a remote Pi because of this once. Never again.
  • Afraid.org (FreeDNS) – Solid and has been around forever. Interface looks like it was built in 2003 because it was. Functional but not intuitive.
  • Cloudflare DDNS – My current setup for production use. Requires your own domain, more configuration, but gives you full DNS control. This is where you graduate to.
  • DuckDNS – Free, no confirmation nag, dead simple, reliable. The right starting point.

DuckDNS has no paid tier because there is nothing to upsell. It is run as a hobby project by a small team and funded by donations. The service has been running since 2013 and it has been consistently available the entire time I have used it. That kind of longevity in a free service is unusual and worth respecting.

Prerequisites

You need very little to get started:

  • A Raspberry Pi or any Linux machine with internet access (this works on Ubuntu, Debian, Raspberry Pi OS, or any distro with curl and cron)
  • A GitHub, Reddit, Google, or Twitter account for DuckDNS login (they use OAuth, no separate account creation)
  • About ten minutes

Pro tip: The machine running the DuckDNS update script does not need to be the machine hosting your services. It just needs to be on the same network, because DuckDNS detects your public IP from the request origin. I run mine on a Pi that also handles Docker hosting and network monitoring, but any always-on box will do.

Step 1: Create Your DuckDNS Account and Subdomain

Go to duckdns.org and sign in with one of the supported accounts. Once logged in, you will see a simple dashboard with a text field.

Type the subdomain you want and click “add domain.” For example, if you enter mylab, you get mylab.duckdns.org.

At the top of the page, you will see your token. This is a UUID string that authenticates your update requests. Copy it and keep it safe. Anyone with this token can update your DNS record.

Treat your DuckDNS token like a password. Do not commit it to a public Git repository. Do not paste it into forum posts when asking for help. If you suspect it has been compromised, regenerate it from the DuckDNS dashboard.

You can create up to five subdomains on a single free account, which is more than enough for most homelab setups.

Step 2: Install the Update Script

SSH into your Pi or Linux box. We are going to create a directory for DuckDNS files, write the update script, and set the correct permissions.

mkdir -p ~/duckdns
nano ~/duckdns/duck.sh

Paste the following script. Replace YOUR_SUBDOMAIN with the name you chose (just the name, not the full .duckdns.org) and YOUR_TOKEN with the token from your dashboard:

#!/bin/bash
# DuckDNS IP update script
# Runs via cron every 5 minutes to keep DNS record current

SUBDOMAIN="YOUR_SUBDOMAIN"
TOKEN="YOUR_TOKEN"
LOG_FILE="$HOME/duckdns/duck.log"

echo "$(date '+%Y-%m-%d %H:%M:%S') - Updating DuckDNS..." >> "$LOG_FILE"

RESPONSE=$(curl -s "https://www.duckdns.org/update?domains=${SUBDOMAIN}&token=${TOKEN}&ip=")

echo "$(date '+%Y-%m-%d %H:%M:%S') - Response: ${RESPONSE}" >> "$LOG_FILE"

Let me walk through what each part does:

  • SUBDOMAIN and TOKEN – Your credentials. Keeping them as variables at the top makes the script easy to audit.
  • LOG_FILE – Writes a timestamped log so you can verify updates are happening. Invaluable for debugging.
  • curl -s – Makes a silent HTTP request to the DuckDNS API. The -s flag suppresses the progress bar.
  • &ip= – Left empty deliberately. When you do not specify an IP, DuckDNS uses the IP address of the machine making the request. This is what you want for a home setup, as it automatically detects your public IP.
  • RESPONSE – DuckDNS returns either OK or KO. Simple. Effective.

Now make the script executable:

chmod 700 ~/duckdns/duck.sh

I use 700 rather than 755 because the script contains your token. There is no reason for other users on the system to read or execute it.

Test it manually before setting up automation:

~/duckdns/duck.sh
cat ~/duckdns/duck.log

You should see a line ending with Response: OK. If you see KO, double-check your subdomain and token.

Step 3: Set Up the Cron Job

Running the script manually defeats the purpose. We need it running automatically so that when your IP changes at 3am, the DNS record updates without you lifting a finger.

crontab -e

Add this line at the bottom:

*/5 * * * * ~/duckdns/duck.sh >/dev/null 2>&1

This runs the script every five minutes. DuckDNS is perfectly happy with this frequency, and five minutes is short enough that any IP change is caught quickly. Some guides suggest every minute, but that is excessive. Your ISP is not changing your IP every sixty seconds.

The >/dev/null 2>&1 part suppresses cron’s default behaviour of emailing output to the local user. We are already logging to duck.log, so cron mail would be redundant noise.

Pro tip: To verify your cron job is registered, run crontab -l. You should see your DuckDNS line listed. If you used sudo crontab -e by mistake, the job is registered under root’s crontab, not yours, which works but is unnecessary for this use case.

Step 4: Verify It Works

Wait five minutes for the first cron execution, then check the log:

cat ~/duckdns/duck.log

You should see timestamped entries with OK responses. Now verify the DNS record resolves correctly from another machine:

# From any machine with internet access
nslookup yourname.duckdns.org

# Or using dig for more detail
dig yourname.duckdns.org +short

The IP returned should match your current public IP. You can confirm your public IP with:

curl -s https://api.ipify.org

If those match, you are done. Your subdomain is pointing at your home network and will update automatically. Once it is running, consider adding a DNS monitor in Uptime Kuma to verify your DuckDNS subdomain keeps resolving correctly. That way you will know immediately if your dynamic DNS stops updating.

For the real test, try accessing your subdomain from outside your home network. Tether your phone and SSH into yourname.duckdns.org, or ask a friend to ping it. If you have port 22 forwarded on your router, you should connect straight to your Pi. If not, the DNS is still working, you just need to set up port forwarding, which is a router configuration step, not a DuckDNS problem.

Bonus: Pair with Let’s Encrypt for HTTPS

DuckDNS supports Let’s Encrypt TLS certificates, which means you can get proper HTTPS on a free subdomain. This matters if you are exposing any web services, as browsers will refuse to load HTTP-only sites in many contexts, and sending credentials over unencrypted connections is reckless.

The recommended approach is to use certbot with the DuckDNS DNS challenge. Install certbot and the DNS plugin:

sudo apt install certbot
sudo pip install certbot-dns-duckdns

Then request a certificate:

sudo certbot certonly \
  --authenticator dns-duckdns \
  --dns-duckdns-token YOUR_TOKEN \
  --dns-duckdns-propagation-seconds 60 \
  -d "yourname.duckdns.org"

Certbot will automatically handle renewal. Your certificates end up in /etc/letsencrypt/live/yourname.duckdns.org/ and can be referenced by Nginx, Caddy, or whatever reverse proxy you are using.

Pro tip: If you are using Nginx Proxy Manager, you can skip the manual certbot step entirely. NPM has built-in Let’s Encrypt support with a DuckDNS DNS challenge option. Just enter your token in the SSL settings and it handles everything. That is the approach I would recommend for most homelab setups.

Bonus: Using DuckDNS with Nginx Proxy Manager

This is the combination I ran for over a year before moving to my own domain. Nginx Proxy Manager (NPM) sits behind your router, receives all traffic on ports 80 and 443, and routes it to the correct container based on the hostname in the request.

The setup:

  1. DuckDNS keeps yourname.duckdns.org pointed at your public IP
  2. Your router forwards ports 80 and 443 to the machine running NPM
  3. NPM handles TLS termination using a Let’s Encrypt certificate (with the DuckDNS DNS challenge)
  4. NPM proxy hosts route traffic to internal services by matching the hostname

With five DuckDNS subdomains available, you could run files.duckdns.org for Nextcloud, media.duckdns.org for Jellyfin, git.duckdns.org for Gitea, and so on. Each one gets its own proxy host in NPM, its own certificate, and its own backend target. You could even dedicate one to a self-hosted Vaultwarden instance so your password manager is accessible from anywhere with proper HTTPS.

This is genuinely a solid setup for a homelab. I ran it exactly this way, and the only reason I moved on was that I wanted wildcard certificates and cleaner URLs, both of which require your own domain.

When to Move Beyond DuckDNS

DuckDNS is not the endgame. It is the starting point. Here is when you should consider upgrading:

  • You own a domain. If you have yourdomain.com, set up Cloudflare DDNS instead. You get wildcard certificates, full DNS control, proper CNAME records, and a URL that looks professional rather than something.duckdns.org.
  • You need internal DNS. DuckDNS is external only. For resolving hostnames inside your network, look at Pi-hole’s local DNS feature, Adguard Home, or a proper DNS server. I run Windows Server DNS internally, which is overkill for most homelabs but it was already there.
  • You want split-horizon DNS. This is where internal requests resolve to local IPs and external requests resolve to your public IP. DuckDNS cannot do this. You need a local DNS server that overrides the external record.
  • You are running more than five services externally. DuckDNS limits you to five subdomains. With your own domain, you have unlimited subdomains and can use wildcards.

The natural progression is: DuckDNS to get started, then your own domain on Cloudflare (free tier) once you have a few services running and understand what you actually need from DNS. Do not overthink it at the beginning. DuckDNS works. Use it until it does not.

Troubleshooting

Script returns KO

Check your token. Copy it fresh from the DuckDNS dashboard. Check your subdomain name, it should be just the name without .duckdns.org. Make sure there are no trailing spaces or newline characters in your variables.

Cron is not running the script

Verify with crontab -l. Check that the script path is correct and uses the full path or ~/ expansion. Look at /var/log/syslog (or journalctl -u cron) for cron execution logs. Also check that the script is executable with ls -la ~/duckdns/duck.sh.

DNS resolves but I cannot connect to services

This is almost always a port forwarding issue, not a DuckDNS problem. DuckDNS only handles DNS resolution. You still need to forward ports on your router to the correct internal machine. Check your router’s NAT/port forwarding settings.

I am behind CGNAT

This is the one scenario where DuckDNS genuinely cannot help you. Carrier-grade NAT means your ISP shares a single public IP across multiple customers, and you cannot receive inbound connections at all. No amount of port forwarding or dynamic DNS will fix this because the public IP is not yours.

Options if you are behind CGNAT:

  • Ask your ISP for a dedicated public IP (some offer this as an add-on)
  • Use a VPN tunnel like Tailscale or WireGuard to a VPS with a public IP
  • Use Cloudflare Tunnel, which creates an outbound tunnel from your network and avoids the inbound port issue entirely

To check if you are behind CGNAT, compare the IP on your router’s WAN page with the result of curl -s https://api.ipify.org. If they do not match, you are behind CGNAT.

ISP is blocking ports 80/443

Some residential ISPs block inbound traffic on common web ports. Try forwarding a high port (like 8443) instead, or use Cloudflare Tunnel to bypass port restrictions entirely. You can test whether a port is reachable from outside using an online port checker tool while your service is running.

Career context: Understanding NAT, CGNAT, port forwarding, and DNS resolution is fundamental networking knowledge. These are the exact concepts tested in CompTIA Network+, CCNA, and asked about in infrastructure interviews. Debugging your own DuckDNS setup teaches you more about real-world DNS than any certification flashcard ever will.

You Have Got Remote Access Sorted. Here Is What to Expose Next.

With DuckDNS running, your home network is addressable from the internet. That opens up everything. Here are the logical next steps:

DuckDNS is step one. The rest of your self-hosted infrastructure builds on top of it.

Watch Out For This

Set your cron job to update every 5 minutes, not every hour. IP changes can happen at any time, and an hour of downtime because your DNS is stale is easily avoidable.

Key Takeaways

  • DuckDNS is free, reliable, and the fastest way to get dynamic DNS working on a Raspberry Pi or any Linux box
  • The entire setup is one script, one cron job, and about ten minutes of your time
  • Leave the IP field empty in the update URL so DuckDNS auto-detects your public IP
  • Pair it with Nginx Proxy Manager and Let’s Encrypt for proper HTTPS on a free subdomain
  • Check for CGNAT before troubleshooting port forwarding issues, as CGNAT makes inbound connections impossible without a tunnel
  • When you outgrow DuckDNS, move to Cloudflare DDNS with your own domain, but do not rush that step
  • Set script permissions to 700, not 755, because the file contains your authentication token

Related Guides

If you found this useful, these guides continue the journey:

The RTM Essential Stack - Gear I Actually Use

Enjoyed this guide?

New articles on Linux, homelab, cloud, and automation every 2 days. No spam, unsubscribe anytime.

Scroll to Top