··
Después de meter los paneles admin del VPS detrás de Cloudflare Tunnel y Access, toca el otro lado de la red, el home lab. Home Assistant, el NAS, las cámaras y la Raspberry no quiero que pasen por la edge de Cloudflare cada vez que abro la app desde el móvil. Pangolin es la pieza self-hosted que hace lo mismo (cero puerto abierto, identidad antes del primer byte) pero con todo el camino de los datos en mi propia infraestructura.

Acabo de cerrar la migración de los paneles administrativos del VPS detrás de Cloudflare Tunnel y Access y la cabeza se me va al otro lado de la red, el home lab. Tengo Home Assistant orquestando luces, persianas y termostato, el NAS de Synology, los paneles de las cámaras, una Raspberry con Pi-hole y un par de servicios más que solo uso yo (y a veces mi familia). Hoy todo eso vive en la LAN y se accede vía VPN al router cuando estoy fuera. Funciona, pero la VPN al router es la pieza más rígida del setup y tengo claro que el modelo del VPS (cero puerto abierto, identidad antes del primer byte) es el que quiero también en casa.
La continuación lógica sería montar otro Cloudflare Tunnel para el home lab. Lo descarté después de pensarlo un par de tardes y la entrega de hoy es por qué, qué pieza pongo en su lugar y cómo encaja con lo que ya tengo en el VPS.
Para los paneles admin del VPS, Cloudflare es una elección obvia. La zona ya estaba en CF, las apps que protejo son herramientas de trabajo, y el tráfico que pasa por ahí es el de un único usuario, yo. Pero el home lab tiene otra naturaleza.
La pieza que voy a usar es Pangolin, un proyecto open source que hace prácticamente lo mismo que Cloudflare Tunnel + Access, pero con todo el camino del dato dentro de máquinas que controlo yo.
Pangolin no es una sola cosa, es un stack. La gracia es entender qué pinta cada pieza antes de tocar el compose, porque luego todo encaja muy rápido.
cloudflared en el modelo de Cloudflare.El modelo mental es exactamente el mismo que ya tengo claro con Cloudflare. El home lab nunca tiene un puerto abierto. Newt establece una sesión saliente a Gerbil sobre WireGuard. Pangolin/Traefik aceptan el tráfico HTTPS público en su VPS, lo enrutan al sitio correcto a través del túnel, y la respuesta vuelve por la misma vía. La diferencia es que Pangolin lo corro yo en una VPS pequeña que controlo, y Newt lo pongo dentro de la red de casa.
Pangolin tiene dos abstracciones que conviene interiorizar antes de empezar a clicar.
Un site es una ubicación remota desde la que sale un Newt. En mi caso voy a tener un único site, "casa", con un solo Newt. Si en el futuro monto un Newt en casa de mis padres para vigilar su NAS, eso será otro site. La separación es útil porque permite reutilizar nombres internos. 192.168.1.10 en mi LAN no es 192.168.1.10 en la suya, pero el dashboard sabe a qué site pertenece cada recurso.
Un resource es lo que de verdad publicas, la pareja de "subdominio público" más "destino interno". Cada uno tiene su política de acceso, su SSL, sus reglas, sus headers, sus targets. Es el equivalente a una Application de Cloudflare Access, con la diferencia de que aquí el routing y la auth viven en la misma pieza.
Para el home lab voy a empezar con cinco recursos, uno por panel.
casa.midominio.com, hacia Home Assistant en http://homeassistant.local:8123.nas.midominio.com, hacia el panel de DSM del Synology.camaras.midominio.com, hacia Frigate (el NVR que tengo encima de las cámaras).pi.midominio.com, hacia el panel de Pi-hole en la Raspberry.printer.midominio.com, hacia el panel de la impresora 3D, que aún uso poco pero no me apetece sacar a Internet con basic auth.El subdominio público lo gestiono en mi proveedor de DNS apuntando a la IP de la VPS donde corre Pangolin. Pangolin pide los certificados Let's Encrypt automáticamente vía Traefik, así que cada panel queda con HTTPS sin mover un dedo después.
El despliegue tiene dos lados.
Por un lado, una VPS pequeña con IP pública donde corre el stack de Pangolin. Sirve solo para esto, así no se mezcla con el VPS de trabajo (donde viven el blog, Dokploy y compañía). Una instancia mínima de cualquier proveedor barato sobra, los recursos van en el túnel, no en la VPS.
El compose de Pangolin sigue el patrón que da la documentación oficial, tres servicios coordinados.
services:
pangolin:
image: fosrl/pangolin:1.x.y
container_name: pangolin
restart: unless-stopped
volumes:
- ./config:/app/config
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: 3s
timeout: 3s
retries: 15
gerbil:
image: fosrl/gerbil:1.x.y
container_name: gerbil
restart: unless-stopped
depends_on:
pangolin:
condition: service_healthy
command:
- --reachableAt=http://gerbil:3004
- --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/
volumes:
- ./config/:/var/config
cap_add:
- NET_ADMIN
- SYS_MODULE
ports:
- 51820:51820/udp
- 21820:21820/udp
- 443:443
- 80:80
traefik:
image: traefik:v3.4.0
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil
depends_on:
pangolin:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
volumes:
- ./config/traefik:/etc/traefik:ro
- ./config/letsencrypt:/letsencrypt
- ./config/traefik/logs:/var/log/traefik
networks:
default:
driver: bridge
name: pangolin
El detalle que se me escapaba al principio es que Traefik usa network_mode: service:gerbil. Es decir, no es un servicio con sus propios puertos, comparte el espacio de red de Gerbil. Por eso los 80 y 443 aparecen en Gerbil, no en Traefik. Es la única forma de que el tráfico HTTPS público y el tráfico WireGuard convivan limpiamente, sin iptables retorcidos.
Las imágenes van fijadas a versión, nunca latest. Es la misma regla que aplico a cloudflared en el otro lado, la actualización es decisión mía con un PR consciente, no algo que pase a las 03:00 sin que me entere.
Por el otro lado, en casa corre Newt dentro de un docker compose dedicado en una de las máquinas de la LAN (la Raspberry sirve perfectamente para esto, no consume nada).
services:
newt:
image: fosrl/newt:1.x.y
container_name: newt
restart: unless-stopped
environment:
- PANGOLIN_ENDPOINT=https://pangolin.midominio.com
- NEWT_ID=${NEWT_ID}
- NEWT_SECRET=${NEWT_SECRET}
NEWT_ID y NEWT_SECRET los genera Pangolin al dar de alta el site en su dashboard. La parte sensible la inyecto desde mi gestor de secretos (en este blog ya conté cómo metí Infisical delante de los proyectos, y aplico el mismo patrón aquí). Si quiero ser más estricto, Pangolin admite pasar la configuración como Compose Secret en lugar de variables, lo cual está bien para una Raspberry expuesta físicamente en la cocina.
Newt no necesita correr en la misma máquina que el servicio destino. Una vez la sesión está abierta, cualquier IP o nombre alcanzable desde la LAN del Newt es alcanzable como target. Para los paneles HTTP eso es transparente, la cámara escucha en su IP de LAN y el resource del dashboard apunta a esa IP. Solo cuando algún servicio internamente corre en localhost del propio Newt me toca pensar dónde lo coloco.
Aquí Pangolin saca la diferencia más bonita respecto a Cloudflare. Access en CF es una pieza pegada al tunnel pero conceptualmente separada, con su propio dashboard, sus Applications evaluadas en orden y sus Bypass. En Pangolin la auth es por recurso y se configura en el mismo formulario donde defines el destino.
Cada resource tiene un bloque auth. La política puede ser de varias clases.
client_id, client_secret, authorization_url, token_url). En el plan futuro me planteo montar Authentik como IdP único de la casa, pero para arrancar Pangolin nativo me sobra.groups contiene "home-lab", vía contains(groups, 'home-lab'). Para un setup familiar es justo lo que quiero, una regla "todo dios con email del dominio puede entrar al panel del NAS para ver fotos" y otra "solo yo entro a Home Assistant".El equivalente a "Bypass" de Cloudflare en Pangolin es simplemente quitar la auth de un recurso (o, si es un endpoint concreto, declarar un recurso aparte para esa ruta sin auth). Para el home lab no preveo ninguno, todos los paneles son privados. Pero si en el futuro quiero exponer un webhook público de Home Assistant para una integración externa, el patrón es el mismo que aprendí con Access, recurso específico antes que recurso general.
Lo dibujo como tabla mental, porque me ha costado decidir cuál uso para qué.
Mi conclusión es que la respuesta no es "uno mata al otro", es repartir. Trabajo y paneles administrativos del VPS detrás de Cloudflare. Home lab y traffic personal detrás de Pangolin. Cada uno donde tiene más sentido.
Si te interesa el cuadro completo (incluyendo cuándo SSH tunnel sigue siendo la mejor opción, qué pinta Tailscale en todo esto y cuándo conviene Headscale), está en SSH vs Cloudflare Tunnel vs Pangolin vs Tailscale vs Headscale vs WireGuard, qué uso para qué.
Una pieza que en CF no me preocupa porque la lleva CF, pero en Pangolin sí. La VPS donde corre el stack tiene un volumen ./config con la base de datos, las claves de WireGuard de Gerbil y los certificados Let's Encrypt cacheados. Si esa VPS se cae sin backup, perder los certificados no es grave (Let's Encrypt los emite de nuevo), pero perder la base de datos sí lo es, ahí están los users, los recursos y el estado del site.
El plan es restic apuntando a un bucket externo (no al mismo NAS que estoy exponiendo, evidentemente) con snapshot diario y retención de 30 días. Es el mismo backup script que ya uso en el VPS de trabajo, así que solo cambia la lista de paths.
El segundo riesgo, y este sí es propio del modelo, es que si Newt en casa se cae sin que yo me entere, los paneles dejan de responder por fuera. La VPS sigue respondiendo, pero con un 502. Lo cubro con un check externo (uptime-kuma corriendo en otro sitio) que avisa por ntfy si el dominio responde 502 más de cinco minutos seguidos. Si la Raspberry donde corre Newt se queda colgada, recibo el push antes de darme cuenta porque me ha fallado abrir Home Assistant.
El home lab funciona ahora mismo, no quiero romperlo en una tarde. La idea es cortar piso a piso, igual que hice con los paneles del VPS.
El rollback en cualquier paso es inmediato, basta con apagar el contenedor de Newt en casa y volver a abrir la VPN al router. Como Pangolin no toca la red interna (no cambia rutas, no cambia DNS local), su caída no afecta al uso desde la LAN.
El siguiente fin de semana decente con tiempo me toca la fase 1, levantar la VPS y dejar el flujo verificado con un Newt de prueba. Cuando tenga el primer panel real corriendo escribiré la entrega de "Pangolin en producción casera, lo que cambió respecto al plan", que es donde de verdad se aprende, en la diferencia entre el README y la realidad.
Hay un par de piezas que no cubro hoy porque las quiero ver con datos. Una es exponer protocolos no HTTP (RTSP de las cámaras hacia un visor que no esté en la LAN, SSH a la Raspberry desde el móvil), porque ahí Pangolin compite con Tailscale en su propio terreno y la decisión depende de cómo se sienta el día a día. La otra es Authentik como IdP único de toda la casa, que tiene sentido cuando empiezo a tener más de cinco recursos y empiezo a invitar a familiares a algunos paneles (las fotos del NAS, por ejemplo). Las dos quedan para su propio post.

Jose, autor del blog
QA Engineer. Escribo en voz alta sobre automatización, IA y arquitectura de software. Si algo te ha servido, escríbeme y cuéntamelo.
¿Qué te ha parecido? ¿Qué añadirías? Cada comentario afina la siguiente entrada.
Si esto te ha gustado

ScamDetector combina inteligencia artificial, búsqueda de reputación de teléfonos y escaneo de URLs para ayudarte a identificar estafas digitales. Sin registro, sin datos almacenados.

Repaso completo de las medidas de seguridad que puedes aplicar a un VPS Linux: desde CrowdSec y el firewall hasta el hardening del kernel, pasando por SSH, Docker y las actualizaciones automáticas.

Nuestros posts viven en una base de datos SQLite. Si alguien accede a ella, puede cambiar cualquier artículo sin dejar rastro. Construimos un verificador externo con hashes SHA-256 y firma Ed25519 que vigila la integridad desde un segundo servidor.