Despliegue de la aplicación
Patrón general del VPS: cada pieza es una aplicación Compose de Dokploy desplegada desde Git, unida a la red externa dokploy-network (Traefik enruta por dominio), con volúmenes con nombre, secretos solo en las variables de entorno de Dokploy y restart: unless-stopped. La imagen se construye en el servidor al desplegar; no se compila nada en local.
Orden recomendado
- Base de datos: un servicio Database → PostgreSQL en Dokploy. No requiere SQL manual (ver Despliegue con Dokploy).
- API (
ganadero-api): migraciones y API en un mismo Compose, conectados a esa base conDATABASE_URL. Ver abajo. - Aplicación web (
ganadero-web), apuntando a la URL de la API. - Manual (
manual-ganadero), opcionalmente con Basic Auth. Ver Despliegue con Dokploy.
Servicios de la API
PostgreSQL es externo (servicio de Dokploy) y el docker-compose.yml de ganadero-api define dos servicios, ambos en la red dokploy-network y con la misma DATABASE_URL:
| Servicio | Función |
|---|---|
migrate | One-shot: crea (si falta) el rol sin privilegios ganadero_app y aplica las migraciones. Termina y sale. |
api | Arranca solo si migrate terminó con éxito; verifica que puede ejecutar cada transacción como ganadero_app (si no, se niega a arrancar); healthcheck a /health/ready. |
migrate es un servicio aparte (y no un paso de arranque de la API) porque un fallo de migración detiene el despliegue antes de arrancar la API y, con varias réplicas, no corren migraciones concurrentes.
Pasos (API)
- Cree el PostgreSQL en Dokploy y copie su conexión interna: usuario, clave, Internal Host y base. Esa es su
DATABASE_URL; no hay que ejecutar SQL. - En Dokploy: Create Service → Compose, fuente Git, ruta
./docker-compose.yml. La reddokploy-networkdebe existir. - En Environment cargue las variables de la sección PRODUCCIÓN del
.env.examplede la API. Son obligatoriasDATABASE_URL,JWT_ACCESS_SECRETyWEB_ORIGIN(sin ellas el Compose falla a propósito). Si el usuario o la clave de la base tienen caracteres especiales, codifíquelos en laDATABASE_URL(@→%40,:→%3A,/→%2F). Ver Variables de entorno. - En Domains asigne el dominio de la API (HTTPS con Let's Encrypt) al servicio
api, puerto3000. Traefik es el único punto público. - Despliegue y verifique
https://<api>/health/ready. El primer despliegue aplica el esquema solo. - Correo: por defecto
MAIL_DRIVER=console; la API arranca igual y registra el avisocorreos deshabilitados (MAIL_DRIVER=console), pero nadie recibe invitaciones ni enlaces de restablecimiento. Para activarlo definaMAIL_DRIVER=smtp,SMTP_HOST,MAIL_FROMy, si aplica,SMTP_USER/SMTP_PASSWORD. - Para rotar la clave de la base, cámbiela en el servicio PostgreSQL de Dokploy, actualice
DATABASE_URLy redespliegue. - Primer usuario y organización: desde la aplicación web, Registrarse (
/authentication/signup). Los datos de demostración de desarrollo (db:seed) no corren en producción.
Para validar la sintaxis sin desplegar:
DATABASE_URL=postgres://admin:x@db:5432/ganadero JWT_ACCESS_SECRET=<32+ caracteres> WEB_ORIGIN=https://a.co docker compose --env-file /dev/null config -q
Pasos (aplicación web)
- Create Service → Docker Compose apuntando al repositorio
ganadero-web(docker-compose.yml). El Dockerfile compila Angular dentro de Docker (node:24-alpine) y lo sirve con nginx. - Defina las variables (ver Variables de entorno): como mínimo
API_URL. - Asigne el dominio al servicio
web, puerto 80 (Traefik termina el HTTPS). No se publica ningún puerto en el host. - Despliegue. Al arrancar, el contenedor genera
/env.jsy las cabeceras de seguridad (CSP) con esas variables: cambiarAPI_URLo la CSP no requiere reconstruir, basta editar la variable y reiniciar.
Cookies y CORS entre dominios
La sesión usa una cookie de refresco HttpOnly; Secure; SameSite=Strict. Verifique que WEB_ORIGIN de la API sea exactamente el origen de la aplicación web. Si la web y la API están en dominios hermanos (app.x.com y api.x.com) la cookie debe poder compartirse entre ambos; en caso de duda, revise COOKIE_SAMESITE y el dominio de la cookie.
Salud y proxy
GET /healthyGET /health/ready(API);GET /healthz(manual).- Detrás de Traefik se necesita
TRUST_PROXY_HOPS=1(es el valor por defecto del Compose) para que el límite de intentos de/auth/*use la IP real del cliente y no la del proxy.
Respaldos
Los respaldos se hacen sobre el servicio PostgreSQL de Dokploy (pestaña Backups de su ficha); la política está pendiente de definir, ver Respaldos y restauración.