Saltar a contenido

Proxmox / VPS (Notas Operativas)#

Este documento registra el estado actual del host Proxmox y los cambios que se han hecho para preparar el despliegue de SignalDashPro en contenedores (LXC).

Host Proxmox (PVE)#

  • Public IP: 185.209.230.42
  • Proxmox UI: https://185.209.230.42:8006/
  • SSH al host Proxmox: puerto 22
  • OS host: Debian 13 (trixie)
  • Proxmox VE: proxmox-ve 9.1.0, pve-manager 9.1.5
  • Kernel: 6.14.11-4-pve
  • Time sync: chrony (NTP activo en el host). Los CTs heredan el reloj del host.

Nota: el acceso SSH por 2201 no es el host, es un DNAT hacia un contenedor (ver sección NAT).

Red / Bridge#

  • Bridge principal usado por CTs: vmbr0
  • Subred interna CTs: 192.168.100.0/24
  • Gateway: 192.168.100.254

Storage#

  • local (dir) activo
  • Templates disponibles localmente:
  • debian-13-standard_13.1-2_amd64.tar.zst
  • debian-12-standard_12.12-1_amd64.tar.zst
  • ubuntu-24.04-standard_24.04-2_amd64.tar.zst

Firewall / NAT (iptables)#

En el host existe un script de reglas:

  • Archivo: /etc/iptables.rules
  • Se carga por systemd:
  • iptables-load.service (enabled)

Reglas relevantes (resumen):

  • Políticas por defecto:
  • INPUT DROP
  • FORWARD DROP
  • OUTPUT ACCEPT
  • Permisos de administración:
  • 22/tcp (SSH host)
  • 8006/tcp (UI Proxmox)
  • NAT/Masquerade:
  • POSTROUTING MASQUERADE para 192.168.100.0/24 -> eth0
  • DNAT de puertos públicos hacia CTs (ejemplos ya existentes):
  • 80/81/443 -> 192.168.100.2 (nginx proxy manager, CT 103)
  • 8007 -> 192.168.100.3:8006 (PMG, CT 104)
  • 2201 -> 192.168.100.8:22 (CT 102 "web")
  • 2202 -> 192.168.100.10:22 (CT 110 "signaldashpro")

Observaciones detectadas:

  • En /etc/iptables.rules hay puertos declarados como UDP para servicios típicamente TCP (por ejemplo 5432, 587, 993) mientras el DNAT está en TCP. Esto conviene revisarlo antes de exponer servicios nuevos.

Proxmox firewall:

  • pve-firewall está corriendo, pero el estado reporta disabled/running (daemon activo, políticas Proxmox deshabilitadas). En la práctica, las reglas efectivas son las de /etc/iptables.rules.

Contenedores existentes (antes de cambios)#

CTs detectados:

  • 100 (ollama) stopped
  • 101 (dns) running
  • 102 (web) running
  • 103 (nginxproxymanager) running
  • 104 (pmg) running
  • 105 (wireguard) running
  • 106 (correo) running
  • 107 (postgresql) running

PostgreSQL interno (CT 107)#

  • CT: 107 (hostname=postgresql)
  • IP: 192.168.100.7
  • Puerto: 5432/tcp (escucha en 0.0.0.0:5432)

Uso recomendado:

  • Los servicios en la subred 192.168.100.0/24 deben usar 192.168.100.7:5432 directamente (sin el mapping local :5433).

Cambio realizado: CT para SignalDashPro#

Se creó un contenedor LXC para desplegar SignalDashPro:

  • CTID: 110
  • Hostname: signaldashpro
  • Template: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
  • Recursos:
  • CPU: 2 cores
  • RAM: 2048 MB
  • Swap: 512 MB
  • Disco: 20G en local
  • Red:
  • Bridge: vmbr0
  • IP: 192.168.100.10/24
  • GW: 192.168.100.254
  • Features:
  • nesting=1,keyctl=1 (útil si instalamos Docker dentro del CT)
  • Arranque:
  • onboot=1
  • Estado: running

Acceso SSH externo (public -> CT 110):

  • 185.209.230.42:2202 -> 192.168.100.10:22

Nota: aunque existe el DNAT :2202, en algunos momentos el CT110 puede rechazar autenticación por password (hardening/sshd). El método confiable para operar CT110 es entrar al host Proxmox por :22 y ejecutar pct exec 110 ....

Config file:

  • /etc/pve/lxc/110.conf

Deploy actual (CT 110)#

En CT 110 ya se instaló Docker y se levantó el stack (sin Postgres local; se usa el Postgres externo del CT 107).

  • Código en CT110 (release activo): /opt/signaldashpro/current
  • current es un symlink: /opt/signaldashpro/current -> /opt/signaldashpro/releases/<timestamp>-<sha>
  • Env compartido (fuera de git, persistente): /opt/signaldashpro/env/.env.binance.demo.month
  • En cada release hay un link: /opt/signaldashpro/current/env/.env.binance.demo.month -> /opt/signaldashpro/env/.env.binance.demo.month
  • Storage compartido (persistente): /opt/signaldashpro/storage
  • En cada release hay un link: /opt/signaldashpro/current/storage -> /opt/signaldashpro/storage
  • Compose usado en CT110: /opt/signaldashpro/current/deploy/docker-compose.ct110.yml
  • En este repo vive la misma definición en deploy/docker-compose.ct110.yml
  • Puertos expuestos en CT110:
  • Backend: 8096
  • Frontend: 3000
  • Docs: 8001 (redirige / -> /docs/)

