# Runbook: Cloudflare Tunnel (coolify-tunnel) Procedimiento para diagnosticar y corregir el túnel de Cloudflare que expone Coolify (`coolify.urieljareth.org`) y las apps (`*.urieljareth.org`). > **Dato clave:** el túnel está **gestionado desde el dashboard de Cloudflare** > (remotely-managed), no por el `config.yml` local. El archivo > `/etc/cloudflared/config.yml` dentro del contenedor solo contiene el UUID del > túnel, las credenciales y el comodín `*.urieljareth.org`. **Todas las rutas de > `coolify.urieljareth.org` (incluidas 6001/6002) viven en el dashboard / la API > de Cloudflare.** Por eso un cambio de rutas NO se arregla editando archivos en > el servidor — se arregla en el dashboard (rápido) o por la API (control total). > La firma de esto en los logs es la línea `INF Updated to new configuration version=N`. Síntomas típicos que llevan aquí: - En la terminal de Coolify: `Terminal websocket connection lost. Reconnecting...` - UI de Coolify inestable / reconexiones constantes / página en blanco al abrir una app - En logs de `cloudflared`: `tls: first record does not look like a TLS handshake` - Dominio de app con `502 Bad Gateway` o `530` Documentos relacionados: [ISSUE_cloudflare-tunnel_routing_websocket-tls-handshake.md](../ISSUE_cloudflare-tunnel_routing_websocket-tls-handshake.md) · [cloudflare-tunnel-coolify-agent_1.md](../cloudflare-tunnel-coolify-agent_1.md) · [issue-coolify-static-app-deploy.md](../issue-coolify-static-app-deploy.md) --- ## 1. Cargar entorno y verificar conexión ```powershell # Secretos privados (gitignored) — necesario solo para llamadas a API . .\.env.local.ps1 # Verifica config + SSH + Docker + API (sale 1 si algo falla) .\scripts\Test-ProxmoxConnection.ps1 ``` El SSH funciona con los defaults aunque no cargues `.env.local.ps1`. --- ## 2. Diagnóstico rápido (solo lectura) ### 2.1 Logs recientes del túnel ```powershell .\scripts\Invoke-ProxmoxSsh.ps1 -Command "pct exec 102 -- docker logs cloudflared --tail 40" ``` Buscar: - `Registered tunnel connection ... protocol=http2` (x4) → túnel arriba y sano - `tls: first record does not look like a TLS handshake` → ruta 6001/6002 en `https://` - `Lost connection with the edge` / `failed to dial to edge with quic` → ver nota de `--protocol http2` ### 2.2 Ver la configuración ACTIVA del túnel (lo que realmente aplica) ```powershell .\scripts\Invoke-ProxmoxSsh.ps1 -Command "pct exec 102 -- docker logs cloudflared --tail 60" | Select-String "Updated to new configuration" ``` Toma la línea con el `version=N` **más alto** (es la config vigente) y revisa el JSON `ingress`. **Los puertos 6001 y 6002 deben decir `http://`, nunca `https://`.** Cada vez que se guarda un cambio en el dashboard/API, `version` se incrementa. ### 2.3 Ver el `config.yml` local (opcional) La imagen `cloudflared` es **distroless** (no tiene `cat`/`sh` propios para leer el archivo directo), así que se copia con `docker cp` y se lee desde el LXC: ```powershell .\scripts\Invoke-ProxmoxSsh.ps1 -Command "pct exec 102 -- sh -c 'docker cp cloudflared:/etc/cloudflared/config.yml /tmp/cf-config.yml && cat /tmp/cf-config.yml; rm -f /tmp/cf-config.yml'" ``` Esperado: solo `tunnel:`, `credentials-file:` y el comodín. Si aquí NO aparecen las rutas de `coolify.urieljareth.org`, confirma que el túnel es dashboard-managed. > Nunca leas `credentials.json` ni lo imprimas — contiene el secreto del túnel. --- ## 3. Reglas de oro de las rutas (ingress) Cloudflare evalúa las rutas **de arriba hacia abajo**; las específicas van ANTES del comodín. Esta es la configuración correcta (orden exacto): | # | Hostname | Path | Servicio | Protocolo | Notas | |---|----------|------|----------|-----------|-------| | 1 | coolify.urieljareth.org | `/build/*` | `192.168.0.117:8000` | **http** | Build logs | | 2 | coolify.urieljareth.org | `/project/*` | `192.168.0.117:8000` | **http** | **Crítico: UI de apps** | | 3 | coolify.urieljareth.org | `/app/*` | `192.168.0.117:6001` | **http** ⚠️ | Websocket realtime | | 4 | coolify.urieljareth.org | `/terminal/ws*` | `192.168.0.117:6002` | **http** ⚠️ | Terminal websocket | | 5 | coolify.urieljareth.org | `*` | `192.168.0.117:8000` | **http** | Catch-all Coolify UI | | 6 | `*.urieljareth.org` | `*` | `192.168.0.117:443` | **https** | Apps via Traefik (`noTLSVerify: true`) | Reglas no negociables: - **6001 y 6002 → `http://` siempre.** Esos puertos no hablan TLS; con `https://` aparece `tls: first record does not look like a TLS handshake` y la terminal/realtime no conecta. - **`/project/*` va ANTES de `/app/*`.** Si no, las URL con `/application/...` las captura `/app/*` (puerto 6001) y la UI carga en blanco. - **`/terminal/ws*` sin barra final** (no `/terminal/ws/` ni `/terminal/ws/*`). - **`*.urieljareth.org` siempre al final**, con `noTLSVerify: true` (Traefik usa cert autofirmado interno). --- ## 4. Solución vía Dashboard (rápida, sin token) 1. Entrar a **Cloudflare Zero Trust → Networks → Tunnels → `coolify-tunnel` → Public Hostnames**. 2. Corregir cada ruta que esté mal según la tabla del paso 3. Lo más común: - `/app/*` (6001): Service `HTTPS` → **`HTTP`**. - `/terminal/ws*` (6002): Service `HTTPS` → **`HTTP`**; path sin barra final. 3. Guardar. **Cloudflared aplica el cambio solo, sin reinicio** (verás un nuevo `version=N`). --- ## 5. Solución vía API de Cloudflare (control total, requiere token) Para gestionar el túnel sin tocar el dashboard (automatizable). Requiere un token y los IDs cargados en `.env.local.ps1` (ver [Set-ProxmoxEnv.example.ps1](../../scripts/Set-ProxmoxEnv.example.ps1)): ```powershell $env:CLOUDFLARE_API_TOKEN = "" $env:CLOUDFLARE_ACCOUNT_ID = "" $env:CLOUDFLARE_ZONE_ID = "" $env:CLOUDFLARE_TUNNEL_ID = "5f0e5c5b-a180-46f2-a090-44d151006a62" ``` Wrapper PowerShell: [scripts/Invoke-CloudflareApi.ps1](../../scripts/Invoke-CloudflareApi.ps1) (mismo patrón que `Invoke-CoolifyApi.ps1`: Bearer token, `Invoke-RestMethod`). ```powershell . .\.env.local.ps1 # Verificar que el token es válido y sus permisos .\scripts\Invoke-CloudflareApi.ps1 -Path "/user/tokens/verify" # Leer la configuración (ingress) actual del túnel .\scripts\Invoke-CloudflareApi.ps1 ` -Path "/accounts/$($env:CLOUDFLARE_ACCOUNT_ID)/cfd_tunnel/$($env:CLOUDFLARE_TUNNEL_ID)/configurations" # Aplicar la configuración corregida (PUT) — CONFIRMAR antes de ejecutar $body = @{ config = @{ ingress = @( @{ hostname = "coolify.urieljareth.org"; path = "/build/*"; service = "http://192.168.0.117:8000" } @{ hostname = "coolify.urieljareth.org"; path = "/project/*"; service = "http://192.168.0.117:8000" } @{ hostname = "coolify.urieljareth.org"; path = "/app/*"; service = "http://192.168.0.117:6001" } @{ hostname = "coolify.urieljareth.org"; path = "/terminal/ws*"; service = "http://192.168.0.117:6002" } @{ hostname = "coolify.urieljareth.org"; service = "http://192.168.0.117:8000" } @{ hostname = "*.urieljareth.org"; service = "https://192.168.0.117:443"; originRequest = @{ noTLSVerify = $true } } @{ service = "http_status:404" } ) } } | ConvertTo-Json -Depth 10 .\scripts\Invoke-CloudflareApi.ps1 -Method PUT ` -Path "/accounts/$($env:CLOUDFLARE_ACCOUNT_ID)/cfd_tunnel/$($env:CLOUDFLARE_TUNNEL_ID)/configurations" ` -BodyJson $body ``` > ⚠️ El último elemento `http_status:404` (sin hostname) es obligatorio o la API > rechaza la configuración. La referencia completa de endpoints (DNS, crear túnel, > obtener token) está en [cloudflare-tunnel-coolify-agent_1.md](../cloudflare-tunnel-coolify-agent_1.md). **Cualquier `PUT`/`POST` a Cloudflare es un cambio de estado → confirmar con el usuario y capturar el estado actual (paso 2.2) antes de aplicar, para tener rollback.** --- ## 6. Verificación ```powershell # Debe aparecer un version=N mayor con 6001/6002 ya en http:// .\scripts\Invoke-ProxmoxSsh.ps1 -Command "pct exec 102 -- docker logs cloudflared --tail 10" ``` Luego en el navegador: recargar la UI de Coolify con **Ctrl+Shift+R** y abrir una terminal en cualquier contenedor. Debe conectar sin `Terminal websocket connection lost`. --- ## Notas - Los errores `tls: first record does not look like a TLS handshake` previos al cambio son **residuales** de conexiones ya abiertas; desaparecen solos. - Si el túnel se cae cada ~5 min con `failed to dial to edge with quic: timeout`, el contenedor debe correr con `--protocol http2` (UDP/7844 suele estar bloqueado en redes domésticas). Ver PASO 6 de [cloudflare-tunnel-coolify-agent_1.md](../cloudflare-tunnel-coolify-agent_1.md). - `cloudflared` es distroless: para leer archivos internos usa `docker cp`, no `cat` directo. --- **Verificado:** 2026-06-03 — corregidas rutas `/app/*` (6001) y `/terminal/ws*` (6002) de `https://` a `http://` desde el dashboard; terminal de Coolify operativa (config `version=16`).