- 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)
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:
- SSH a Proxmox — para ejecutar comandos en el servidor
- 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 → EditaryZero Trust → Editarpara urieljareth.orgCF_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:
curldebe mostrarConnected to cloudflare.comncdebe mostrarConnection 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 devuelvenDNS_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:404es 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 |