How to Setup DuckDNS on a Raspberry Pi!

Today we are going to look at how to setup DuckDNS on a Raspberry Pi.

DDNS is a way to automatically update a name server (in this case, DuckDNS) in real-time with your external IP address. There are many reasons why you might need a DDNS hostname, with one of the most common being creating a VPN Server. In this tutorial, we will run through the commands to setup DDNS on a Raspberry Pi.

Instructions - DuckDNS Raspberry Pi

Before you start the setup tutorial, ensure that you have navigated to https://www.duckdns.org and setup a user account. We will be using the domain and token that exist on that page

1. SSH into your Raspberry Pi. If you aren’t sure how, you can find our tutorial here.

2. After you SSH in, we need to make a directory and then create a file in that directory that will run at a scheduled time (created in later steps).

mkdir duckdns
cd duckdns
nano duck.sh

3. We’re now going to place a script in this file that will run at a scheduled interval. The script needs to have the domains you are using (from DuckDNS) added to it.

echo url="https://www.duckdns.org/update?domains=[YOUR_DOMAIN]&token=[YOUR_TOKEN]&ip=" | curl -k -o ~/duckdns/duck.log -K -

4. We now need to change the permissions of these files and access crontab to schedule this script to run every five minutes. Crontab is a task schedular that automates certain commands.

chmod 700 duck.sh
crontab -e

5. Paste the contents of the file below in the crontab file.

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

8. The job will now run every five minutes. We will test it, and assuming it works, start the cron service. If you receive OK after running this command, the process worked properly. If you receive KO, you have an error somewhere in your script file or you did not give the file the proper permissions.

./duck.sh
cat duck.log
sudo service cron start

Conclusion

This is a very simple and straight forward process, but we will be building on top of this in future tutorials. Ensuring that you are connecting to your external IP address is very important when you use services that require your external IP address.

If you’re interested in using your new DDNS to setup a WireGuard VPN, check out our tutorial here!

