feat(saas): infraestructura de producción para la VM
Bloque C de la fase SaaS (ficheros; el despliegue se hace con el runbook): - docker-compose.prod.yml: superficie mínima (solo el frontend en 127.0.0.1:8089 para el nginx nativo), límites de memoria, sin seeder. - deploy/nginx-recordalexia.conf: server block con cabeceras de seguridad, X-Forwarded-For para el rate-limiter y preparado para certbot. - deploy/backup.sh: pg_dump diario comprimido, retención 14, copia externa vía rclone y detección de backups vacíos. - .env.prod.example (y .env.prod en .gitignore). - docs/operacion-saas.md: runbook con primer despliegue, checklist, restore ENSAYADO paso a paso, actualización y problemas conocidos.
This commit is contained in:
72
docker-compose.prod.yml
Normal file
72
docker-compose.prod.yml
Normal file
@@ -0,0 +1,72 @@
|
||||
# Stack de PRODUCCIÓN de recordaLexia (instancia pública en la VM).
|
||||
# docker compose -f docker-compose.prod.yml --env-file .env.prod up -d --build
|
||||
#
|
||||
# Principios:
|
||||
# - Superficie mínima: Postgres y backend NO publican puertos; el frontend solo
|
||||
# escucha en 127.0.0.1:8089 y el nginx NATIVO de la VM hace el proxy TLS.
|
||||
# - Límites de recursos: la VM aloja más servicios; nadie se la come.
|
||||
# - Sin familia demo: el seeder es fail-safe y aquí NO se activa.
|
||||
# - Ningún secreto en este fichero: todo llega de .env.prod (fuera de git).
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: recordalexia-prod-postgres
|
||||
environment:
|
||||
POSTGRES_DB: ${DB_NAME}
|
||||
POSTGRES_USER: ${DB_USER}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
TZ: Europe/Madrid
|
||||
volumes:
|
||||
- pgdata-prod:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
mem_limit: 512m
|
||||
restart: unless-stopped
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
container_name: recordalexia-prod-backend
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
SPRING_PROFILES_ACTIVE: prod
|
||||
DB_HOST: postgres
|
||||
DB_PORT: "5432"
|
||||
DB_NAME: ${DB_NAME}
|
||||
SPRING_DATASOURCE_USERNAME: ${DB_USER}
|
||||
SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD}
|
||||
# Dominio público (enlaces del email de recuperación) y CORS estricto.
|
||||
PUBLIC_BASE_URL: ${PUBLIC_BASE_URL}
|
||||
CORS_ALLOWED_ORIGINS: ${PUBLIC_BASE_URL}
|
||||
MAIL_FROM: ${MAIL_FROM}
|
||||
# SMTP del proveedor (binding relajado de Spring). Sin SMTP_HOST definido,
|
||||
# los emails de recuperación se quedan en el log del contenedor.
|
||||
SPRING_MAIL_HOST: ${SMTP_HOST:-}
|
||||
SPRING_MAIL_PORT: ${SMTP_PORT:-587}
|
||||
SPRING_MAIL_USERNAME: ${SMTP_USERNAME:-}
|
||||
SPRING_MAIL_PASSWORD: ${SMTP_PASSWORD:-}
|
||||
SPRING_MAIL_PROPERTIES_MAIL_SMTP_AUTH: "true"
|
||||
SPRING_MAIL_PROPERTIES_MAIL_SMTP_STARTTLS_ENABLE: "true"
|
||||
TZ: Europe/Madrid
|
||||
mem_limit: 768m
|
||||
restart: unless-stopped
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
container_name: recordalexia-prod-frontend
|
||||
depends_on:
|
||||
- backend
|
||||
ports:
|
||||
# SOLO loopback: el único acceso desde fuera es el nginx nativo de la VM.
|
||||
- "127.0.0.1:8089:80"
|
||||
mem_limit: 64m
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
pgdata-prod:
|
||||
Reference in New Issue
Block a user