Ir al contenido
  1. Posts/

Cloudflare Tunnels: expón tu homelab sin abrir un solo puerto

Durante años viví con el router abierto como una casa. Puerto 80, 443, varios más. El típico setup de “expongo Nginx Proxy Manager y que Cloudflare me gestione los certificados”. Funcionaba, pero siempre con la incomodidad de saber que mi IP doméstica estaba ahí fuera, que si el ISP me cambiaba la IP había que actualizar DNS, y que cualquier vulnerabilidad en cualquier servicio era directamente accesible desde internet.

Cloudflare Tunnels resolvió todo eso de golpe. Sin tocar el router, sin abrir puertos, sin revelar la IP de casa. Un tunnel que sale desde dentro de mi red hacia Cloudflare, y Cloudflare gestiona la entrada.

Cómo funciona, en plan sencillo
#

En el modelo tradicional, el tráfico va así:

1
Usuario → DNS → Tu IP pública → Router → Puerto abierto → Tu servidor

Con Cloudflare Tunnels:

1
Usuario → DNS → Cloudflare → Tunnel → cloudflared → Tu servidor

La diferencia clave: en el segundo caso, la conexión la inicia tu servidor hacia Cloudflare. No hay ningún puerto abierto en tu router. Tu IP no aparece en ningún sitio. Si alguien ataca tu dominio, golpea los servidores de Cloudflare, no los tuyos.

El agente que hace eso posible se llama cloudflared. Es un proceso ligero que corre en tu red, mantiene una conexión persistente con Cloudflare, y reenvía el tráfico hacia los servicios internos que configures.

Requisitos previos
#

Necesitas:

  1. Una cuenta de Cloudflare (gratis)
  2. Un dominio gestionado por Cloudflare (lo mueves a Cloudflare si no está ahí ya)
  3. Docker instalado en el servidor donde quieras correr cloudflared
  4. Los servicios que quieras exponer funcionando en tu red local

No necesitas IP fija. No necesitas tocar el router. No necesitas nada de tu ISP.

Crear el tunnel desde la interfaz web
#

La forma más sencilla de empezar es desde el panel de Cloudflare Zero Trust.

  1. Ve a one.dash.cloudflare.com
  2. En el menú lateral: Networks > Tunnels
  3. Pulsa Create a Tunnel
  4. Elige Cloudflared como conector
  5. Dale un nombre (por ejemplo: homelab-tunnel)
  6. Cloudflare te muestra un token. Guárdalo, lo necesitas después

La parte importante viene cuando defines las rutas (routes). Cada ruta mapea un nombre de dominio a un servicio interno:

  • fotos.tu-dominio.comhttp://192.168.1.50:2283 (Immich)
  • libros.tu-dominio.comhttp://192.168.1.50:8083 (Kavita)
  • monitor.tu-dominio.comhttp://192.168.1.50:3001 (Uptime Kuma)

Cloudflare crea automáticamente los registros DNS de tipo CNAME apuntando a tu tunnel. No tienes que tocar nada en DNS tú mismo.

Instalar cloudflared con Docker
#

Con el token que te dio Cloudflare, levanta el agente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    restart: unless-stopped
    command: tunnel --no-autoupdate run
    environment:
      - TUNNEL_TOKEN=tu-token-aqui
    networks:
      - homelab

networks:
  homelab:
    external: true

Si tus servicios están en la misma red Docker, puedes usar nombres de container en lugar de IPs:

  • http://immich_server:3001
  • http://uptime-kuma:3001

Eso es más limpio porque sobrevive a cambios de IP.

Levanta el container:

1
2
docker compose up -d
docker logs cloudflared

Si el token está bien, verás en los logs que el tunnel se conecta a Cloudflare y queda en estado healthy. En el panel de Cloudflare también aparece el conector como activo.

A partir de ahí, cualquier petición a los dominios que configuraste pasa por Cloudflare, baja por el tunnel, y llega a tu servicio. Sin abrir ningún puerto en el router.

Configuración alternativa con YAML
#

Para homelabs con muchos servicios, la configuración desde la UI puede volverse tediosa. Hay una alternativa: un archivo de configuración YAML que define todas las rutas.

Crea config.yml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
tunnel: tu-tunnel-id
credentials-file: /etc/cloudflared/credentials.json

ingress:
  - hostname: fotos.tu-dominio.com
    service: http://192.168.1.50:2283
  - hostname: libros.tu-dominio.com
    service: http://192.168.1.50:8083
  - hostname: monitor.tu-dominio.com
    service: http://192.168.1.50:3001
  - service: http_status:404

La última línea es el “catch-all”: cualquier petición que no coincida con ninguna hostname devuelve 404. Importante incluirla.

Con este método el Docker Compose cambia un poco:

1
2
3
4
5
6
7
8
services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    restart: unless-stopped
    command: tunnel --config /etc/cloudflared/config.yml run
    volumes:
      - ./config.yml:/etc/cloudflared/config.yml
      - ./credentials.json:/etc/cloudflared/credentials.json

El archivo credentials.json lo obtienes ejecutando cloudflared tunnel login y autenticando en tu cuenta de Cloudflare. Después cloudflared tunnel create nombre-del-tunnel genera el JSON con las credenciales.

Acceso con autenticación adicional
#

Una de las cosas que más me gusta de Cloudflare Tunnels es que puedes añadir autenticación delante de cualquier servicio, aunque el servicio en sí no la tenga.

