# Caso: open-seo vuelve a gestión completa de Coolify (imagen precompilada) **Fecha:** 2026-09-04/05 · **App:** `open-seo:main-0fgs5kwaab9esytaxkddsvts` (uuid `kj0kccsb4d46tm0d6qe6docy`, id DB 57) · **FQDN:** `https://kj0kccsb4d46tm0d6qe6docy.urieljareth.org` ## Contexto El sitio público estaba sirviéndose por una **cadena manual improvisada** tras la caída del 2026-08-27 22:14: ``` Traefik → open-seo-sidecar (nginx:alpine manual, montado 22:25 ese día) → proxy_pass → test-openseo (docker run manual de ghcr.io/every-app/open-seo:latest) ``` La aplicación en Coolify quedó `exited:unhealthy` sin contenedor. El usuario fijó como objetivo: **todo gestionable desde Coolify**. ## Qué se hizo (en orden) 1. **Limpieza de filas huérfanas de env vars** (ids 1152-1156, app 52 `insta-portal`): eran duplicados invisibles de un INSERT SQL manual con el morph type mal escapado (`App\\Models\\Application`). Las variables reales ya existían cifradas (ids 1161-1170) → se **borraron** los duplicados, no se activaron. Backup: `/root/backups/envvar-orphans-1152-1156-20260904-212119.tsv`. Tras esto: 0 valores sin cifrar en `environment_variables` de toda la instancia. 2. **Deploy git+railpack (intento 1):** `POST /deploy` contra el origen. El build tardó ~23 min y terminó, pero la app servía **404**: el repo (`github.com/every-app/open-seo`, público, actualizado ese mismo día) ahora compila un monorepo (`dist/client|server|open_seo_audit`, sin `index.html` en raíz) y railpack eligió un plan "estático con Caddy" que no corresponde. 3. **Conversión a imagen precompilada** (doctrina del caso firecrawl): - API: `docker_registry_image_name=ghcr.io/every-app/open-seo`, `docker_registry_image_tag=latest` (la API acepta estos campos). - DB: `UPDATE applications SET build_pack='dockerimage' WHERE id=57` — la API **rechaza** `build_pack=dockerimage` en PATCH (enum de validación sin ese valor, 422 "The selected build pack is invalid"), aunque el pipeline de deploy lo soporta de forma nativa (`deploy_dockerimage_buildpack` usa `docker_registry_image_name`, no `static_image`). Backup previo: `/root/backups/app57-pre-dockerimage-20260904-215456.tsv`. - Deploy 2: pull de `:latest` + rolling update. El contenedor **crash-loopeaba (exit 1)**: el preflight de la nueva imagen exige configurar auth. 4. **Env vars nuevas vía API** (`POST /applications/{uuid}/envs` — cifra por modelo, sin riesgo del bug de texto plano): - `AUTH_MODE=local_noauth` — replica el estado previo (el sitio ya corría público sin auth vía sidecar). Para Cloudflare Access: `AUTH_MODE=cloudflare_access` + `TEAM_DOMAIN` + `POLICY_AUD`. - `ALLOWED_HOST=kj0kccsb4d46tm0d6qe6docy.urieljareth.org` — allowlist de Vite detrás de proxy. - Deploy 3: contenedor **healthy** (la imagen GHCR trae healthcheck con `start_period=300s`, a diferencia de los servicios del §1.5 del índice). 5. **Retiro de los contenedores manuales** (con snapshots previos en `/root/backups/*-inspect-20260904-212317.json`): - `open-seo-sidecar` (nginx) — además sus labels duplicaban los routers de Traefik del FQDN y provocaban 503 mientras coexistía con el contenedor nuevo. - `test-openseo` (backend manual) — ya sin referencias. ## Resultado ``` status=running:healthy build_pack=dockerimage image=ghcr.io/every-app/open-seo:latest FQDN → HTTP 200 OpenSEO (servido por el contenedor de Coolify) ``` Dominio, env vars (DATAFORSEO_API_KEY, AUTH_MODE, ALLOWED_HOST), healthcheck, redeploys y rollbacks: todo administrable desde la UI/API de Coolify. ## Rollback - **App a git-build:** `UPDATE applications SET build_pack='railpack' WHERE id=57;` y redeploy (nota: con el main actual vuelve a servir 404 — ver paso 2). - **Imagen anterior:** el tag local `ghcr.io/every-app/open-seo:sha-c469a48` (12 días) sigue en el host; o fijar `docker_registry_image_tag` a ese sha. - **Contenedores manuales:** recrear desde los inspect snapshots (sidecar: `docker run -d --name open-seo-sidecar --network coolify --restart unless-stopped -v /tmp/openseo-sidecar/nginx.conf:/etc/nginx/nginx.conf:ro nginx:alpine`). ## Lecciones (añadir a la lista mental de gotchas) - **PATCH /applications/{uuid} no acepta `build_pack=dockerimage`** aunque el backend lo soporta y `POST /applications/dockerimage` lo crea así. Para convertir una app existente: DB o recrear el recurso. - **`static_image` NO es la imagen del build pack dockerimage** — ese modo lee `docker_registry_image_name` (+`docker_registry_image_tag`, default `latest`). - **Un contenedor manual con los labels de Traefik de una app de Coolify rompe el enrutamiento** cuando la app real vuelve a deployar (routers duplicados → 503). Al restaurar una app, retirar esos "sidecars con labels". - La nueva imagen de open-seo exige `AUTH_MODE` en su preflight (exit 1 si falta) y recomienda `ALLOWED_HOST` detrás de proxy.