You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Internal services are currently reached as name:port — monitoring.homelab.chrison.internal:9091 for Prometheus, :3000 for Grafana, :9090 for Cockpit, :7655 for Pulse. That works, but the ports are load-bearing trivia: they collide (Cockpit and Prometheus both wanted 9090, #283 phase 3), they change, and every change means finding whatever hard-coded them.
We already run the thing that solves this — Pangolin. pulse.lab.chrison.dev is exactly the shape we want: one hostname, no port, proxied to host:7655. This issue is to do the same for internal-only services.
Important distinction, so the design starts honest: Pangolin is a reverse proxy, not a DNS server. It cannot serve *.homelab.chrison.internal — that resolution comes from UniFi's local DNS. What Pangolin provides is the name → service routing once the name resolves. So an internal service needs two things, and today they're maintained in different places by hand:
a UniFi local-DNS record pointing the name at the proxy, and
a Pangolin resource routing that hostname to target host:port.
Usage Example
Declare both from one place in the shape, so a service gets a portless internal name in a single edit:
# stacks/Core/pangolin.lxc.yamlresources:
- name: Prometheussubdomain: prometheuszone: internal # → prometheus.homelab.chrison.internalinternalOnly: true # no Cloudflare record, no public exposuretarget: { ip: monitoring.homelab.chrison.internal, port: 9091, method: http }
Converge would then reconcile both sides: the Pangolin resource and a UniFi static-DNS A record for prometheus.homelab.chrison.internal → the Pangolin host. UniFi's standalone static-DNS API supports this — verified against the controller at /proxy/network/v2/api/site/default/static-dns, which is also how smarthome.homelab.chrison.internal was added.
Candidates once the mechanism exists: Grafana, Prometheus, Pulse (internal path), and Cockpit on each of the three podman hosts.
Points to settle in design
Internal zone naming.*.homelab.chrison.internal is the established internal namespace and deliberately separate from the public *.lab.chrison.dev / *.arr.chrison.dev (which are real Cloudflare records fronting private infra). An internalOnly resource must create no Cloudflare record.
TLS. Public resources get Let's Encrypt via DNS-01. .internal is not a real TLD, so LE can't issue for it — internal resources are either plain HTTP on the LAN or need an internal CA. Simplest first cut is HTTP.
Cockpit is per-host, so it needs three names (cockpit-monitoring, cockpit-media, cockpit-smarthome) rather than one.
SRV records are the only DNS type that carries a port, and UniFi does accept them (tested) — but browsers and curl ignore SRV, so they don't help here. Mentioned only to close off the obvious alternative.
Alternative
Keep using name:port. It costs nothing to run and every hostname already resolves. The downsides are the ones #283 phase 3 just hit: ports collide and get renumbered, and each renumbering is a hunt for whatever referenced the old one.
Description
Internal services are currently reached as
name:port—monitoring.homelab.chrison.internal:9091for Prometheus,:3000for Grafana,:9090for Cockpit,:7655for Pulse. That works, but the ports are load-bearing trivia: they collide (Cockpit and Prometheus both wanted 9090, #283 phase 3), they change, and every change means finding whatever hard-coded them.We already run the thing that solves this — Pangolin.
pulse.lab.chrison.devis exactly the shape we want: one hostname, no port, proxied tohost:7655. This issue is to do the same for internal-only services.Important distinction, so the design starts honest: Pangolin is a reverse proxy, not a DNS server. It cannot serve
*.homelab.chrison.internal— that resolution comes from UniFi's local DNS. What Pangolin provides is the name → service routing once the name resolves. So an internal service needs two things, and today they're maintained in different places by hand:target host:port.Usage Example
Declare both from one place in the shape, so a service gets a portless internal name in a single edit:
Converge would then reconcile both sides: the Pangolin resource and a UniFi static-DNS A record for
prometheus.homelab.chrison.internal→ the Pangolin host. UniFi's standalone static-DNS API supports this — verified against the controller at/proxy/network/v2/api/site/default/static-dns, which is also howsmarthome.homelab.chrison.internalwas added.Candidates once the mechanism exists: Grafana, Prometheus, Pulse (internal path), and Cockpit on each of the three podman hosts.
Points to settle in design
*.homelab.chrison.internalis the established internal namespace and deliberately separate from the public*.lab.chrison.dev/*.arr.chrison.dev(which are real Cloudflare records fronting private infra). AninternalOnlyresource must create no Cloudflare record..internalis not a real TLD, so LE can't issue for it — internal resources are either plain HTTP on the LAN or need an internal CA. Simplest first cut is HTTP.cockpit-monitoring,cockpit-media,cockpit-smarthome) rather than one.Alternative
Keep using
name:port. It costs nothing to run and every hostname already resolves. The downsides are the ones #283 phase 3 just hit: ports collide and get renumbered, and each renumbering is a hunt for whatever referenced the old one.