Automatizacion (systemd timers)#

Para operar 30 dias con evidencia y backups, se agregaron systemd timers.

En CT110: - signaldashpro-daily.timer: smoke + evidencia (escribe logs en storage/logs/ops/ y JSON en storage/logs/evidence/). - signaldashpro-alerts.timer: notificaciones Slack/Teams (solo corre si hay SLACK_WEBHOOK_URL o TEAMS_WEBHOOK_URL en el env del backend). - signaldashpro-storage-backup.timer: backup diario de storage/ a /opt/backups/signaldashpro/ con retencion de 14.

En CT107 (Postgres): - signaldashpro-db-backup.timer: pg_dump diario a /var/backups/signaldashpro/ con retencion de 14.

Notas de tiempo: - En CT110 y CT107 se configuró Time zone: UTC para que logs/timers sean consistentes. - systemd-timesyncd se salta en contenedores por ConditionVirtualization=!container; la sincronizacion NTP ocurre en el host (chrony).

Comandos:

systemctl list-timers --all | grep signaldashpro
systemctl status signaldashpro-daily.service signaldashpro-alerts.service signaldashpro-storage-backup.service

Notas importantes de docker compose:

  • docker-compose carga variables desde un .env en el mismo directorio del compose.
  • En CT110 se resolvió enlazando deploy/.env hacia ../.env:
# Opcion A (recomendada): no dependas del symlink, usa env-file explicito
cd /opt/signaldashpro/current/deploy
docker compose --env-file ../.env -f docker-compose.ct110.yml up -d --build

# Opcion B: mantener `deploy/.env -> ../.env` (si prefieres el comportamiento "por default")
cd /opt/signaldashpro/current
ln -sf ../.env deploy/.env
cd /opt/signaldashpro/current/deploy
docker compose -f docker-compose.ct110.yml up -d --build

Deploy (release inmutable) via pct + tarball#

Si el repo en GitHub es privado, curl https://github.com/.../archive/<sha>.tar.gz desde CT110 puede devolver 404 sin token. El flujo estable es:

  • git archive en tu PC (donde ya tienes el repo)
  • scp al host Proxmox
  • pct push al CT110
  • extraer al nuevo release y mover el symlink current

El procedimiento completo está descrito en HANDOFF.md (sección "Deploy rapido en CT110").

Acceso / Operación básica#

Entrar al CT desde el host:

pct enter 110

Ver estado/listado:

pct status 110
pct list

PostgreSQL / permisos (CT 107)#

El backend hace pequeñas migraciones al iniciar (por ejemplo ALTER TABLE ... para asegurar volúmenes como BIGINT).

Si las tablas fueron creadas por postgres (superuser), el rol de la app puede tener GRANT pero no puede hacer ALTER TABLE sin ser el owner.

Se aplicó el fix: cambiar ownership de todas las tablas/secuencias del schema public al rol signaldashpro.

Referencia de ejecución (desde el host, dentro del CT107):

pct exec 107 -- bash -lc "runuser -u postgres -- psql -P pager=off -d signaldashpro -v ON_ERROR_STOP=1 <<'SQL'
ALTER SCHEMA public OWNER TO signaldashpro;
SELECT format('ALTER TABLE public.%I OWNER TO signaldashpro;', tablename)
FROM pg_tables WHERE schemaname = 'public';
\\gexec
SELECT format('ALTER SEQUENCE public.%I OWNER TO signaldashpro;', sequence_name)
FROM information_schema.sequences WHERE sequence_schema = 'public';
\\gexec
SQL"

Nginx Proxy Manager (CT 103)#

Recomendación: exponer todo por NPM (CT103) y evitar abrir nuevos DNAT.

Proxies típicos:

  • sdp.perlatec.net -> 192.168.100.10:3000 (frontend)
  • api.sdp.perlatec.net -> 192.168.100.10:8096 (backend)
  • docs.sdp.perlatec.net -> 192.168.100.10:8001 (mkdocs)

Let’s Encrypt en NPM requiere que el DNS apunte correctamente al public IP (185.209.230.42) y que estén abiertos 80/443 hacia NPM (ya están DNAT a 192.168.100.2).

Estado actual (2026-02-09):

  • https://sdp.perlatec.net responde 200 (frontend).
  • https://api.sdp.perlatec.net responde 200 (backend).
  • CORS validado para Origin: https://sdp.perlatec.net en endpoints privados como /binance/account.

Nota: si Chrome muestra errores viejos (CORS/500) despues de un deploy, suele ser cache; probar hard refresh o incognito.

Próximos pasos (pendientes)#

  1. Operacion 30 dias (demo):
  2. Mantener jobs (market_sync, cola, alertas) estables y con evidencia diaria.
  3. Observabilidad minima:
  4. Definir retencion/rotacion de logs del host/containers y alertas por caidas.
  5. Seguridad:
  6. Evitar exponer puertos nuevos por DNAT; centralizar en NPM.
  7. Evaluar autenticacion para el API si se mantiene publico.

Seguridad (importante)#

  • No guardar credenciales en git.
  • env/.env.ssh está pensado para uso local y debe permanecer ignorado por git.