This Post Has 16 Comments

  1. Thank you very much for this tutorial. I am having a problem though, everything went well until I got to the step

    [email protected]:/duckdns# ./duck.sh

    And this is what I got:

    % Total % Received % Xferd Average Speed Time Time Time Current
    Dload Upload Total Spent Left Speed
    0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0Warning: Failed to create the file /root/duckdns/duck.log: No such file or
    Warning: directory
    100 2 0 2 0 0 3 0 –:–:– –:–:– –:–:– 3
    curl: (23) Failed writing body (0 != 2)
    % Total % Received % Xferd Average Speed Time Time Time Current
    Dload Upload Total Spent Left Speed
    0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0Warning: Failed to create the file /root/duckdns/duck.log: No such file or
    Warning: directory
    100 2 0 2 0 0 3 0 –:–:– –:–:– –:–:– 3
    curl: (23) Failed writing body (0 != 2)

    Sorry, this is probably very basic, but it’s not working for me.

    Thanks

    1. Thanks for reading the tutorial!

      That’s a weird error, as the log file should be automatically created. Can you run the commands below and see if the “duck.log” file exists?

      cd /duckdns
      ls

      If it doesn’t exist, can you run the command below, create the file (keep it blank, just run the command below and press CTRL+O) and then try again? Make sure you are in the duckdns folder (cd /duckdns).

      sudo nano duck.log

      Sorry for the basic troubleshooting, it’s just a weird error since that file is normally automatically created. Let me know how it goes!

    2. try with “/home/pi/duckdns/duck.log”
      this is how i is working for me

      echo url=”https://www.duckdns.org/update?domains=xxxxxxxxxxxxx&token=xxxxxx&ip=” | curl -k -o /home/pi/duckdns/duck.log -K –

      1. Thanks for helping out!

  2. I was having similar issues and I think I figured it out. Sorry if this is a duplicate post, the first one disappeared when I clicked post.

    Using ~/duckdns/duck.log tried to creat the log in /root/duckdns/duck.log, which we do not have permissions to. Stating the home directory for our user explicitly seems to fix this. /home/pi/duckdns/duck.log

    The same is true when stating ~/duckdns/duck.sh in the crontab -e, it tries to run the file in the /root/duckdns/duck.sh location, which doesn’t exist. Again stating /home/pi/duckdns/duck.sh seems to fix this.

    Now everything is running fine from what I can tell. I am not sure if this is a recent change to Raspberry Pi OS that changed the way things treat the home directory.

    1. This is great information and when I get some time, I will test everything out on my own to ensure the tutorial doesn’t need to be updated.

      I really appreciate you sharing this! If you need anything, please let me know!

  3. I have the duckdns client running on a raspberry pi in a remote location, which is prone to power cuts.
    After a recent power cut, the external IP address changed, but this was not reported to the duckdns server.
    I am wondering if this is because the client only reports a change after the cron job runs – i.e. every five minutes.
    In that case it would not see the change.
    Is there a way to make the client send the current external IP address to the duckDNS server when it boots up (after a power cut)?

    1. You are correct that the cron job is what reports the new external IP address to DuckDNS. However, cron should start when the Raspberry Pi is booted up.

      Can you reboot your machine and then run this command to see if cron starts automatically?

      sudo service cron status

      If it’s not running, can you run this command to start it up, reboot it and see if it automatically starts back up?

      sudo service cron start

      Let me know and we can continue troubleshooting!

      1. Good afternoon,
        I tried some more experiments today. I edited duck.sh and the cron job to change ~ to /home/pi/
        a) when I ran the the script it still sent the IP to duckdns,
        and
        b) the cronjob ran every five minutes – and sent the IP address to duckdns.
        Success!

        But I still had a little clearing up to do
        Having done some reading about cron, I discovered that I had two cron jobs running.

        Nov 18 14:50:02 LTrevproxynginx CRON[15964]: (pi) CMD (/home/pi/duckdns/duck.sh >/dev/null 2>&1)
        Nov 18 14:50:02 LTrevproxynginx CRON[15966]: (root) CMD (~/duckdns/duck.sh >/dev/null 2>&1)

        One owned by “pi” – the new job, with the amended path
        and the other owned by “root” – the original job that I created several weeks ago, with the original ~ path

        But if I type crontab -l I only see one job.

        I realized that I must have created the original job by typing sudo crontab -e and that created the “root” job
        (I had got used to using sudo nano to create and edit files!)

        I typed sudo crontab -l and found the original cronjob

        I used sudo crontab -e and removed the */5 * * * * ~/duckdns/duck.sh >/dev/null 2>&1 line
        restarted cron, and checked status.

        I now only have one cron job running, with owner “pi”

        Fingers crossed that that is everything fixed.

        Thanks for your help

        Regards,

        Boyd.

        1. I apologize for not having a chance to respond to your other messages. I’ve been busy and finally had a chance to sit down and catch up. It sounds like you’ve narrowed down what the issue is and I’m hoping that it is resolved with your latest changes. If it’s not, let me know and I will do my best to help troubleshoot!

  4. Thank You. Here is a little more background.
    Like Chuck, above, I could not get duck,sh and duck.log to work when I followed the instructions on the duckdns website.
    I had a look at the raspbmc instructions, and found that they start with which start with cd ~ to access the root directorty.
    So I tried doing that, and was able to run duck.sh which created duck.log, and also sent the IP address to the duckdns server.
    I assumed that I had solved the problem, but I now realize that the cron job is either not running, or is not pointing to the correct path for the duck.sh script.
    Before reading your message this afternoon, I had done some more research and figured out that the duck.sh script has no memory, it simply reports the current IP address to the duckdns server when it is triggered.
    I was not sure it the duckdns server would show updates if the IP address did not change, so I set up the following experiment.
    I have two instances of duck.sh running on two separate Raspberry Pi’s, one on my home network, and the other in a remote location on a separate network – so they both have different IP addresses, but both have the same duckdns domain name.
    (I can access the remote site via puTTy on a PC running Anydesk.)
    When I run the script manually with ./duck.sh on either machine, the duckdns website sees the appropriate IP address.
    I then set up the same cron job on both machines, ran sudo service cron start on one machine, waited for a couple of minutes and did the same on the second machine. I expected to see the IP addresses alternating every 2-3 minutes on the duckdns website.
    But this does does not happen, the IP address never changes, and the website does not show updates every 2-3 minutes.
    This confirms my suspicion that the cron job is not running correctly.

    I have just rebooted (the local machine) and typed sudo service cron status
    Here is the report

    [email protected]:~ $ sudo service cron status
    ● cron.service – Regular background program processing daemon
    Loaded: loaded (/lib/systemd/system/cron.service; enabled; vendor preset: ena
    Active: active (running) since Tue 2020-11-17 19:02:57 GMT; 1h 22min ago
    Docs: man:cron(8)
    Main PID: 246 (cron)
    CGroup: /system.slice/cron.service
    └─246 /usr/sbin/cron -f

    Nov 17 19:02:57 LTrevproxynginx systemd[1]: Started Regular background program p
    Nov 17 19:02:57 LTrevproxynginx cron[246]: (CRON) INFO (pidfile fd = 3)
    Nov 17 19:02:57 LTrevproxynginx cron[246]: (CRON) INFO (Running @reboot jobs)
    Nov 17 20:25:01 LTrevproxynginx CRON[452]: pam_unix(cron:session): session opene
    Nov 17 20:25:01 LTrevproxynginx CRON[451]: pam_unix(cron:session): session opene
    Nov 17 20:25:01 LTrevproxynginx CRON[460]: (pi) CMD (~/duckdns/duck.sh >/dev/nul
    Nov 17 20:25:01 LTrevproxynginx CRON[462]: (root) CMD (~/duckdns/duck.sh >/dev/n
    Nov 17 20:25:01 LTrevproxynginx CRON[452]: pam_unix(cron:session): session close
    Nov 17 20:25:03 LTrevproxynginx CRON[451]: pam_unix(cron:session): session close
    lines 1-17/17 (END)
    ● cron.service – Regular background program processing daemon
    Loaded: loaded (/lib/systemd/system/cron.service; enabled; vendor preset: enabled)
    Active: active (running) since Tue 2020-11-17 19:02:57 GMT; 1h 22min ago
    Docs: man:cron(8)
    Main PID: 246 (cron)
    CGroup: /system.slice/cron.service
    └─246 /usr/sbin/cron -f

    Nov 17 19:02:57 LTrevproxynginx systemd[1]: Started Regular background program processing daemon.
    Nov 17 19:02:57 LTrevproxynginx cron[246]: (CRON) INFO (pidfile fd = 3)
    Nov 17 19:02:57 LTrevproxynginx cron[246]: (CRON) INFO (Running @reboot jobs)
    Nov 17 20:25:01 LTrevproxynginx CRON[452]: pam_unix(cron:session): session opened for user root by (uid=0)
    Nov 17 20:25:01 LTrevproxynginx CRON[451]: pam_unix(cron:session): session opened for user pi by (uid=0)
    Nov 17 20:25:01 LTrevproxynginx CRON[460]: (pi) CMD (~/duckdns/duck.sh >/dev/null 2>&1)
    Nov 17 20:25:01 LTrevproxynginx CRON[462]: (root) CMD (~/duckdns/duck.sh >/dev/null 2>&1)
    Nov 17 20:25:01 LTrevproxynginx CRON[452]: pam_unix(cron:session): session closed for user root
    Nov 17 20:25:03 LTrevproxynginx CRON[451]: pam_unix(cron:session): session closed for user pi

    I am guessing that this is saying that the cron job is running, but the duckdns server is still showing the IP address of the distant machine.

    I suspect that this problem goes back to the initial issue of having to install the script in the root directory (in a duckdns directory)

    Here is what I see if I type pwd and ls -a

    [email protected]:~ $ pwd
    /home/pi
    [email protected]:~ $ ls -a
    . .. .bash_history .bash_logout .bashrc duckdns .gnupg .local .profile .selected_editor

    Things to try.
    1 Change the cronjob to: */5 * * * * /home/pi/duckdns/duck.sh >/dev/null 2>&1
    then run sudo service cron start

    wait five minutes and see what happens.

    Please let me have your thoughts!

    Regards,

    Boyd

  5. A follow up . . . the good thing about problems is that you learn from tackling them.
    Cron timing
    I read a tutorial on cron last night and realized that the timing is relative to a real-time clock, and not just the time the computer starts running. That meant that my experiment where I ran the same cron job in two machines starting one of them two minutes after the other would not result in them running scripts in alternation.
    I have set up the same test this morning, using 2/5 * * * * in one machine and */5 * * * * in the other machine.

    The full string in the local machine is
    2/5 * * * * ~/home/pi/duckdns/duck.sh >/dev/null 2>&1
    but this is not sending the IP address to the duckns server

    I then tried
    2/5 * * * * /home/pi/duckdns/duck.sh >/dev/null 2>&1
    this also does not work

    Regards,

    Boyd.

  6. So I had this working a while back with these instructions but had to reinstall the OS when I got an M.2 case for my Pi to give it a boost and real space.

    My current situation: My Pi is meant to stay on a VPN. When I stop the OpenVPN service and do an NSLOOKUP from another computer, it works. When I have the VPN on and do the NSLOOKUP, I get a DNS request time out. I checked the duck.log and it’s reporting a “502 Bad Gateway” when it is in this state. What step am I missing here that when I basically am just changing my external IP address, I can’t access the Pi? Is this just a basic function of using the VPN so I can’t be tracked and now it’s coming back on me?

    1. Are you using a full-tunnel VPN for the Raspberry Pi? Is your local subnet and VPN subnet the same? I haven’t run this on a server that’s always connected to a VPN, so I’m not sure exactly how it will work, but it does sound like it could cause problems. Mostly if you’re using a full-tunnel VPN.

      1. I’m currently running a split-tunnel so that I can access it from other nodes on my network, all the same sub-net. It has to be some mismatch it’s not able to handle with the VPN being on.

        More nebulous question. The point of getting all of this working properly was because I had to reinstall WireGuard on my Pi4 after I needed to reinstall the OS, I have a Pi Zero just running AdGuard on my network. What type of performance degradation would I get running WireGuard on the Pi Zero? I know it’s super low power compared to the 4 but I’d rather not have to run an additional Pi, pretty much because I’m out of outlets on my UPS!

        1. It would be a pretty big performance hit. Not necessarily from a processing point of view (not ideal either), but from a network speeds point of view. The Raspberry Pi 4 has Gigabit ethernet (10/100/1000) whereas the Pi Zero with a Micro-USB to Ethernet adapter will only be 10/100. For this reason, you will see significantly slower connection times.

Leave a Reply

Close Menu