Adding a new service¶
Cómo exponer un servicio Docker nuevo bajo *.monxas.casa con Caddy + CF Tunnel.
La fuente única de configuración es homelab-ctl.py (en scripts/) que lee
labels Docker, genera managed.caddy y reconfigura el ingress de CF Tunnel.
Tres archivos tocan, uno comando reconcilia
docker-compose.ymldel stack — labels del container.managed.caddygenerado — no editar a mano.- CF Tunnel ingress — no editar a mano.
Solo tocas el (1). El (2) y (3) los regenera homelab-ctl.py sync.
Workflow¶
1. Añadir labels al container¶
En el docker-compose.yml del stack en VM 208:
yaml
services:
newservice:
image: example/newservice:latest
ports:
- "12345:8080" # host_port:container_port
labels:
- tunnel.hostname=newservice.monxas.casa
- tunnel.port=12345
# Opcional: SSO PocketID (F5)
- tunnel.auth=pocketid
# Opcional: Sablier lazy (apaga tras 15min idle)
- tunnel.sablier=true
- tunnel.sablier.timeout=15m
Convenciones:
| Label | Valor | Default |
|---|---|---|
tunnel.hostname |
FQDN bajo monxas.casa |
requerido |
tunnel.port |
Puerto en el host VM 208 | requerido |
tunnel.auth |
none | cfaccess | pocketid |
cfaccess |
tunnel.sablier |
true | false |
false |
tunnel.lan_only |
true excluye CF Tunnel ingress |
false |
2. Aplicar labels al runtime¶
Labels solo afectan al container nuevo, así que hay que recrearlo:
bash
ssh [email protected] 'cd ~/stacks/<stack> && docker compose up -d newservice'
3. Reconciliar Caddy + CF Tunnel¶
Desde la Mac (o el host VM 208 directamente):
bash
python3 ~/scripts/homelab-ctl.py sync --yes
Esto:
- Lee labels de TODOS los containers en VM 208.
- Genera
/etc/caddy/managed.caddy(rsync a LXC 270 y 271 post-F4). - Reconfigura ingress de CF Tunnel (
config.yamlen LXC 123) y lo aplica. - Hace
caddy reloaden ambos Caddy HA.
4. Verificar DNS¶
CF tiene un wildcard *.monxas.casa desde 2026-05-20, así que no hace falta
crear un registro DNS por servicio nuevo. Verifica que resuelve:
```bash dig +short newservice.monxas.casa
Debería devolver una IP de CF (1xx.xxx.xxx.xxx o 1xx anycast).¶
```
Internamente (LAN), Pi-hole intercepta con address=/monxas.casa/192.168.0.XXX
(o .208 pre-F4) y devuelve la IP local.
5. Caso especial: Sablier activo¶
Si Sablier estaba activo en Caddy cuando añades el servicio, un caddy reload
puede no aplicar el cambio del plugin. Workaround:
bash
ssh pmx-50 'pct exec 270 -- systemctl restart caddy'
ssh pmx-51 'pct exec 271 -- systemctl restart caddy'
(Restart, no reload). Esto causa una ventana de 2-3s sin servicio HTTP externo — hacerlo fuera de horario punta si posible.
6. Test¶
Desde dentro del cluster (bypass de DNS):
bash
curl --resolve newservice.monxas.casa:443:192.168.0.XXX -fsSv https://newservice.monxas.casa/
Desde fuera:
bash
curl -fsSv https://newservice.monxas.casa/
Si pides auth: comprueba que tunnel.auth esté bien (cfaccess = login Cloudflare,
pocketid = login PocketID forward-auth, none = abierto).
Casos especiales¶
Servicio NO en VM 208¶
Si el backend vive en otro container o nodo (e.g. n8n en LXC 200):
yaml
labels:
- tunnel.hostname=n8n.monxas.casa
- tunnel.upstream=http://192.168.0.XXX:5678
# No usar tunnel.port (asume VM 208 :port)
Servicio LAN-only¶
yaml
labels:
- tunnel.hostname=internal-tool.monxas.casa
- tunnel.port=9999
- tunnel.lan_only=true
homelab-ctl.py configura Caddy para servirlo pero no añade ingress en CF
Tunnel. Accesible solo desde LAN o WireGuard.
Servicio con WebSockets / SSE¶
Caddy maneja WS por defecto. Si hay timeouts SSE largos:
yaml
labels:
- tunnel.hostname=foo.monxas.casa
- tunnel.port=1234
- tunnel.timeout=300s
Troubleshooting¶
| Síntoma | Fix |
|---|---|
curl: ... 502 Bad Gateway |
Backend container muerto. docker ps | grep <service> |
curl: ... 404 desde Caddy |
Label tunnel.hostname typo o homelab-ctl.py sync no corrió |
curl: ... Could not resolve externo |
CF wildcard inactivo o DNS propagation. Espera 1min |
curl: ... SSL_ERROR_SYSCALL interno |
Pi-hole devuelve IP vieja (cache). pihole -f para flush DNS |
| Login PocketID falla | tunnel.auth=pocketid pero PocketID no tiene el client. Ver Creating OIDC client |