Esto se hace con Cloudflare Access, dentro de Zero Trust. Puedes proteger un hostname con:

  • Email OTP (te mandan un código al email)
  • Proveedores OAuth (Google, GitHub, etc.)
  • Certificados de cliente

Yo tengo algunos servicios internos de administración protegidos con Access usando mi email de Google. Antes de llegar al servicio, Cloudflare te pide que te autentiques con Google y verifica que eres yo. El servicio no sabe nada de eso.

Para configurarlo: en Zero Trust > Access > Applications, crea una aplicación del tipo “Self-hosted”, pon el hostname que quieres proteger y define la política de acceso.

HTTP vs HTTPS internamente
#

Cloudflare gestiona el TLS entre el usuario y Cloudflare. Lo que va por el tunnel puede ser HTTP plano sin problema. Cloudflare se encarga del certificado.

Esto simplifica mucho la configuración interna. No necesitas certificados en cada servicio. No necesitas Let’s Encrypt en tu red. Cloudflare presenta un certificado válido al usuario y la comunicación interna es HTTP.

Si quieres más seguridad (TLS de extremo a extremo), puedes configurar el modo “Full (strict)” en los ajustes SSL de Cloudflare para ese dominio, pero entonces sí necesitas certificados válidos en tus servicios internos.

Para la mayoría de homelabs, HTTP interno más HTTPS de Cloudflare hacia afuera es suficiente.

Rendimiento y latencia
#

Aquí hay que ser honesto: Cloudflare Tunnels añade latencia. El tráfico hace una ruta extra pasando por los servidores de Cloudflare. En mi caso, acceder a servicios desde dentro de casa pasando por el tunnel añade unos 20-40ms frente al acceso directo por IP local.

Para servicios web normales no se nota nada. Para algo con requisitos de baja latencia (como una consola de gaming o streams de vídeo en alta calidad), quizás no es la mejor opción.

Para acceso desde dentro de la red local, yo suelo usar directamente la IP o un DNS local. El tunnel lo uso principalmente para acceso externo.

Límites del plan gratuito
#

Con la cuenta gratuita de Cloudflare tienes:

  • Tunnels sin límite
  • Sin límite de ancho de banda (dentro del uso razonable)
  • Cloudflare Access para autenticación: gratis para hasta 50 usuarios

Lo que cuesta en planes de pago son funciones avanzadas de Zero Trust como integración con directorio corporativo, políticas más complejas, o analytics detallados. Para un homelab personal, el plan gratuito cubre todo.

Un límite que hay que conocer: Cloudflare no permite streaming de media o grandes transferencias de archivos a través de tunnels de forma oficial. Si intentas montar un servidor de Plex o Jellyfin para streaming a través de tunnel, técnicamente viola los términos de servicio. Para streaming uso acceso directo por VPN.

Troubleshooting más común
#

El tunnel aparece como offline en el panel: Comprueba los logs del container. Lo más frecuente es que el token esté mal copiado o que haya problemas de conectividad desde el servidor hacia internet.

1
docker logs cloudflared --tail 50

El servicio es accesible pero muy lento: Revisa si el servicio interno está respondiendo rápido cuando accedes directamente por IP. El tunnel no añade más de 50-100ms en condiciones normales. Si tarda segundos, el problema está en el servicio.

Error 502 Bad Gateway: Cloudflare llega al tunnel pero el servicio interno no responde. Comprueba que el servicio está corriendo y que la IP/puerto en la configuración son correctos.

WebSockets no funcionan: Algunos servicios usan WebSockets (Home Assistant, por ejemplo). En la configuración del hostname en Cloudflare, activa la opción “WebSocket” en las reglas de red o en la configuración del servicio dentro del tunnel.

Comparando con Tailscale
#

Uso tanto Cloudflare Tunnels como Tailscale, para cosas distintas.

Tailscale es para acceso privado desde dispositivos míos. Cuando quiero acceder al homelab desde el portátil o el móvil, Tailscale me conecta directamente a la red de casa. Es rápido, sin latencia extra, y el tráfico no pasa por servidores externos.

Cloudflare Tunnels es para exponer servicios públicamente: cosas que comparto con otras personas que no tienen Tailscale, o servicios web que quiero que sean accesibles desde cualquier navegador sin instalar nada.

Los dos se complementan bien. No son competencia entre sí.

Mi configuración actual
#

Tengo un container de cloudflared corriendo en un servidor que está siempre encendido. Expone varios servicios:

  • Gestor de fotos accesible para la familia
  • Panel de monitorización para ver el estado del homelab
  • Aplicación de recetas compartida con mi pareja
  • Una instancia de Gitea para proyectos personales

Cada uno tiene su subdominio, y los más sensibles tienen Cloudflare Access activado. Todo sin abrir un solo puerto en el router, sin que mi IP de casa aparezca en ningún sitio.

Tardé una tarde en montarlo todo y desde entonces no he tenido que tocar nada en meses. Eso es exactamente lo que busco en infraestructura: que funcione sin que tengas que pensar en ella.

Para empezar
#

  1. Crea cuenta en Cloudflare y mueve tu dominio (si no está ahí)
  2. Ve a Zero Trust > Networks > Tunnels
  3. Crea un tunnel, copia el token
  4. Levanta cloudflared con Docker usando el token
  5. Define las rutas hacia tus servicios internos

La documentación oficial de Cloudflare para tunnels es buena y está actualizada. Si algo no te queda claro, el Discord de Cloudflare tiene una comunidad activa.

Si tienes preguntas sobre algún caso concreto de configuración, deja un comentario.