petid-wikimemoria rápida, no arquitectura

Despliegue

Dónde vive cada cosa y cómo llega a producción — no un manual de Docker/CI, solo lo que no es obvio o se olvida entre deploys. Verificado contra el estado real de cada repo el 2026-09-02, no copiado de ecosystem-wiki.

Topología

VPS único — firewall solo ssh/http/https, todo bind a 127.0.0.1

petid-api
petid-redis
notification-api
notification-worker
notification-redis

Cloudflare — deploy manual, sin CI en ningún caso

petid-crm
Pages, vía dashboard
petid-admin
Pages, vía dashboard
petid-landing
Worker, npm run deploy
petid-client — sin deploy real (ver más abajo)

petid-api

Único repo con CI/CD de verdad: push a main → SSH al VPS →docker compose down && up -d --build.

petid-crm / petid-admin

Cero rastro de deploy en ninguno de los dos repos — sin.github/workflows, sin wrangler.*, sin Dockerfile. Si buscás un workflow file acá y no lo encontrás, no es que se perdió: nunca existió.

Confirmado directamente (no inferible del código): están deployados igual, a mano, vía el dashboard de Cloudflare Pages.

petid-landing

Cloudflare Worker (no Pages) vía @astrojs/cloudflare, con SSR real — no es un sitio estático. Deploy 100% manual: npm run deploy = astro build && wrangler deploy. Sin CI.

petid-client

Sin deploy real todavía.

dev-identity-server

No tiene wiring con ningún app de petid (client/api/crm/admin/landing) — no lo confundas con parte de este ecosistema todavía. La única conexión real es indirecta, vía notification-api: IDENTITY_JWT_SECRET/IDENTITY_SERVER_URL protegen el dashboard de Bull Board de notification-api, con BULL_BOARD_PASSWORD como fallback si dev-identity-server no está disponible.

Pasos

Runbook de cero para petid-api + notification-stack, con Nginx como reverse proxy en el propio VPS y TLS por Let's Encrypt. No cubre petid-crm/admin/landing (Cloudflare, deploy manual sin runbook formal — ver arriba) ni petid-client (sin deploy real todavía).

1

Preparar el VPS (una sola vez, compartido por ambos stacks)

Instalar Docker (docs oficiales). Crear los registros DNS en Cloudflare antes de llegar a certbot:

TipoNombreContenidoProxy
Aapi<IP del VPS>DNS only (nube gris)
Anotificaciones<IP del VPS>DNS only (nube gris)

Nube gris, no naranja, al menos para el primer certbot — con el proxy de Cloudflare activo, certbot nunca llega a validar el reto HTTP-01 contra tu Nginx. Se puede reactivar después de emitido el certificado.

2

Desplegar petid-api

git clone <url-petid-api> && cd petid-api
cp .env.example .env

Fijos en producción: NODE_ENV=production, DATABASE_URL real, CORS_ORIGINS con los dominios reales de Cloudflare Pages (no localhost). REDIS_URL/NOTIFICATIONS_BASE_URL apuntan al nombre del servicio dentro del propio compose (redis, notification-api), no a una URL pública.

Generar secretos — 5 valores distintos, no reusar el mismo:

openssl rand -base64 48   # → JWT_ACCESS_SECRET
openssl rand -base64 48   # → JWT_REFRESH_SECRET
openssl rand -base64 48   # → JWT_RESET_SECRET   (token stateless de reset, sin tabla en BD)
openssl rand -base64 48   # → JWT_VERIFY_SECRET  (token stateless de verificación de email)
openssl rand -base64 48   # → JWT_MFA_SECRET     (challengeToken del login en dos pasos)
openssl rand -base64 32   # → TWO_FACTOR_ENC_KEY  (AES, debe decodificar a exactamente 32 bytes)
docker compose up -d --build
3

Migrar petid-api

Desde tu máquina o CI (la imagen final del Dockerfile nunca instala pnpm, no se puede correr con docker compose exec):

DATABASE_URL="<prod>" pnpm prisma:deploy

Verificar: curl http://127.0.0.1:$PORT/health desde el VPS.

4

Desplegar notification-stack

git clone <url-notification-api>
git clone <url-notification-worker>
git clone <url-notification-stack>
cp notification-api/.env.example notification-api/.env
cp notification-worker/.env.example notification-worker/.env

Secretos que deben coincidir EXACTO entre repos — la causa más común de que algo falle en este paso:

  • NOTIFICATIONS_API_KEY: petid-api ↔ notification-api
  • FCM_CLEANER_WEBHOOK_SECRET: petid-api ↔ notification-worker
  • PROVIDER_ENCRYPTION_KEY: notification-api ↔ notification-worker — openssl rand -base64 32, mismo valor pegado en los dos .env
  • IDENTITY_JWT_SECRET: notification-api ↔ dev-identity-server (solo si se usa — BULL_BOARD_PASSWORD es el fallback sin dev-identity-server)

En ambos: REDIS_URL=redis://notification-redis:6379 (nombre del servicio en el propio compose). Solo notification-api corre migraciones — es la dueña del schema; el worker lo copia en build time desde su contexto.

cd notification-stack && ./deploy.sh

DATABASE_URL="<prod de notification-api>" pnpm prisma:deploy
5

Nginx + Let's Encrypt

server {
    listen 80;
    server_name api.tudominio.com;
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Mismo patrón para notification-api (su propio puerto, server_name notificaciones.tudominio.com). notification-worker no necesita site — no expone HTTP.

sudo ln -s /etc/nginx/sites-available/petid-api /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/notification-api /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

sudo certbot --nginx -d api.tudominio.com
sudo certbot --nginx -d notificaciones.tudominio.com

Si certbot da Failed authorization procedure o timeout: confirmar con dig que ambos dominios resuelven a la IP del VPS — no a una IP de Cloudflare — antes de reintentar (volver al paso 1, nube gris).

6

Verificación end-to-end

curl https://api.tudominio.com/health
curl https://notificaciones.tudominio.com/health
docker network inspect main-network   # confirma los 5 contenedores conectados

Desde petid-crm/petid-admin (Cloudflare Pages, ya deployados a mano), confirmar que las llamadas a api.tudominio.com pasan CORS y que un flujo que dispare notificación (ej. reset password) llega a notificaciones.tudominio.com internamente. petid-client no aplica todavía — sin deploy real (ver arriba).

7

logger-dozzle (opcional) — logs en vivo sin SSH por contenedor

Repo aparte, mismo VPS (comparte main-network). Un solo contenedor que lee el socket de Docker del host y muestra logs de todos los contenedores del VPS vía UI web — petid-api, notification-api, notification-worker, ambos Redis — no solo los que están en main-network con él.

git clone <url-logger-dozzle> && cd logger-dozzle
cp .env.example .env   # ajustar DOZZLE_PORT si hace falta
docker compose up -d

Exposición pública (fuera del repo, se gestiona en el VPS como el resto):

  1. Subdominio nuevo (p. ej. logs.tudominio.com) apuntando al VPS.
  2. Vhost de Nginx con proxy_pass http://127.0.0.1:$DOZZLE_PORT; + certbot.
  3. Basic Auth (htpasswd) delante, obligatorio — ver abajo.

El socket de Docker montado (/var/run/docker.sock) es acceso root-equivalente al host — este contenedor puede controlar todos los demás. Por eso el puerto va solo a 127.0.0.1 (nunca 0.0.0.0) y la exposición pública real pasa siempre por Nginx con Basic Auth como mínimo, nunca el puerto directo.