Files
Proxmox-Coolify-Manager/docs/cloudflare-tunnel-coolify-agent_1.md
urieljareth f587a8baa2 Agrega scripts y runbooks: Cloudflare API, autostart Coolify, parche Chatwoot, deploy Solo Leveling + docs de casos
- scripts/Invoke-CloudflareApi.ps1: control de tunnel/DNS via API
- scripts/Install-CoolifyAutostart.ps1 + scripts/host/: autostart de LXC 102 + tunnel tras corte
- scripts/Apply-ChatwootEnterprisePatch.ps1: parche enterprise (autodetecta creds)
- Deploy-SoloLeveling.ps1: deploy build-on-server verificado
- docs/runbooks/cloudflare-tunnel.md y autostart-coolify.md
- docs/casos/chatwoot-enterprise-patch.md (credenciales redactadas)
- docs/AGENTS-coolify-apps.md + issues y reportes
- deploy_skill/references/coolify-4.1.2-notes.md: hallazgos verificados (seccion 7)
- package.json para verify-online.mjs del coolify-deploy skill
- .gitignore: excluye .claude/ y .opencode/ (config local de herramientas)
2026-07-18 11:31:31 -06:00

14 KiB

Instrucciones para Agente IA — Cloudflare Tunnel + Coolify

Entorno: Proxmox VE → LXC CT 102 → Coolify (Docker) → Traefik + cloudflared
Dominio: urieljareth.org
IP interna del servidor Coolify: 192.168.0.117


HERRAMIENTAS DISPONIBLES

El agente tiene acceso a:

  1. SSH a Proxmox — para ejecutar comandos en el servidor
  2. API de Cloudflare — para configurar DNS, Tunnel y Zero Trust sin usar la UI

Credenciales necesarias antes de comenzar:

  • CF_API_TOKEN — token con permisos: Zona → DNS → Editar y Zero Trust → Editar para urieljareth.org
  • CF_ACCOUNT_ID — ID de cuenta de Cloudflare (visible en el panel principal)
  • CF_ZONE_ID — ID de zona DNS (visible en la sección DNS del dominio)
  • TUNNEL_TOKEN — token del túnel cloudflared (se obtiene o regenera via API)
  • Acceso SSH al host Proxmox o directamente al CT 102 ([email protected].0.117)

PASO 1 — Verificar conectividad básica desde el servidor

Conectarse por SSH al contenedor de Coolify y ejecutar:

# Verificar que TCP/443 saliente funciona
curl -v https://cloudflare.com 2>&1 | head -10

# Verificar que UDP/7844 NO está disponible (es común en redes domésticas)
nc -zv 198.41.192.37 7844

Resultado esperado:

  • curl debe mostrar Connected to cloudflare.com
  • nc debe mostrar Connection timed out (UDP bloqueado — esto es normal y se resuelve en el Paso 3)

PASO 2 — Configurar DNS en Cloudflare via API

2.1 Verificar registros existentes

curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" | jq '.result[] | {name, type, content, proxied}'

2.2 Crear registro CNAME wildcard (obligatorio)

Reemplazar <TUNNEL_ID> con el ID del túnel (formato: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx):

curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "type": "CNAME",
    "name": "*",
    "content": "<TUNNEL_ID>.cfargotunnel.com",
    "proxied": true,
    "ttl": 1
  }'

2.3 Crear registro CNAME para coolify

curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "type": "CNAME",
    "name": "coolify",
    "content": "<TUNNEL_ID>.cfargotunnel.com",
    "proxied": true,
    "ttl": 1
  }'

⚠️ Ambos registros deben tener proxied: true (ícono naranja en la UI). Sin el wildcard *, los subdominios de las apps desplegadas devuelven DNS_PROBE_FINISHED_NXDOMAIN.


PASO 3 — Obtener o regenerar el token del túnel

3.1 Listar túneles existentes

curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/cfd_tunnel" \
  -H "Authorization: Bearer $CF_API_TOKEN" | jq '.result[] | {id, name, status}'

3.2 Obtener el token del túnel existente

curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/cfd_tunnel/<TUNNEL_ID>/token" \
  -H "Authorization: Bearer $CF_API_TOKEN" | jq -r '.result'

Guardar el resultado como TUNNEL_TOKEN.

3.3 (Alternativa) Crear un túnel nuevo

Solo si no existe un túnel previo:

curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/cfd_tunnel" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "name": "coolify-tunnel",
    "tunnel_secret": "'$(openssl rand -base64 32)'"
  }' | jq '{id: .result.id, name: .result.name}'

Luego obtener el token con el endpoint del paso 3.2.


PASO 4 — Configurar las rutas del túnel via API

Este es el paso más crítico. Las rutas se llaman "ingress rules" y deben configurarse en orden exacto.

Orden obligatorio de rutas

# 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

Aplicar configuración via API

curl -s -X PUT "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/cfd_tunnel/<TUNNEL_ID>/configurations" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "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"
        }
      ]
    }
  }'

⚠️ El último elemento http_status:404 es obligatorio. La API rechaza la configuración si no hay un catch-all final sin hostname.

Verificar que las rutas se aplicaron correctamente

curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/cfd_tunnel/<TUNNEL_ID>/configurations" \
  -H "Authorization: Bearer $CF_API_TOKEN" | jq '.result.config.ingress[] | {hostname, path, service}'

PASO 5 — Reglas críticas de las rutas (no negociables)

Puertos 6001 y 6002 — siempre HTTP, nunca HTTPS

Los puertos 6001 (websocket realtime) y 6002 (terminal websocket) de Coolify NO hablan TLS internamente. Si se configura https:// para estas rutas, cloudflared devuelve:

tls: first record does not look like a TLS handshake

Siempre usar http://192.168.0.117:6001 y http://192.168.0.117:6002.

