🕓
This post was published in 2016
The contents of this post and the code I wrote are no longer being maintained.
I still like the idea of running a custom DNS server - so please feel free to take inspiration from concept! However, this approach adds complexity and potential security risks so if you can, it might just be simpler to configure static IPs for your devices.

I have a Raspberry Pi home server that I can remotely access through a tinc VPN tunnel with my VPS. Most services can be accessed through the tunnel with addresses like plex.crawford.kiwi, but some of them are only available on my local network, for example, SMB and SSH. Accessing these services from the local network is actually more difficult than the services available remotely for two reasons:

  1. The local IP address may change. Most routers let you configure a MAC address based IP reservation, however one of my original goals for this system was that it should be able to work even in situations where I don’t have control of the router.
A representation of local IP addresses for a Raspberry Pi being changed.
  1. Differences between networks. The local address space (e.g.: 192.168.1.xx) varies between networks and the address you want to reserve may already be taken. This means that if you change your network you may also need to change your computers configuration to access the Pi at it’s new address.
Three routers all representing different networks and types of addresses.
  1. IP addresses are hard to remember. There is a reason DNS was invented! DNS allows a friendly URL to map to the underlying IP address.
192.168.1.?

My solution is to run a DNS server on my VPS that fetches the IP address from the Pi through the VPN tunnel when a request is made. This means to access my Pi through the local network, I just punch in an address like local.pi.crawford.kiwi and I can access it no matter what it’s local IP address is!

A diagram showing the information flow between the computer, local address DNS server and Raspberry Pi.

The DNS server that runs on my VPS server is something I created called local-address-dns. This runs a DNS server using dnsd and upon an incoming DNS request it connects to local-address-dns-client-rpi running on my Pi. local-address-dns-client-rpi is a simple web server that returns the Pi’s IP address on one of it’s network interfaces. An NS record on my domain points addresses like local.pi.crawford.kiwi to my VPS to be handled by local-address-dns.

A connection dialog with the local.pi.crawford.ord.nz address entered.
Connecting to SMB on the Pi. I don't ever need to worry about what the Pi's IP address is!

This works great for services like SSH and SMB which tend to use a default port, but what about when you run multiple web-based serices off the same machine? For example, if you run Plex on port 32400 and Deluge on port 8112.

In these cases, I’d love to just use a domain name like plex.local.pi.crawford.kiwi over having to remember local.pi.crawford.kiwi:32400!

Theres a few answers to this question. One is to configure NGINX to proxy the service based on the domain name being accessed, or simply to redirect to another URL based on the domain name being accessed.

I went with the redirect based approach and created virtual-host-redirector for this purpose. This makes configuration easy as you just need to setup a rules.json file with host to URL mappings.

With either of these approaches you’ll need to:

  • Make sure you aren’t running anything else on port 80. If you are, just reconfigure this service to another port and setup a redirect rule for it.
  • Configure a wildcard CNAME record with your DNS provider to the local-address-dns managed domain name. For example, mine is a CNAME record of *.local.pi.crawford.kiwi pointing at local.pi.crawford.kiwi.

Edit, 1 July 2018: I’ve added some more details about another project of mine called virtual-host-redirector.

Edit, 12 September 2023: I’ve removed the Docker images for local-address-dns, local-address-dns-client-rpi and virtual-host-redirectormade them all Public Archives on GitHub. The code is still available for inspiration but is not being maintained - especially as they were getting very behind on security updates.