Lately I’ve been learning how to administer services that I want to run for fun. Some of these services don’t make sense for me to run outside of my network (i.e., on a VPS somewhere) but I still might want access to them if I’m ever away from my home network. A good example of this would be my IRC bouncer that I installed on my Raspberry Pi Zero W.
Since I don’t have a static IP for my home network (and I don’t want to ask for one from my ISP), I decided to go with DuckDNS as my dynamic DNS service. DuckDNS allows you to set a domain name like
blah.duckdns.org and as long as you periodically make an HTTP request from your network to one of
duckdns.org‘s endpoints, DuckDNS will automatically update
blah.duckdns.org to point at your (potentially changing) home IP address.
The usual instructions for DuckDNS show you how to get started quickly by setting up a cronjob to make this HTTP request. Let’s go against the grain and get some more experience with systemd.
To get started, go ahead and create a domain at duckdns.org. Leave the instructions open for what the HTTP request is supposed to be.
Create a DuckDNS user
There’s no reason this script needs to run as your user or even worse, as root.
$ sudo groupadd duckdns $ sudo useradd duckdns -g duckdns -m
The above commands create the
duckdns group and a user named
duckdns. It adds the
duckdns user to the
duckdns group (
-g flag) and creates a home directory for the
duckdns user (
Save the DuckDNS curl one-liner
DuckDNS shows you a cool one-liner for making the HTTP request when you create your DuckDNS domain. It should look something like this:
echo url="https://www.duckdns.org/update?domains=YOUR_DOMAIN&token=YOUR_TOKEN&ip=" | curl -k -o ~/duck.log -K -
Note that I’ve changed the output file in the
curl command from
~/duckdns/duck.log to just
~/duck.log. If you don’t do this, you’ll need to create this folder structure for the script to work:
Go ahead and save that one-liner to
duckdns the owner of that file with:
$ sudo chown duckdns:duckdns /home/duckdns/duckdns.sh $ sudo chmod 700 /home/duckdns/duckdns.sh
Now let’s go ahead and create the actual systemd unit and timer.
Make a systemd unit
[Unit] Description=DuckDNS heartbeat After=network-online.target Wants=network-online.target [Service] User=duckdns Group=duckdns Type=oneshot ExecStart=/bin/bash /home/duckdns/duckdns.sh ProtectSystem=yes NoNewPrivileges=yes PrivateTmp=yes [Install] WantedBy=multi-user.target
Some of these options may admittedly be overkill, but let’s review some of the essentials:
Wants=network-online.target: since our service requires network connectivity to work, we require there to be a network connection before it runs.
Group=duckdns: The process should be ran as
Type=oneshot: This means that systemd will consider the unit “active” once it exits successfully.
WantedBy=multi-user.target: Once the system has reached a level where multiple users can login, we want this unit activated.
Make a systemd timer
[Unit] Description=Timer for DuckDNS unit [Timer] OnCalendar=*:0/5 [Install] WantedBy=timers.target
This one is a little bit simpler. The
OnCalendar line is set to run the unit every five minutes.
Activate the timer
$ sudo cp duckdns.timer /lib/systemd/system/ $ sudo cp duckdns.service /lib/systemd/system/ $ sudo systemctl enable duckdns.timer
Confirm that it works
The service runs every five minutes, so you can sanity check that it’s scheduled to run and that it has ran with:
$ sudo systemctl list-timers --all
NEXT LEFT LAST PASSED UNIT ACTIVATES Mon 2020-09-07 17:55:00 BST 2min 56s left Mon 2020-09-07 17:50:07 BST 1min 56s ago duckdns.timer duckdns.service
Furthermore, you can also check the modified-time on
/home/duckdns/duck.log: (the minute timestamp should be a multiple of 5)
$ ls -lh /home/duckdns/duck.log
-rw-r--r-- 1 duckdns duckdns 2 Sep 7 17:50 /home/duckdns/duck.log
And finally, you can check to make sure
/home/duckdns/duck.log has “OK” logged (this indicates the script ran successfully):
$ cat /home/duckdns/duck.log
You should now be able to reach your home network with your DuckDNS domain name!