Got a load of self-hosted services and can't remember all their ports? Only got a couple but you still prefer writing
myawesome.subdomain.com instead of
machineIP:ServicePort? This is for you.
It's totally possible and pretty easy to access your services via their own URL, and have it served locally. What do I mean? Well let's take Radarr for instance. If we assume you've set up a docker container for it on the default port of 7878, and your machine's LAN IP is 192.168.0.10, you probably know you can access the GUI through your browser by typing
http://192.168.0.10:7878 into the address bar. You may also know that you can set up a reverse proxy to access it remotely by an URL you would define, something like
Using that last method is great, but it basically takes a longer route through a public space (the internet) rather than keeping the request inside your LAN. Well, there's a way to have both! Today we're going to look at SWAG (an nginx-based reverse proxy) and AdGuard, both on a
Before we get started, you'll need the following:
- Docker and docker-compose installed on your machine
- The ability to SSH / use CLI/terminal on your machine, or use Portainer to spin up your stacks
- Some sort of access and relevant permissions to manage and create new directories
- A Fully Qualified Domain Name (FQDN, e.g.
- A reverse proxy already set up for the service you want, on its own docker network (for instance called
proxy) with the relevant SSL certificate(s) (check out my SWAG article for some info on how to do that if you haven't already)
Setting up your MacVlan network
This is a type of network which allows us to give a docker container its own IP on our LAN's subnet (i.e. if your LAN is
192.168.0.0/24, you could assign the container an IP of
192.168.1.100 and it will be accessible at that IP without needing to use a port).
First, we need to find the parent network of your machine's IP (make sure you know what your machine's IP is already):
- SSH into your machine and if necessary access the root user by typing
sudo su -followed by your password
if you don't want to access root, then append each docker command below with
Enter, and you'll get a chunk of network information
- Scroll down the list, looking for your machine's IP in the blocks on the right. It should be towards the bottom
- Once you've located your IP, take note of the network adapter which is on the left hand side, and will likely be something like
So we've identified the parent network, here it's
eth1. Next we need to specify the macvlan network and set it up. The format goes like this (everything in
[ ] brackets will be specific to your setup):
So let's say the subnet was
192.168.0.0/24, the gateway would be
192.168.0.1, you've found your parent network is
eth1 and you want to call the network
docker network create -d macvlan --subnet=192.168.0.0/24 --gateway=192.168.0.1 -o parent=eth1 mymacnetwork
remember, all commands and information typed into the cli are case sensitive
That's the macvlan set up. Now to create AdGuard and add it to your macvlan:
- Still in SSH, create your AdGuard container (you can use the following as a template, don't forget to create the relevant folders/directories)
Once up and running, you should be able to access your AdGuard instance at
http://192.168.0.200 (or whatever IP you set).
If you wanted to create the macvlan network as part of your compose file, you could use this instead:
networks: mymacnetwork: name: mymacnetwork driver: macvlan driver_opts: parent: eth1 ipam: config: - subnet: 192.168.0.0/24 proxy: external: true services: adguard: #ad blocking and local DNS, set your router's DNS to this container's IP address container_name: adguard image: adguard/adguardhome ports: - 3000:3000/tcp - 54:53/tcp - 54:53/udp - 67:67/udp - 67:67/tcp - 69:68/tcp - 69:68/udp - 853:853/tcp volumes: - ./adguard/work:/opt/adguardhome/work - ./adguard/conf:/opt/adguardhome/conf restart: unless-stopped networks: mymacnetwork: ipv4_address: 192.168.0.200 #change as necessary to an UNUSED IP on your network proxy:
Setting up local routing
Let's go back to our
https://radarr.mycooldomain.com example. You've already got your reverse proxy set up to serve radarr to any request for that URL when received externally. But we want to serve it when the request stays internal to our network only.
Well, the macvlan you created is going to come in useful again, as you're going to add your reverse proxy to it on its own IP (but you'll also keep it on the proxy network).
Assuming your reverse proxy container is called
swag, and we're adding it to the same
mymacnetwork with an IP address of
192.168.0.201 (assuming that IP is currently unused):
- SSH into your machine
- Type the following (modify to match your setup)
docker network connect --ip 192.168.0.201 mymacnetwork swag
And done. Why did we do this and not just leave our RP on its own network? Well AdGuard has an option to route DNS requests locally to any IP. But it CANNOT route them to specific ports. So whatever was going to serve your request needed to be on its own IP, and now it is.
Right, back to AdGuard, and we're looking for a screen called
Once inside the
DNS rewrites page:
Add DNS rewrite
- In the pop-up modal, add either your specific
https) OR, to catch all subdomains you create a reverse proxy for,
- Then add the IP address you set for your reverse proxy in the next box, and press
Ok great. Before, you would type
radarr.mycooldomain.com into your browser, and it would come up with your radarr GUI. Now it.... does the same? So how do you know it's working?
There's a way to check this, using the
nslookup tool in CLI/terminal:
If you'd run this before creating the DNS rewrite, it would have popped up with the server and IP address of whichever public DNS you were using - it could have been Google and 220.127.116.11, or Cloudflare and 18.104.22.168. Now, it should show
adguard as the server name, and the address would be
Below that, you should see the name of your request
radarr.mycooldomain.com followed by your reverse proxy's IP address
If that's what's showing, then congrats, you've now got local DNS serving you your apps using your domain, all with SSL, without ever leaving your network.
PiHole instead of AdGuard
Assuming you've already got PiHole set up, it's essentially exactly the same process.
- After logging in, click
Local DNSin the left hand navigation panel, then
DNS Recordsin the drop down
- Add your domain (without a
*and without the
- Add the IP address (e.g.
Add, let it do it's thing, and then test in the same way as above