# recordaLexia · Runbook de operación (instancia pública) Operación de la instancia SaaS en la VM (Ubuntu, Docker, nginx nativo). Escrito para poder ejecutarse dentro de 6 meses sin recordar nada. ## 0. Requisitos - VM con Docker + Compose v2 y nginx nativo (ya presentes en `vnicjaume`). - DNS: `recordalexia.jaumegar.work` (o el subdominio elegido) apuntando a la IP de la VM. - Credenciales SMTP de cualquier relay estándar (para el email de recuperación). - `rclone` configurado con un remoto para backups fuera de la VM (recomendado). ## 1. Primer despliegue ```bash # 1) Código git clone https://gitea.jaumegar.work/jaume/recordalexia.git cd recordalexia # 2) Variables (rellenar TODAS; DB_PASSWORD nueva y larga) cp .env.prod.example .env.prod nano .env.prod # 3) Levantar el stack (build en la propia VM) docker compose -f docker-compose.prod.yml --env-file .env.prod up -d --build # 4) Comprobar salud docker compose -f docker-compose.prod.yml --env-file .env.prod ps curl -s http://127.0.0.1:8089/ | head -c 200 # el frontend responde en loopback # 5) Nginx + TLS sudo cp deploy/nginx-recordalexia.conf /etc/nginx/sites-available/recordalexia sudo ln -s /etc/nginx/sites-available/recordalexia /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx sudo certbot --nginx -d recordalexia.jaumegar.work # 6) Humo: abrir https://recordalexia.jaumegar.work, registrar una familia de # prueba, pedir recuperación de contraseña y comprobar que LLEGA EL EMAIL. # Después borrar la familia desde Cuenta → Borrar cuenta. ``` Checklist post-despliegue: - [ ] `https://…` carga y Chrome ofrece «Instalar app» (o Añadir a pantalla de inicio). - [ ] NO existe la familia demo (el seeder queda apagado en prod). - [ ] El email de recuperación llega (si no: revisar SMTP_* y el log del backend). - [ ] Cabeceras: `curl -sI https://… | grep -iE "nosniff|frame-ancestors|referrer"`. - [ ] Alta del dominio en uptime-kuma. - [ ] Backup + RESTORE ensayado (sección 3). **Sin esto no se abre el registro a terceros.** ## 2. Backups ```bash chmod +x deploy/backup.sh crontab -e # 03:30 Europe/Madrid, log en el home: 30 3 * * * /home/ubuntu/recordalexia/deploy/backup.sh >> $HOME/recordalexia-backup.log 2>&1 ``` - Local: `~/backups/recordalexia/` (retención 14). - Externa: define `RCLONE_REMOTE` en `.env.prod` (p. ej. `b2:recordalexia-backups`). - Vigila el log: un backup de menos de 1 KB se marca como ERROR. ## 3. Restauración (ENSAYAR antes de abrir al público) El ensayo restaura sobre una BD limpia SIN tocar la de producción: ```bash cd ~/recordalexia LAST=$(ls -1t ~/backups/recordalexia/recordalexia-*.sql.gz | head -1) # BD de ensayo temporal (sin volumen persistente ni puertos publicados) docker run -d --name restore-test \ -e POSTGRES_DB=recordalexia -e POSTGRES_USER=recordalexia \ -e POSTGRES_PASSWORD=ensayo postgres:16-alpine sleep 5 gunzip -c "$LAST" | docker exec -i restore-test psql -U recordalexia recordalexia # Verificar: las familias están docker exec restore-test psql -U recordalexia recordalexia -tA \ -c "SELECT count(*) FROM family;" docker rm -f restore-test ``` Restauración REAL (pérdida de la BD de producción): ```bash docker compose -f docker-compose.prod.yml --env-file .env.prod stop backend gunzip -c "$LAST" | docker compose -f docker-compose.prod.yml --env-file .env.prod \ exec -T postgres psql -U recordalexia recordalexia docker compose -f docker-compose.prod.yml --env-file .env.prod start backend ``` ## 4. Actualizar la aplicación ```bash cd ~/recordalexia && git pull docker compose -f docker-compose.prod.yml --env-file .env.prod up -d --build ``` Liquibase aplica las migraciones al arrancar. Si el backend no levanta, sus logs dicen exactamente qué changeset falló: `docker logs recordalexia-prod-backend | grep -iA3 liquibase`. ## 5. Problemas conocidos | Síntoma | Causa probable | Arreglo | |---|---|---| | 413 al subir algo | límite del nginx | ya hay `client_max_body_size` en el server block y 64m global | | No llegan emails | SMTP_* mal o puerto bloqueado | `docker logs recordalexia-prod-backend \| grep -i mail`; probar el relay con `openssl s_client -starttls smtp -connect $SMTP_HOST:587` | | 429 al entrar | freno anti fuerza bruta | esperar el Retry-After; es el comportamiento diseñado | | Certificado caducado | certbot | `sudo certbot renew --dry-run`; el timer de systemd debería renovarlo solo | | La PWA no se actualiza | SW cacheado | el SW comprueba `ngsw.json` al abrir; forzar con recarga o cerrar/abrir la app | ## 6. Rotación de credenciales - **DB**: cambiar en `.env.prod` + `ALTER USER recordalexia WITH PASSWORD '…'` en el contenedor postgres + `up -d` del backend. - **SMTP**: cambiar en `.env.prod` + `up -d backend`. - **PIN/contraseña de una familia**: lo hace la propia familia desde la app (Cuenta o «¿Has olvidado la contraseña?»).