Homelab connections map
Which quadlet talks to which, over which protocol and port, and whether the traffic stays in a pod, on the server, on your LAN, goes through WireGuard or out to the internet.
In short: the internet reaches your server only through the router's port
forwards to Caddy (HTTPS 443) and WireGuard (51820/udp). Caddy hands each name to one app on
the same server, apps in one pod talk over localhost, the LAN finds everything through AdGuard
Home's DNS, and a few apps call out to the internet on their own. Addresses are placeholders:
the server is 10.0.0.10 and the domain example.com.
The map
- Inside a pod: localhost between containers of one podman pod; nothing is published.
- On the server: a container reaching a port published on the same server, through host.containers.internal or the host network.
- Home network: across the local subnet 10.0.0.0/24: port forwards, DNS, NAS mounts, device traffic.
- Internet: to or from the public internet, through the router.
- WireGuard tunnel: remote devices reaching home services through the VPN on 10.8.0.0/24.
Arrows point at the side that receives the connection. Hover or focus a station to highlight its lines; select it to open its example. Scroll sideways to see the whole map.
How the traffic flows
From the internet to an app
A browser outside resolves app.example.com to your public IP. The router forwards
80 and 443 to the server, where Caddy terminates TLS and proxies the request to the app's
published port through host.containers.internal. Only three ports are forwarded;
every app port stays closed to the internet.
At home: one name, answered locally
The router's DHCP gives every client AdGuard Home as DNS server. A DNS rewrite answers
*.example.com with 10.0.0.10, so the same URL works at home and
outside, but home traffic goes straight to Caddy without leaving the house.
Inside a pod: localhost
Containers in one .pod share a network namespace. Radarr reaches NZBGet at
localhost:6789 and the UniFi app reaches MongoDB at localhost:27017;
the database port is never published.
Remote access over WireGuard
Your laptop on the road opens a tunnel to 51820/udp and gets an address in
10.8.0.0/24. It then uses AdGuard for DNS and reaches LAN-only services through
Caddy, as if it were at home.
Out to the internet
Some apps call out by themselves: NZBGet to the Usenet provider (NNTPS 563), Prowlarr to the indexers, ntfy to its upstream for iPhone push, restic to off-site storage, Caddy to Let's Encrypt. These need no port forward; the router allows replies to connections that start inside.
Every connection
| From | To | Protocol and port | Line | Why |
|---|---|---|---|---|
| Visitors outside | Router | HTTPS 443/tcp | Internet | Public HTTPS to your domain; DNS for app.example.com points at your public IP. |
| Your laptop away | Router | WireGuard 51820/udp | Internet | The encrypted tunnel from outside; nothing else needs to be open for remote admin. |
| Router | Caddy | HTTPS 443/tcp | Home network | Port forward of 80/tcp and 443/tcp+udp to 10.0.0.10. |
| Router | WireGuard | WireGuard 51820/udp | Home network | Port forward of 51820/udp to 10.0.0.10. |
| Caddy | Let's Encrypt | HTTPS 443/tcp | Internet | ACME: Caddy requests and renews certificates; the HTTP-01 check comes back in on port 80. |
| LAN clients | AdGuard Home | DNS 53/udp | Home network | Every lookup at home; a rewrite answers *.example.com with 10.0.0.10, so LAN traffic never leaves the house. |
| LAN clients | Caddy | HTTPS 443/tcp | Home network | The same URLs as outside, answered locally thanks to the DNS rewrite. |
| AdGuard Home | Upstream DNS | DoH 443/tcp | Internet | Allowed queries go out encrypted as DNS-over-HTTPS. |
| WireGuard | AdGuard Home | DNS 53/udp | WireGuard tunnel | Peers set DNS = 10.0.0.10 in their config and get ad blocking on the road. |
| WireGuard | Caddy | HTTPS 443/tcp | WireGuard tunnel | Through the tunnel, LAN-only services are reachable from anywhere. |
| Caddy | whoami | HTTP 8080/tcp | On the server | reverse_proxy host.containers.internal:8080 |
| Caddy | changedetection.io | HTTP 5000/tcp | On the server | reverse_proxy host.containers.internal:5000 |
| Caddy | ntfy | HTTP 2586/tcp | On the server | reverse_proxy 10.0.0.10:2586; phones subscribe to topics through the proxy. |
| Caddy | Gatus | HTTP 8084/tcp | On the server | reverse_proxy 10.0.0.10:8084 for the status page. |
| Caddy | Jellyfin | HTTP 8096/tcp | On the server | reverse_proxy 10.0.0.10:8096; streaming with TLS handled by Caddy. |
| Caddy | Seerr | HTTP 5055/tcp | On the server | reverse_proxy 10.0.0.10:5055 for the request page. |
| Caddy | Homepage | HTTP 3010/tcp | On the server | reverse_proxy 10.0.0.10:3010; the start page, keep it LAN and VPN only. |
| Caddy | Lidarr | HTTP 8686/tcp | On the server | reverse_proxy 10.0.0.10:8686 |
| changedetection.io | Watched websites | HTTPS 443/tcp | Internet | Fetches each watched page on its schedule. |
| ntfy | ntfy.sh upstream | HTTPS 443/tcp | Internet | Only a message id leaves the server, not the message text. |
| Gatus | Caddy | HTTPS 443/tcp | On the server | Checks the public URLs end to end, certificate expiry included. |
| Gatus | AdGuard Home | DNS 53/udp | On the server | Checks that 10.0.0.10 answers jellyfin.example.com with 10.0.0.10. |
| Gatus | Jellyfin | HTTP 8096/tcp | On the server | Checks the backend directly at localhost:8096/health, bypassing Caddy. |
| Gatus | NAS | TCP 2049/tcp | Home network | Checks that the NAS accepts NFS connections. |
| Gatus | ntfy | HTTP 2586/tcp | On the server | Alerts to localhost:2586 when a check fails, and a health check of ntfy itself. |
| Homepage | Jellyfin | HTTP 8096/tcp | On the server | Widget data from the Jellyfin API at host.containers.internal. |
| Homepage | Radarr | HTTP 7878/tcp | On the server | Widget data from the Radarr API. |
| Homepage | Sonarr | HTTP 8989/tcp | On the server | Widget data from the Sonarr API. |
| Homepage | Gatus | HTTP 8084/tcp | On the server | Status widget from Gatus. |
| Homepage | ntfy | HTTP 2586/tcp | On the server | Reads the alert topic with a read-only ntfy user. |
| Seerr | Jellyfin | HTTP 8096/tcp | On the server | Sign-in and library sync. |
| Seerr | Radarr | HTTP 7878/tcp | On the server | Sends approved movie requests. |
| Seerr | Sonarr | HTTP 8989/tcp | On the server | Sends approved series requests. |
| Recyclarr | Radarr | HTTP 7878/tcp | On the server | Pushes quality profiles and custom formats. |
| Recyclarr | Sonarr | HTTP 8989/tcp | On the server | Pushes quality profiles and custom formats. |
| Recyclarr | TRaSH Guides | HTTPS 443/tcp | Internet | Downloads the current guide data. |
| NetAlertX | APs, switches, devices | ARP | Home network | Scans 10.0.0.0/24 for new devices. |
| restic | Off-site backup | HTTPS 443/tcp | Internet | offsite-push.timer uploads new repository files one way, an hour after the backup; only encrypted data leaves. |
| restic | NAS | NFS / SMB 2049/tcp, 445/tcp | Home network | Writes the encrypted repository to /mnt/backup, a NAS share, from read-only btrfs snapshots. |
| Jellyfin | NAS | NFS / SMB 2049/tcp, 445/tcp | Home network | Reads the library through the host's /srv/media mount. |
| Prowlarr | Radarr | HTTP 7878/tcp (both ways) | Inside a pod | Prowlarr syncs indexers to localhost:7878; Radarr searches through localhost:9696. |
| Prowlarr | Sonarr | HTTP 8989/tcp (both ways) | Inside a pod | Same indexer sync for series. |
| Radarr | NZBGet | HTTP 6789/tcp | Inside a pod | Hands NZBs to localhost:6789. |
| Sonarr | NZBGet | HTTP 6789/tcp | Inside a pod | Hands NZBs to localhost:6789. |
| Bazarr | Radarr | HTTP 7878/tcp | Inside a pod | Reads the movie list. |
| Bazarr | Sonarr | HTTP 8989/tcp | Inside a pod | Reads the series list. |
| Prowlarr | Lidarr | HTTP 8686/tcp (both ways) | On the server | Prowlarr syncs indexers to host.containers.internal:8686; Lidarr searches through host.containers.internal:9696. |
| Lidarr | NZBGet | HTTP 6789/tcp | On the server | Hands NZBs to host.containers.internal:6789. |
| Lidarr | NAS | NFS / SMB 2049/tcp, 445/tcp | Home network | Mounts the same /srv/media as /data, so imports stay instant. |
| Prowlarr | Usenet indexers | HTTPS 443/tcp | Internet | Searches through each indexer's API. |
| NZBGet | Usenet provider | NNTPS 563/tcp | Internet | Downloads over TLS. |
| Bazarr | Subtitle providers | HTTPS 443/tcp | Internet | Downloads subtitles. |
| media.pod | NAS | NFS / SMB 2049/tcp, 445/tcp | Home network | The host mounts the NAS at /srv/media; every app bind-mounts it as /data. |
| UniFi Network | MongoDB | MongoDB 27017/tcp | Inside a pod | localhost:27017, never published. |
| APs, switches, devices | UniFi Network | HTTP 8080/tcp | Home network | Access points and switches report to the inform URL. |
| APs, switches, devices | UniFi Network | STUN 3478/udp | Home network | Device status and remote actions. |
Services on the map
| Service | Listens on | What it does |
|---|---|---|
| media.pod | No port (client, pod or timer job) | Five download and library apps in one pod: they reach each other on localhost and share /srv/media as /data. Lidarr runs next to it as its own container. |
| unifi.pod | No port (client, pod or timer job) | The UniFi Network Application and its MongoDB in one pod; the database port is never published. |
| Visitors outside | No port (client, pod or timer job) Address 203.0.113.50 | Anyone who opens https://app.example.com from outside, your own phone on mobile data included. |
| Your laptop away | No port (client, pod or timer job) Address 10.8.0.2 | A WireGuard peer: it opens the tunnel, then uses home services as if it sat on the LAN. |
| Let's Encrypt | No port (client, pod or timer job) | The ACME certificate authority that issues and renews Caddy's TLS certificates. |
| Upstream DNS | No port (client, pod or timer job) | Public resolvers that AdGuard Home forwards allowed queries to, encrypted with DNS-over-HTTPS. |
| Watched websites | No port (client, pod or timer job) | The pages changedetection.io checks for changes. |
| ntfy.sh upstream | No port (client, pod or timer job) | Relays instant push to iPhones: a self-hosted ntfy server sends it a poll request per message (upstream-base-url). |
| Usenet indexers | No port (client, pod or timer job) | Search sites Prowlarr queries over their API. |
| Usenet provider | No port (client, pod or timer job) | The news servers NZBGet downloads from, over NNTP with TLS. |
| Subtitle providers | No port (client, pod or timer job) | Sites Bazarr downloads subtitles from. |
| TRaSH Guides | No port (client, pod or timer job) | Community quality settings on GitHub that Recyclarr downloads. |
| Off-site backup | No port (client, pod or timer job) | Cloud storage that holds a one-way copy of the encrypted restic repository; it never sees plain data. |
| Router | HTTP 80/tcp (forwarded), HTTPS 443/tcp+udp (forwarded), WireGuard 51820/udp (forwarded) Address 10.0.0.1 | Forwards only these ports to the server; everything else from the internet is dropped. |
| LAN clients | No port (client, pod or timer job) Address 10.0.0.0/24 | Laptops, phones and TVs at home. The router's DHCP hands them AdGuard Home as DNS server. |
| Caddy | HTTP 80/tcp, HTTPS 443/tcp, HTTP/3 443/udp Address 10.0.0.10 | Reverse proxy: terminates TLS for *.example.com and forwards each name to one app. |
| AdGuard Home | DNS 53/tcp+udp, HTTP 8081/tcp (admin), HTTP 3000/tcp (setup) Address 10.0.0.10 | DNS for the whole network: blocks ads and rewrites *.example.com to 10.0.0.10. |
| WireGuard | WireGuard 51820/udp Address 10.8.0.1 | VPN endpoint for remote access; peers get addresses in 10.8.0.0/24. |
| whoami | HTTP 8080/tcp | A tiny test page that proves the proxy chain works. It shares 8080 with UniFi: run one or move whoami. |
| changedetection.io | HTTP 5000/tcp | Watches web pages and sends an alert when they change. |
| ntfy | HTTP 2586/tcp (localhost and 10.0.0.10 only) | Push notifications: failed systemd units, podman auto-update and Gatus post alerts to it; phones subscribe through Caddy. |
| Gatus | HTTP 8084/tcp (host network) | Health checks and a status page on the host network; alerts through ntfy. |
| Jellyfin | HTTP 8096/tcp | Media server with Intel iGPU transcoding; reads the library read-only from /srv/media. |
| Seerr | HTTP 5055/tcp | Request page: approved requests go to Radarr and Sonarr. |
| Homepage | HTTP 3010/tcp | Start page with widgets that read each app's API; no container socket. |
| Recyclarr | No port (client, pod or timer job) | A timer job without a port: it pushes TRaSH quality settings into Radarr and Sonarr. |
| Lidarr | HTTP 8686/tcp | Finds and organises music; runs next to media.pod and reaches it through host.containers.internal. |
| NetAlertX | HTTP 20211/tcp (host network) | Scans the subnet for new devices; needs the host network for raw ARP. |
| restic | No port (client, pod or timer job) | Root timer jobs without a port: nightly encrypted backups of every podman volume to the NAS, then a one-way copy to the cloud. |
| Prowlarr | HTTP 9696/tcp | Indexer manager for the other apps. |
| Radarr | HTTP 7878/tcp | Finds and organises movies. |
| Sonarr | HTTP 8989/tcp | Finds and organises series. |
| Bazarr | HTTP 6767/tcp | Fetches subtitles for Radarr and Sonarr. |
| NZBGet | HTTP 6789/tcp | Usenet downloader. |
| UniFi Network | HTTPS 8443/tcp, HTTP 8080/tcp (inform), STUN 3478/udp, Discovery 10001/udp | Controller for UniFi access points and switches. |
| MongoDB | MongoDB 27017/tcp (pod only) | The controller's database, reachable only inside the pod. |
| NAS | NFS 2049/tcp, SMB 445/tcp Address 10.0.0.11 | Holds the media library; the server mounts it at /srv/media. |
| APs, switches, devices | No port (client, pod or timer job) Address 10.0.0.0/24 | UniFi hardware and every other device on the subnet. |