Ruta /project/* — va ANTES de /app/*

Sin la regla /project/* en posición 2, cualquier URL con /application/ en el path (como /project/xxx/environment/xxx/application/xxx) es capturada por /app/* y enviada al websocket del puerto 6001. La UI de Coolify carga en blanco.

Ruta /terminal/ws* — sin barra al final

Escribir /terminal/ws* y NO /terminal/ws/*. La variante con barra no hace match con las conexiones del terminal.

Ruta *.urieljareth.org — siempre al final con noTLSVerify

El wildcard debe ir después de todas las rutas específicas de coolify.urieljareth.org. Apunta a Traefik en puerto 443 con HTTPS y requiere noTLSVerify: true porque Traefik usa certificados autofirmados internamente.


PASO 6 — Desplegar cloudflared en el servidor

Ejecutar via SSH en el contenedor de Coolify:

# Detener y eliminar el contenedor anterior si existe
docker stop cloudflared 2>/dev/null
docker rm cloudflared 2>/dev/null

# Crear el contenedor con protocolo HTTP2 forzado
docker run -d \
  --name cloudflared \
  --restart always \
  -e TUNNEL_EDGE_PORT=443 \
  cloudflare/cloudflared:latest \
  tunnel --no-autoupdate --protocol http2 run --token $TUNNEL_TOKEN

Por qué --protocol http2 es obligatorio

En redes domésticas, el puerto UDP/7844 que usa QUIC (el protocolo por defecto de cloudflared) está bloqueado por la mayoría de routers e ISPs. Sin --protocol http2, el túnel se conecta brevemente y cae en timeout cada ~5 minutos con el error:

failed to dial to edge with quic: timeout: no recent network activity

-e TUNNEL_EDGE_PORT=443 fuerza la conexión TCP por el puerto 443, que siempre está abierto.

Verificar que el túnel está activo

docker logs cloudflared --tail 20

Resultado correcto — deben aparecer 4 líneas como esta:

INF Registered tunnel connection connIndex=0 ... protocol=http2
INF Registered tunnel connection connIndex=1 ... protocol=http2
INF Registered tunnel connection connIndex=2 ... protocol=http2
INF Registered tunnel connection connIndex=3 ... protocol=http2

Si aparece Provided Tunnel token is not valid, el token se truncó. Repetir el Paso 3.2 para obtenerlo completo.


PASO 7 — Configurar Traefik con DNS Challenge

Esto elimina la dependencia de validación HTTP para obtener certificados SSL. Es necesario cuando "Always Use HTTPS" está activo en Cloudflare (que redirige las validaciones HTTP de Let's Encrypt antes de que lleguen a Traefik).

7.1 Crear token de API para DNS Challenge

El token debe tener permisos: Zona → DNS → Editar para urieljareth.org.

# Verificar que el token tiene los permisos correctos
curl -s -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
  -H "Authorization: Bearer $CF_DNS_API_TOKEN" | jq '{status: .result.status}'

7.2 Editar el docker-compose de Traefik via SSH

# Hacer backup primero
cp /data/coolify/proxy/docker-compose.yml /data/coolify/proxy/docker-compose.yml.bak

# Editar
nano /data/coolify/proxy/docker-compose.yml

Agregar dentro del servicio traefik, sección environment:

environment:
  - CF_DNS_API_TOKEN=<token-dns-api>

Reemplazar las líneas de httpchallenge en command por:

- '--certificatesresolvers.letsencrypt.acme.dnschallenge=true'
- '--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare'
- '--certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53'
- '--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json'

7.3 Limpiar certificados anteriores y reiniciar

echo '{}' > /data/coolify/proxy/acme.json
chmod 600 /data/coolify/proxy/acme.json
docker compose -f /data/coolify/proxy/docker-compose.yml up -d --force-recreate

⚠️ Si hay error 429 en los logs de coolify-proxy, Let's Encrypt aplicó rate limit por intentos fallidos previos. Esperar 1 hora antes de reintentar.


PASO 8 — Verificación final

Verificar estado de todos los servicios

# Contenedores corriendo
docker ps --format "table {{.Names}}\t{{.Status}}" | grep -E "coolify|cloudflared|traefik"

# Túnel con 4 conexiones activas
docker logs cloudflared --tail 5 | grep "Registered tunnel"

# Traefik sin errores de certificado
docker logs coolify-proxy --tail 50 2>&1 | grep -i "error\|certificate\|acme\|429"

Verificar rutas desde afuera

# Coolify UI debe responder 200
curl -s -o /dev/null -w "%{http_code}" https://coolify.urieljareth.org

# Una app desplegada debe responder 200 o 301
curl -s -o /dev/null -w "%{http_code}" https://miapp.urieljareth.org

DIAGNÓSTICO DE ERRORES COMUNES

Error Causa más probable Verificar
530 — Unregistered from Argo Tunnel cloudflared caído docker logs cloudflared --tail 20
502 Bad Gateway Traefik sin cert SSL o puerto 3000 Logs de coolify-proxy
Página en blanco al abrir app en Coolify Falta regla /project/* Verificar rutas del túnel
tls: first record does not look like TLS https:// en puerto 6001 o 6002 Corregir a http:// en ingress rules
DNS_PROBE_FINISHED_NXDOMAIN Falta CNAME wildcard Verificar registro * en DNS
Túnel cae cada 5 min UDP/7844 bloqueado Confirmar --protocol http2 en el contenedor
429 en logs de Traefik Rate limit de Let's Encrypt Esperar 1h, luego reiniciar Traefik
Provided Tunnel token is not valid Token truncado Obtener token completo via API (Paso 3.2)

FLUJO PARA DESPLEGAR UNA APP NUEVA

Por un bug en Coolify beta.472, la UI de apps individuales no carga tras la creación. Usar este flujo:

# 1. Obtener el ID y UUID de la app recién creada
docker exec coolify-db psql -U coolify -d coolify \
  -c "SELECT id, uuid, name, fqdn FROM applications ORDER BY id DESC LIMIT 3;"

# 2. Actualizar dominio y puerto antes del deploy
docker exec coolify-db psql -U coolify -d coolify \
  -c "UPDATE applications SET fqdn='https://miapp.urieljareth.org', ports_exposes='80' WHERE id=<ID>;"

# 3. Hacer deploy via API de Coolify
curl -X POST "http://localhost:8000/api/v1/deploy?uuid=<UUID>&force=false" \
  -H "Authorization: Bearer <COOLIFY_API_TOKEN>"

# 4. Verificar que Traefik apunta al puerto correcto
grep "server.port" /data/coolify/applications/<UUID>/docker-compose.yaml

Si el puerto sigue siendo 3000 en lugar de 80:

sed -i 's/loadbalancer.server.port=3000/loadbalancer.server.port=80/g' \
  /data/coolify/applications/<UUID>/docker-compose.yaml

docker compose -f /data/coolify/applications/<UUID>/docker-compose.yaml up -d --force-recreate

ARCHIVOS DE REFERENCIA EN EL SERVIDOR

Archivo Ruta
Docker-compose de Traefik /data/coolify/proxy/docker-compose.yml
Backup de Traefik /data/coolify/proxy/docker-compose.yml.bak
Certificados SSL /data/coolify/proxy/acme.json
Docker-compose de cada app /data/coolify/applications/<UUID>/docker-compose.yaml