Cost efficient dynamic DNS solution with AWS resources
Fri 27 June 2025For my home lab, I have the need to expose some services on the public internet. My current ISP does not offer fixed IP addresses. So I was looking at some of the dynamic DNS providers out there. I did not feel like spending money on it, and the free offerings look a bit shady. I happen to already have an AWS account for running some workloads. So I was exploring the Route 53 and Lambda offerings a bit and saw that I can build a dynamic DNS easily with low effort myself. And I like building stuff. 😀
Dynamic DNS server
So the decision was made to build my own. I jumped into it and created a new project on GitHub: Simple Dynamic DNS with AWS
I created a little application which eats an HTTP requests with parameters containing dynamic DNS update data. It then configures a domain you own on AWS with Route 53 DNS service. This application is put in a Docker container which is then run via an AWS Lambda function. That's basically it. I want my infrastructure to be fully declarative, so I choose to provision the AWS resources with OpenTofu. This makes it also very easy to install by anyone else as you just need to pull the repo and create your own configuration:
$ git pull https://github.com/max-pfeiffer/simple-dynamic-dns-aws.git
$ cd dynamic-dns-update-client/opentofu
$ cp credentials.auto.tfvars.example credentials.auto.tfvars
$ vim credentials.auto.tfvars
Then run a couple of OpenTofu commands:
$ tofu init
$ tofu plan
$ tofu apply
The Lambda function URL and API token are provided as output. As api_token
is marked, as security-sensitive you need
to call for it specifically:
$ tofu output lambda_function_url
$ tofu output api_token
For updating the DNS entry for your domain just call your new Lambda function adding your ip
and your client_id
:
$ curl "https://uwigefgf8437rgeydbea2q40jedbl.lambda-url.eu-central-1.on.aws/?domain=www.example.com&ip=123.45.56.78&client_id=myrouter&token=78234rtgf438g7g43r4bfi3784fgh"
Dynamic DNS Client
Most consumer grade routers offer an option in their web user interface to configure updates for a dynamic DNS provider. In a lot of cases, this is not very flexible and bound to some commercial providers that are out there. If the provider you are using is not supported, you are screwed. On OpenWRT routers configuration options are plentyful, but configuration is a nightmare, especially if you have some more exotic use case like I have.
So I decided to write a dynamic-dns-update-client myself. It's basically a companion project for Simple Dynamic DNS with AWS. It's a little CLI tool written in Python that obtains your public IP address by different means and then updates the IP address at the dynamic DNS provider using an HTTP request. You can run it locally on your machine, on an OpenWRT router or on any other machine in your infrastructure. It requires:
- Python v3.11 or higher
- pip
You can pick from four methods for getting the public IP address:
openwrt_network
: on an OpenWRT device by calling OpenWRT specific functionsinterface
: physical network interface to look for the public IP address (parsesip
orifconfig
output)- by calling one of the following IP address services using an HTTP GET request:
This way you are quite flexible where you run it. You can install and run it on your router in front of your infrastructure. Or you can run it on any device or in a container anywhere else in your IT infrastructure behind your router when you use one of the two IP address services.
Usage
Install it with pip:
$ pip install dynamic-dns-update-client
You can use it for calling your new Lamda function, which you created with Simple Dynamic DNS with AWS.
Start playing around and use the --dry-run
option to check how it works:
$ dynamic-dns-update-client https://uwigefgf8437rgeydbea2q40jedbl.lambda-url.eu-central-1.on.aws/ \
--ip-address-url-parameter-name ip \
--url-parameter domain=example.com \
--url-parameter api-token=nd4u33huruffbn \
--dry-run
Current IP address: 82.4.110.122
Dry run, no changes will be made.
Dynamic DNS provider URL: https://uwigefgf8437rgeydbea2q40jedbl.lambda-url.eu-central-1.on.aws/?ip-address=82.4.110.122&domain=example.com&api-token=nd4u33huruffbn
OpenWRT installation/configuration
For installing and running dynamic-dns-update-client on your OpenWRT router, you need to install the Python v3 interpreter and pip. You need the packages python3-light and python3-pip (and some MB of free space on your router). The packages can be installed via Luci web interface or via SSH:
$ opkg install python3-light python3-pip
$ pip install dynamic-dns-update-client
If you want to use it on your OpenWRT router, just
add a cron job for it. DNS entries are cached. In my experience,
an update every 10 minutes is sufficient. It's also a good idea to leverage the --cache-ip-address
option. This
minimizes the number of HTTP requests you send to the dynamic DNS server. If you happen to use my solution, AWS is
billing you for every Lambda function run.
So a crontab entry for an OpenWRT router would look like this:
*/10 * * * * dynamic-dns-update-client https://example.com --ip-address-provider openwrt_network --ip-address-url-parameter-name ip --url-parameter domain=example.com --url-parameter api-token=nd4u33huruffbn --cache-ip-address