99 lines
5.0 KiB
Markdown
99 lines
5.0 KiB
Markdown
# 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 <title>OpenSEO</title> (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
|
|
<labels-del-snapshot> 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.
|