Domain
Prerequisite: Watchtower
This page is where you need to make an intentional decision:
- do you actually need public access?
For many home setups, the answer is still no, and Tailscale remains the better default.
But sometimes you do want a real domain and clean HTTPS access, especially for services you plan to use frequently from many devices.
If you go down that path, do it deliberately.
Access Models
Private-Only Access
This is still my recommended default.
- no public port forwarding
- access services over LAN or Tailscale
- lowest operational burden
Public HTTPS Access
This is appropriate when:
- you genuinely need browser-friendly public URLs
- you understand that public exposure increases your attack surface
- you are willing to maintain it properly
If that describes you, keep reading.
Buy a Domain and Add It to Cloudflare
At a high level:
- register a domain from the registrar of your choice
- add it to Cloudflare
- change the registrar nameservers to the ones Cloudflare gives you
Once that is done, Cloudflare will manage DNS for your domain.
Plan Your Public Surface Area
Do not expose everything just because you can.
Reasonable candidates:
jellyfin.example.comimmich.example.comnavidrome.example.com
Bad candidates:
- Proxmox
- TrueNAS
- qBittorrent
- most raw admin dashboards
Keep core infrastructure private.
Why Use Caddy Here?
Because for many people it is the most painless route to:
- reverse proxying
- automatic HTTPS
- readable configuration
You can absolutely use NGINX or Traefik instead. Caddy is simply easier to recommend to most people for this stage.
Create the Caddy Stack
Create the directories:
mkdir -p ~/docker/appdata/caddy/{data,config}
mkdir -p ~/docker/compose/core
Create ~/docker/compose/core/caddy.compose.yml:
services:
caddy:
image: caddy:latest
container_name: caddy
ports:
- "80:80"
- "443:443"
volumes:
- /home/<your-user>/docker/appdata/caddy/Caddyfile:/etc/caddy/Caddyfile
- /home/<your-user>/docker/appdata/caddy/data:/data
- /home/<your-user>/docker/appdata/caddy/config:/config
restart: unless-stopped
networks:
- proxy
networks:
proxy:
external: true
Then create ~/docker/appdata/caddy/Caddyfile:
jellyfin.example.com {
reverse_proxy jellyfin:8096
}
navidrome.example.com {
reverse_proxy navidrome:4533
}
immich.example.com {
reverse_proxy immich-server:2283
}
Adjust the service names and ports to match your actual containers.
Start Caddy:
docker compose -f ~/docker/compose/core/caddy.compose.yml up -d
Router and DNS Requirements
For standard public HTTPS access with automatic certificates:
- forward TCP
80to the NixOS VM - forward TCP
443to the NixOS VM - create DNS records in Cloudflare pointing the relevant subdomains at your public IP
If your home IP changes periodically, you may also want dynamic DNS.
A Few Rules Worth Following
- expose only the services that benefit from it
- keep admin UIs private
- use strong authentication on anything public
- prefer a small public surface over a big clever one
If you are unsure whether something should be public, the safer answer is usually “no”.
Cloudflare Is Not a Magic Security Blanket
Cloudflare can absolutely be useful, but adding Cloudflare does not automatically make a badly exposed service safe.
You still need to think about:
- authentication
- updates
- backups
- what the service actually does if compromised
Do not outsource your judgment to a CDN.
Next Steps
Next, we will tie the whole system together with a backup strategy that covers more than just “the disks did not fail today”.
Proceed to Backups.
Last updated: March 2026