I got tired of remembering which port each self-hosted service was running on. 192.168.1.50:3000, 192.168.1.50:8080, 192.168.1.50:9000—it gets old fast.

So I set up *.local domains. Now I just type toolshed.local or photos.local in any browser on my network. Much easier.

What are .local domains?

The .local top-level domain is reserved for local network use. It’s not routable on the internet—it only works within your home network. That makes it perfect for self-hosted services.

When you set up myapp.local, any device on your network can reach your service by typing that into a browser. No port numbers to remember. No IP addresses to look up.

This works through mDNS (multicast DNS), which broadcasts hostname-to-IP mappings across your local network. On Linux, Avahi handles this.

My setup

I’m using Dokploy to manage my self-hosted services. It’s a self-hosted PaaS that handles deployment, SSL, and reverse proxying. Think of it as a simpler alternative to Kubernetes for home use.

Dokploy runs Traefik under the hood for routing. When I add a new service, I tell Dokploy to route myservice.local to the right container. The missing piece was getting myservice.local to actually resolve to my server’s IP.

That’s where Avahi comes in.

Setting up Avahi

First, install the avahi daemon and utilities:

sudo apt update
sudo apt install avahi-daemon avahi-utils

Start and enable the daemon:

sudo systemctl enable avahi-daemon
sudo systemctl start avahi-daemon

At this point, your server’s hostname should already be available as hostname.local on your network. But we want custom domains for each service.

The add/remove scripts

I wrote two scripts to make adding and removing local domains painless. Each domain gets its own systemd service that publishes the hostname via Avahi.

Add a domain

Create /usr/local/bin/add-local-domain:

#!/bin/bash
set -e

if [ -z "$1" ]; then
    echo "Usage: add-local-domain <name>"
    echo "Example: add-local-domain toolshed"
    echo "         → Creates toolshed.local"
    exit 1
fi

NAME="$1"
IP=$(hostname -I | awk '{print $1}')
SERVICE_FILE="/etc/systemd/system/avahi-alias-${NAME}.service"

if [ -f "$SERVICE_FILE" ]; then
    echo "Service ${NAME}.local already exists"
    exit 1
fi

cat > "$SERVICE_FILE" << UNIT
[Unit]
Description=Publish ${NAME}.local via Avahi
After=avahi-daemon.service
Requires=avahi-daemon.service

[Service]
Type=simple
ExecStart=/usr/bin/avahi-publish -a -R ${NAME}.local ${IP}
Restart=on-failure

[Install]
WantedBy=multi-user.target
UNIT

systemctl daemon-reload
systemctl enable "avahi-alias-${NAME}"
systemctl start "avahi-alias-${NAME}"

echo "✓ ${NAME}.local is now available on your network"
echo "  Set '${NAME}.local' as the domain in Dokploy for your service"

Make it executable:

sudo chmod +x /usr/local/bin/add-local-domain

Remove a domain

Create /usr/local/bin/remove-local-domain:

#!/bin/bash
set -e

if [ -z "$1" ]; then
    echo "Usage: remove-local-domain <name>"
    exit 1
fi

NAME="$1"
SERVICE_FILE="/etc/systemd/system/avahi-alias-${NAME}.service"

if [ ! -f "$SERVICE_FILE" ]; then
    echo "Service ${NAME}.local doesn't exist"
    exit 1
fi

systemctl stop "avahi-alias-${NAME}"
systemctl disable "avahi-alias-${NAME}"
rm "$SERVICE_FILE"
systemctl daemon-reload

echo "✓ ${NAME}.local removed"

Make it executable:

sudo chmod +x /usr/local/bin/remove-local-domain

Using it

Adding a new local domain is now one command:

sudo add-local-domain photos

Then in Dokploy (or whatever reverse proxy you’re using), set the domain to photos.local. That’s it.

To remove it later:

sudo remove-local-domain photos

Caveats

A few things to keep in mind:

  • mDNS support varies. Most modern devices handle .local fine, but some older devices or corporate networks might not.
  • This is local only. If you want to access services from outside your network, you’ll need a different solution (VPN, Cloudflare Tunnel, etc.).
  • One server per domain. If you have multiple servers, each publishing the same .local name, things get weird. Keep names unique.
  • Manual step. Every time I add a service in Dokploy, I have to remember to run the script separately. It would be great if Dokploy could handle this for me—maybe a hook that auto-publishes the domain when I configure a service.

That’s it. Simple scripts, but they’ve made my home server setup much more pleasant to use. No more bookmark folders full of 192.168.x.x:yyyy links.

I’d like to make this more automated. If you have ideas on how to streamline this—maybe a Dokploy plugin, a file watcher, or something else entirely—I’d love to hear them.