Skip to content
On this page

📖 GUNEI ERP - RUNBOOK COMPLETO

Deployment & Operations Guide - Enterprise ArchitectureVersión 2.1 - Enero 2026


📑 Tabla de Contenidos

  1. Overview del Sistema
  2. Infraestructura
  3. Componentes Individuales
  4. CI/CD Pipeline
  5. Operaciones Diarias
  6. Gestión de Ambientes
  7. Monitoreo y Alertas
  8. Troubleshooting
  9. Apéndices

1. Overview del Sistema

1.1 Arquitectura General

┌────────────────────────────────────────────────────────────┐
│                       Internet                             │
└──────────────────────┬─────────────────────────────────────┘


┌────────────────────────────────────────────────────────────┐
│          Caddy Shared (Reverse Proxy + SSL)                │
│              Ports: 80 (HTTP), 443 (HTTPS)                 │
└──────────┬────────────────────┬────────────────────────────┘
           │                    │
           ▼                    ▼
  ┌─────────────────┐   ┌─────────────────┐
  │  gunei.xyz      │   │ api.gunei.xyz   │
  │  [STAGING]      │   │ [STAGING]       │
  │  Frontend:3001  │   │ Backend:3000    │
  └─────────────────┘   └────────┬────────┘

           ┌─────────────────────┴────────────┐
           │                                  │
           ▼                                  ▼
  ┌─────────────────┐              ┌─────────────────┐
  │ erp-dev DB      │              │ erp DB          │
  │ [STAGING]       │              │ [PRODUCTION]    │
  └─────────────────┘              └─────────────────┘
           │                                  │
           └──────────────┬───────────────────┘

                  ┌───────▼────────┐
                  │ cloud-sql-proxy │
                  │  (Port 5450)    │
                  └───────┬─────────┘


              ┌───────────────────────┐
              │   GCP Cloud SQL       │
              │ banco-gal:southamerica│
              │    -east1:gunei-erp   │
              └───────────────────────┘

      VPS containers conectados vía: gunei-network
      Cloud SQL Proxy conecta a GCP vía credentials.json

1.2 Arquitectura de Ambientes

El sistema ahora implementa una arquitectura enterprise por ambientes:

/opt/
├── infrastructure/              # Servicios compartidos
│   ├── caddy/                  # Reverse proxy compartido
│   ├── cloud-sql-proxy/        # Conexión a GCP Cloud SQL
│   └── monitoring/             # Prometheus/Grafana (futuro)

└── apps/
    └── gunei-erp/
        ├── backend/
        │   ├── staging/        # Ambiente de desarrollo
        │   └── production/     # Ambiente productivo
        └── frontend/
            ├── staging/        # Ambiente de desarrollo
            └── production/     # Ambiente productivo

Ventajas de esta arquitectura:

  • ✅ Separación clara entre staging y production
  • ✅ Base de datos en GCP Cloud SQL (backups automáticos, alta disponibilidad)
  • ✅ Un solo Caddy para todo el routing (simplicidad)
  • ✅ Escalabilidad: fácil agregar nuevos ambientes
  • ✅ Mejor organización y mantenimiento

1.3 Stack Tecnológico

ComponenteTecnologíaVersiónPuertos
RuntimeBunLatest-
Backend FrameworkHonoLatest3000 (staging), 3100 (prod)
Frontend FrameworkSvelteKit5.x3001 (staging), 3101 (prod)
DatabaseGCP Cloud SQL (PostgreSQL)175450 (via proxy)
DB ProxyCloud SQL Proxy2.15.05450
Reverse ProxyCaddyLatest80/443
Container RuntimeDocker28.x-
OrchestrationDocker Compose2.x-

1.4 Ambientes y URLs

Staging (Ambiente actual operativo)

Production (Preparado, no deployado aún)

  • Frontend: (Pendiente configurar subdominio) → gunei-frontend-production:3101
  • Backend: (Pendiente configurar subdominio) → gunei-backend-production:3100
  • Database: erp en GCP Cloud SQL (via cloud-sql-proxy:5450)

1.5 Health Endpoints

AmbienteServicioURLEndpoint
StagingFrontendhttps://gunei.xyz/health/health
StagingBackendhttps://api.gunei.xyz/status/status
ProductionFrontend(Pendiente)/health
ProductionBackend(Pendiente)/status

1.6 Flujo de Requests (Staging)

Usuario → gunei.xyz:443 (HTTPS)

Caddy Shared (SSL termination + routing)

gunei-frontend-staging:3001 (SvelteKit)

api.gunei.xyz:443 (API calls)

Caddy Shared (CORS + routing)

gunei-backend-staging:3000 (Hono)

cloud-sql-proxy:5450 → GCP Cloud SQL → erp-dev (PostgreSQL)

2. Infraestructura

2.1 VPS Contabo

Especificaciones:

  • Provider: Contabo
  • IP: 154.53.36.180
  • OS: Ubuntu 24.04 LTS
  • Hostname: vmi2991275
  • Acceso: SSH con ED25519 key

Conexión SSH:

bash
ssh root@gunei.xyz
# o
ssh root@154.53.36.180

2.2 Estructura de Directorios Actualizada

/opt/
├── infrastructure/
│   ├── caddy/
│   │   ├── Caddyfile              # Configuración del reverse proxy
│   │   ├── docker-compose.yml     # Compose de Caddy
│   │   └── caddy_data/            # SSL certificates y data
│   │
│   ├── cloud-sql-proxy/
│   │   ├── docker-compose.yml     # Compose de Cloud SQL Proxy
│   │   └── credentials.json       # GCP service account credentials
│   │
│   └── monitoring/                # Preparado para Grafana/Prometheus
│       └── docker-compose.yml

└── apps/
    └── gunei-erp/
        ├── backend/
        │   ├── staging/
        │   │   ├── docker-compose.yml
        │   │   └── .env           # Variables de entorno staging
        │   └── production/
        │       ├── docker-compose.yml
        │       └── .env           # Variables de entorno production

        └── frontend/
            ├── staging/
            │   └── docker-compose.yml
            └── production/
                └── docker-compose.yml

/root/
├── scripts/
│   ├── monitor-logs.sh            # Script de monitoreo
│   ├── health-check.sh            # Health checks multi-ambiente
│   └── alert-check.sh             # Sistema de alertas

└── legacy/                        # Archivos viejos (preservados por backup)
    └── gunei-erp-back/

2.3 Docker Networking

Red Principal: gunei-network

Todos los servicios comparten esta red para comunicación interna:

bash
# Ver la red
docker network inspect gunei-network

# Servicios en la red:
# - caddy-shared (reverse proxy)
# - cloud-sql-proxy (conexión a GCP Cloud SQL)
# - gunei-frontend-staging (frontend staging)
# - gunei-backend-staging (backend staging)
# - gunei-frontend-production (frontend production - futuro)
# - gunei-backend-production (backend production - futuro)

Ventajas:

  • Comunicación entre contenedores por nombre
  • Aislamiento de red del host
  • DNS interno automático
  • Único punto de configuración

Migración de red:

  • Red anterior: red-npm (deprecada)
  • Red actual: gunei-network (enterprise)

2.4 DNS Configuration

Registros en Porkbun:

TipoHostApunta aTTL
A@ (gunei.xyz)154.53.36.180600
Aapi154.53.36.180600
Aprod (futuro)154.53.36.180600
Aapi-prod (futuro)154.53.36.180600

Verificación:

bash
dig gunei.xyz +short
dig api.gunei.xyz +short
# Ambos deben devolver: 154.53.36.180

3. Componentes Individuales

3.1 Caddy Shared (Reverse Proxy + SSL)

3.1.1 Descripción

Caddy es el punto de entrada único para todo el tráfico HTTP/HTTPS de todos los ambientes. Se encarga de:

  • SSL automático con Let's Encrypt
  • Reverse proxy a múltiples ambientes
  • CORS headers por ambiente
  • Compresión gzip/zstd
  • Logs separados por dominio/ambiente
  • Routing inteligente

3.1.2 Ubicación y Archivos

bash
# Directorio principal
cd /opt/infrastructure/caddy

# Archivos clave
/opt/infrastructure/caddy/Caddyfile           # Configuración principal
/opt/infrastructure/caddy/docker-compose.yml  # Definición del servicio
/opt/infrastructure/caddy/caddy_data/         # Certificados SSL

3.1.3 Caddyfile Actualizado

nginx
{
    email miguel.culaciati@gunei.com.ar  # Email para Let's Encrypt
    log {
        output stdout
        format console
    }
}

# Snippet reutilizable para headers de seguridad
(security_headers) {
    encode zstd gzip
    header -Via
    header -Server
    header Server "Gunei Platform"
    header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    header X-Content-Type-Options "nosniff"
    header X-Frame-Options "SAMEORIGIN"
    header Referrer-Policy "strict-origin-when-cross-origin"
}

# Snippet reutilizable para CORS
(cors_headers) {
    header {
        Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, OPTIONS"
        Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
        Access-Control-Allow-Credentials "true"
    }
    
    @options {
        method OPTIONS
    }
    respond @options 204
}

# ============================================
# STAGING ENVIRONMENT
# ============================================

# Frontend Staging
gunei.xyz {
    import security_headers
    reverse_proxy gunei-frontend-staging:3001
    log {
        output file /var/log/caddy/staging-frontend.log
    }
}

# Backend Staging
api.gunei.xyz {
    import security_headers
    import cors_headers
    
    header Access-Control-Allow-Origin "https://gunei.xyz"
    
    reverse_proxy gunei-backend-staging:3000
    
    log {
        output file /var/log/caddy/staging-backend.log
    }
}

# ============================================
# PRODUCTION ENVIRONMENT (Preparado)
# ============================================

# Frontend Production (cuando se configure)
# prod.gunei.xyz {
#     import security_headers
#     reverse_proxy gunei-frontend-production:3101
#     log {
#         output file /var/log/caddy/production-frontend.log
#     }
# }

# Backend Production (cuando se configure)
# api-prod.gunei.xyz {
#     import security_headers
#     import cors_headers
#     
#     header Access-Control-Allow-Origin "https://prod.gunei.xyz"
#     
#     reverse_proxy gunei-backend-production:3100
#     
#     log {
#         output file /var/log/caddy/production-backend.log
#     }
# }

3.1.4 docker-compose.yml

yaml
services:
  caddy:
    image: caddy:latest
    container_name: caddy-shared
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    ports:
      - "80:80"                          # HTTP
      - "443:443/tcp"                    # HTTPS
      - "443:443/udp"                    # HTTP/3
    environment:
      - TZ=America/Argentina/Buenos_Aires
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./caddy_data:/data
    networks:
      - gunei-network

networks:
  gunei-network:
    external: true

3.1.5 Comandos Comunes

bash
# Ver logs de Caddy
docker logs caddy-shared -f

# Recargar configuración (sin downtime)
docker exec caddy-shared caddy reload --config /etc/caddy/Caddyfile

# Reiniciar Caddy
docker restart caddy-shared

# Ver certificados SSL
docker exec caddy-shared ls -la /data/caddy/certificates/

# Verificar configuración
docker exec caddy-shared caddy validate --config /etc/caddy/Caddyfile

# Ver logs de acceso por ambiente
docker exec caddy-shared cat /var/log/caddy/staging-frontend.log
docker exec caddy-shared cat /var/log/caddy/staging-backend.log

# Tail logs en tiempo real
docker exec caddy-shared tail -f /var/log/caddy/staging-backend.log

3.1.6 SSL Automático

Caddy obtiene y renueva certificados SSL automáticamente:

  1. Primera vez: Al iniciar, solicita certificados a Let's Encrypt
  2. Renovación: Automática ~30 días antes del vencimiento
  3. Almacenamiento: En /data/caddy/certificates/

Verificar SSL:

bash
# Ver info del certificado
curl -vI https://gunei.xyz 2>&1 | grep -i 'expire\|issuer'

# Ver desde el VPS
docker exec caddy-shared ls -la /data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/gunei.xyz/

3.1.7 Troubleshooting Caddy

Problema: SSL no se genera

bash
# Ver logs detallados
docker logs caddy-shared --tail 100 | grep -i "acme\|tls"

# Forzar renovación
docker restart caddy-shared

# Verificar que el puerto 443 está abierto
netstat -tlnp | grep :443

Problema: "502 Bad Gateway"

bash
# Verificar que el backend/frontend está corriendo
docker ps | grep "gunei-"

# Probar conexión interna
docker exec caddy-shared wget -O- http://gunei-frontend-staging:3001/health
docker exec caddy-shared wget -O- http://gunei-backend-staging:3000/status

Problema: Cambios en Caddyfile no se aplican

bash
# El volumen está montado como read-only
# Editar en el HOST, luego reload
nano /opt/infrastructure/caddy/Caddyfile
docker exec caddy-shared caddy reload --config /etc/caddy/Caddyfile

3.2 Cloud SQL Proxy (Conexión a GCP)

3.2.1 Descripción

Cloud SQL Proxy conecta los containers del VPS a la base de datos PostgreSQL en GCP Cloud SQL:

  • Seguridad: Conexión encriptada vía IAM, no expone puertos públicos
  • Backups: GCP maneja backups automáticos
  • Alta disponibilidad: Failover automático gestionado por GCP
  • Databases: erp-dev (staging), erp (production)

3.2.2 Ubicación y Archivos

bash
# Directorio principal
cd /opt/infrastructure/cloud-sql-proxy

# Archivos clave
/opt/infrastructure/cloud-sql-proxy/docker-compose.yml   # Definición del servicio
/opt/infrastructure/cloud-sql-proxy/credentials.json     # GCP service account key

3.2.3 Instancia GCP

Proyecto GCP: banco-gal
Región: southamerica-east1
Instancia: gunei-erp
Connection name: banco-gal:southamerica-east1:gunei-erp

Databases:
  - erp-dev   → Staging
  - erp       → Production

Usuario: dba_dev
Puerto proxy: 5450

3.2.4 docker-compose.yml

yaml
services:
  cloud-sql-proxy:
    image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.15.0
    container_name: cloud-sql-proxy
    restart: unless-stopped
    command: banco-gal:southamerica-east1:gunei-erp --address 0.0.0.0 --port 5450 --credentials-file /credentials.json
    volumes:
      - ./credentials.json:/credentials.json:ro
    networks:
      - gunei-network

networks:
  gunei-network:
    external: true

Nota sobre el puerto:

  • Puerto: 5450 (expuesto a gunei-network)
  • Los containers acceden via: cloud-sql-proxy:5450

3.2.5 Comandos Comunes

bash
# Ver logs del proxy
docker logs cloud-sql-proxy -f

# Ver últimas líneas
docker logs cloud-sql-proxy --tail 50

# Verificar que el proceso está corriendo
docker exec cloud-sql-proxy ps aux | grep cloud-sql-proxy

# Restart del proxy
docker restart cloud-sql-proxy

# Ver estado del container
docker ps | grep cloud-sql-proxy

# Test de conexión desde backend staging
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "SELECT 1;"

# Test de conexión desde backend production
docker exec gunei-backend-production psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp -c "SELECT 1;"

# Listar databases
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "\l"

# Ver tablas de staging
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "\dt"

3.2.6 Backups

Gestionados por GCP Cloud SQL:

  • Backups automáticos diarios
  • Retención configurable en GCP Console
  • Point-in-time recovery disponible

Backup manual (export):

bash
# Desde GCP Console o gcloud CLI
gcloud sql export sql gunei-erp gs://bucket-name/backup.sql --database=erp-dev

# Dump local via proxy (para archivos locales)
docker exec gunei-backend-staging pg_dump -h cloud-sql-proxy -p 5450 -U dba_dev erp-dev > backup_staging_$(date +%Y%m%d).sql

3.2.7 Migrations

Workflow por ambiente:

bash
# Para staging (desde máquina local o CI)
# Conectar via Cloud SQL Proxy o Auth Proxy local
export DATABASE_URL="postgresql://dba_dev:PASSWORD@cloud-sql-proxy:5450/erp-dev"
dbmate up

# Para production
export DATABASE_URL="postgresql://dba_dev:PASSWORD@cloud-sql-proxy:5450/erp"
dbmate up

3.2.8 Troubleshooting Cloud SQL Proxy

Problema: "Connection refused" al proxy

bash
# Verificar que el proxy está corriendo
docker ps | grep cloud-sql-proxy

# Ver logs del proxy
docker logs cloud-sql-proxy --tail 50

# Verificar que está escuchando
docker exec cloud-sql-proxy netstat -tlnp | grep 5450

Problema: "Authentication failed" o "Permission denied"

bash
# Verificar que credentials.json existe y es válido
ls -la /opt/infrastructure/cloud-sql-proxy/credentials.json

# Verificar logs por errores de IAM
docker logs cloud-sql-proxy | grep -i "error\|permission\|denied"

# Verificar que el service account tiene los roles correctos en GCP:
# - Cloud SQL Client
# - Cloud SQL Instance User

Problema: "Unable to connect to Cloud SQL instance"

bash
# Verificar conexión a internet desde el proxy
docker exec cloud-sql-proxy ping -c 3 google.com

# Verificar el connection string
docker logs cloud-sql-proxy | head -20

# Verificar que la instancia existe y está running en GCP Console
# https://console.cloud.google.com/sql/instances

Problema: Proxy se reinicia constantemente

bash
# Ver logs completos
docker logs cloud-sql-proxy

# Causas comunes:
# - credentials.json inválido o expirado
# - Instancia Cloud SQL no existe
# - Service account sin permisos
# - Errores de red/firewall

# Recrear el container
cd /opt/infrastructure/cloud-sql-proxy
docker compose down
docker compose up -d

3.3 Backend (Bun + Hono)

3.3.1 Descripción

API REST construida con:

  • Runtime: Bun (JavaScript/TypeScript runtime ultra-rápido)
  • Framework: Hono (framework web minimalista)
  • Features: AFIP integration, facturación electrónica
  • Ambientes: Staging (operativo) y Production (preparado)

3.3.2 Backend Staging

Ubicación:

bash
cd /opt/apps/gunei-erp/backend/staging

docker-compose.yml:

yaml
services:
  backend:
    image: ghcr.io/gunei-dev/gunei-erp-back:staging
    pull_policy: always
    container_name: gunei-backend-staging
    ports:
      - "3000:3000"
    networks:
      - gunei-network
    env_file:
      - .env
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
        tag: "{{.Name}}/{{.ID}}"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/status"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

networks:
  gunei-network:
    external: true

Variables de entorno (.env):

bash
# Database - GCP Cloud SQL via Proxy
DATABASE_URL=postgresql://dba_dev:PASSWORD@cloud-sql-proxy:5450/erp-dev

# Application
NODE_ENV=staging
PORT=3000

# AFIP (ejemplos)
AFIP_KEY=...
AFIP_SECRET=...

# JWT
JWT_SECRET=...

3.3.3 Backend Production (Preparado)

Ubicación:

bash
cd /opt/apps/gunei-erp/backend/production

docker-compose.yml:

yaml
services:
  backend:
    image: ghcr.io/gunei-dev/gunei-erp-back:production
    pull_policy: always
    container_name: gunei-backend-production
    ports:
      - "3100:3000"                      # Puerto diferente
    networks:
      - gunei-network
    env_file:
      - .env
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
        tag: "{{.Name}}/{{.ID}}"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/status"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

networks:
  gunei-network:
    external: true

Variables de entorno (.env):

bash
# Database - GCP Cloud SQL via Proxy
DATABASE_URL=postgresql://dba_dev:PASSWORD@cloud-sql-proxy:5450/erp

# Application
NODE_ENV=production
PORT=3000                                # Puerto interno (mapeado a 3100)

# AFIP
AFIP_KEY=...
AFIP_SECRET=...

# JWT
JWT_SECRET=...

3.3.4 Comandos Comunes

Staging:

bash
# Ver logs del backend staging
docker logs gunei-backend-staging -f

# Ver últimas 50 líneas
docker logs gunei-backend-staging --tail 50

# Reiniciar backend staging
docker restart gunei-backend-staging

# Ver health status
curl http://localhost:3000/status
curl https://api.gunei.xyz/status

# Entrar al contenedor
docker exec -it gunei-backend-staging sh

# Ver variables de entorno
docker exec gunei-backend-staging env | grep DATABASE

# Deploy manual (pull + restart)
cd /opt/apps/gunei-erp/backend/staging
docker compose pull backend
docker compose up -d backend

Production:

bash
# Ver logs del backend production (cuando esté deployado)
docker logs gunei-backend-production -f

# Reiniciar backend production
docker restart gunei-backend-production

# Ver health status (puerto diferente)
curl http://localhost:3100/status

# Deploy manual
cd /opt/apps/gunei-erp/backend/production
docker compose pull backend
docker compose up -d backend

3.3.5 Healthcheck

El backend expone dos endpoints de health:

/status - Estado básico

json
{
  "status": "ok",
  "timestamp": "2026-01-12T23:00:00Z",
  "environment": "staging"
}

/ping - Ping simple

pong

3.3.6 Troubleshooting Backend

Problema: Backend no inicia

bash
# Ver logs de error (staging)
docker logs gunei-backend-staging --tail 100

# Verificar variables de entorno
docker exec gunei-backend-staging env

# Verificar conexión a Cloud SQL via proxy
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "SELECT 1;"

Problema: "Database connection failed"

bash
# Verificar cloud-sql-proxy está corriendo
docker ps | grep cloud-sql-proxy

# Ver logs del proxy
docker logs cloud-sql-proxy --tail 50

# Probar conexión desde backend
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "SELECT 1;"

# Verificar credenciales en .env
cat /opt/apps/gunei-erp/backend/staging/.env | grep DATABASE

Problema: Backend staging y production en conflicto

bash
# Los puertos deben ser diferentes:
# - Staging: 3000 (host) → 3000 (container)
# - Production: 3100 (host) → 3000 (container)

# Verificar puertos en uso
netstat -tlnp | grep -E "3000|3100"

# Ver puertos de containers
docker ps --format "table {{.Names}}\t{{.Ports}}"

3.4 Frontend (SvelteKit + Bun)

3.4.1 Descripción

Aplicación web construida con:

  • Framework: SvelteKit 5
  • Runtime: Bun
  • Adapter: svelte-adapter-bun (SSR con Bun)
  • Features: ERP UI, AFIP integration
  • Ambientes: Staging (operativo) y Production (preparado)

3.4.2 Frontend Staging

Ubicación:

bash
cd /opt/apps/gunei-erp/frontend/staging

docker-compose.yml:

yaml
services:
  frontend:
    image: ghcr.io/gunei-dev/gunei-erp-front:develop
    pull_policy: always
    container_name: gunei-frontend-staging
    ports:
      - "3001:3001"
    networks:
      - gunei-network
    environment:
      - NODE_ENV=staging
      - API_BASE_URL=https://api.gunei.xyz
      - BASE_URL=https://api.gunei.xyz
      - PUBLIC_BASE_URL=https://api.gunei.xyz
      - PORT=3001
      - ORIGIN=https://gunei.xyz
      - TZ=America/Argentina/Buenos_Aires
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
        tag: "{{.Name}}/{{.ID}}"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

networks:
  gunei-network:
    external: true

3.4.3 Frontend Production (Preparado)

Ubicación:

bash
cd /opt/apps/gunei-erp/frontend/production

docker-compose.yml:

yaml
services:
  frontend:
    image: ghcr.io/gunei-dev/gunei-erp-front:production
    pull_policy: always
    container_name: gunei-frontend-production
    ports:
      - "3101:3001"                      # Puerto diferente
    networks:
      - gunei-network
    environment:
      - NODE_ENV=production
      - API_BASE_URL=https://api-prod.gunei.xyz
      - BASE_URL=https://api-prod.gunei.xyz
      - PUBLIC_BASE_URL=https://api-prod.gunei.xyz
      - PORT=3001
      - ORIGIN=https://prod.gunei.xyz
      - TZ=America/Argentina/Buenos_Aires
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
        tag: "{{.Name}}/{{.ID}}"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

networks:
  gunei-network:
    external: true

3.4.4 Comandos Comunes

Staging:

bash
# Ver logs del frontend staging
docker logs gunei-frontend-staging -f

# Reiniciar frontend staging
docker restart gunei-frontend-staging

# Ver health status
curl http://localhost:3001/health
curl https://gunei.xyz/health

# Deploy manual
cd /opt/apps/gunei-erp/frontend/staging
docker compose pull frontend
docker compose up -d frontend

# Ver configuración
docker inspect gunei-frontend-staging | grep -A 20 Env

Production:

bash
# Ver logs del frontend production (cuando esté deployado)
docker logs gunei-frontend-production -f

# Reiniciar frontend production
docker restart gunei-frontend-production

# Ver health status (puerto diferente)
curl http://localhost:3101/health

# Deploy manual
cd /opt/apps/gunei-erp/frontend/production
docker compose pull frontend
docker compose up -d frontend

3.4.5 Health Endpoint

Endpoint: /healthMétodo: GET Acceso: Público (no requiere auth)

json
{
  "status": "healthy",
  "timestamp": "2026-01-12T23:00:00.000Z",
  "service": "gunei-erp-frontend",
  "version": "0.0.1",
  "runtime": "bun",
  "environment": "staging"
}

3.4.6 Troubleshooting Frontend

Problema: Frontend no carga

bash
# Ver logs (staging)
docker logs gunei-frontend-staging --tail 100

# Verificar que está corriendo
docker ps | grep frontend

# Test interno desde Caddy
docker exec caddy-shared curl http://gunei-frontend-staging:3001/health

Problema: "Failed to fetch" en API calls

bash
# Verificar CORS en Caddy
docker exec caddy-shared cat /etc/caddy/Caddyfile | grep -A 10 "api.gunei.xyz"

# Verificar backend está accesible
curl https://api.gunei.xyz/status

# Ver logs del navegador (Network tab en DevTools)

Problema: Frontend staging y production en conflicto

bash
# Los puertos deben ser diferentes:
# - Staging: 3001 (host) → 3001 (container)
# - Production: 3101 (host) → 3001 (container)

# Verificar puertos en uso
netstat -tlnp | grep -E "3001|3101"

# Ver puertos de containers
docker ps --format "table {{.Names}}\t{{.Ports}}"

4. CI/CD Pipeline

4.1 Overview del CI/CD

Ambos repositorios (frontend y backend) tienen GitHub Actions configurado para deployment automático.

Flujo completo:

git push origin develop

GitHub Actions triggered

Build Docker image

Push to GHCR (ghcr.io)

SSH to VPS

Ir al directorio del ambiente correcto

docker compose pull

docker compose up -d

Health check

Discord notification

4.2 Ambientes en CI/CD

BranchAmbienteDirectorio DeployContainer
developStaging/opt/apps/gunei-erp/{backend|frontend}/staginggunei-{backend|frontend}-staging
mainProduction/opt/apps/gunei-erp/{backend|frontend}/productiongunei-{backend|frontend}-production

4.3 Frontend CI/CD Workflow

Archivo: .github/workflows/deploy-staging.yml (para develop)

4.3.1 Trigger

yaml
on:
  push:
    branches: [develop]  # Deploy a staging
  workflow_dispatch:     # También manual desde GitHub UI

4.3.2 Deploy Step (actualizado)

yaml
- name: Deploy to VPS - Staging
  uses: appleboy/ssh-action@v1.0.0
  with:
    host: ${{ secrets.VPS_HOST }}
    username: ${{ secrets.VPS_USER }}
    key: ${{ secrets.VPS_SSH_KEY }}
    script: |
      cd /opt/apps/gunei-erp/frontend/staging
      docker compose pull frontend
      docker compose up -d frontend
      sleep 10
      docker compose ps

4.3.3 Health Check (actualizado)

yaml
- name: Health Check - Staging
  run: |
    max_attempts=30
    attempt=0
    while [ $attempt -lt $max_attempts ]; do
      if curl -f https://gunei.xyz/health; then
        echo "✅ Staging health check passed!"
        exit 0
      fi
      attempt=$((attempt + 1))
      echo "Attempt $attempt/$max_attempts failed, retrying in 2s..."
      sleep 2
    done
    echo "❌ Staging health check failed after $max_attempts attempts"
    exit 1

4.4 Backend CI/CD Workflow

Similar estructura pero con paths actualizados:

yaml
- name: Deploy to VPS - Staging
  uses: appleboy/ssh-action@v1.0.0
  with:
    host: ${{ secrets.VPS_HOST }}
    username: ${{ secrets.VPS_USER }}
    key: ${{ secrets.VPS_SSH_KEY }}
    script: |
      cd /opt/apps/gunei-erp/backend/staging
      docker compose pull backend
      docker compose up -d backend
      sleep 10
      docker compose ps

4.5 Production CI/CD (Futuro)

Cuando se implemente production:

Archivo: .github/workflows/deploy-production.yml (para main)

yaml
on:
  push:
    branches: [main]  # Deploy a production
  workflow_dispatch:

# ... similar estructura pero con:
# - Directorio: /opt/apps/gunei-erp/{service}/production
# - Container: gunei-{service}-production
# - Health check: https://prod.gunei.xyz/health (o equivalente)

4.6 GitHub Secrets (sin cambios)

Los secrets se mantienen igual:

SecretValorUso
VPS_HOSTgunei.xyzHostname del VPS para SSH
VPS_USERrootUsuario SSH
VPS_SSH_KEY-----BEGIN OPENSSH PRIVATE KEY-----...Private key ED25519 completa
DISCORD_WEBHOOK_URLhttps://discord.com/api/webhooks/...Webhook para notificaciones

5. Operaciones Diarias

5.1 Deploy Manual por Ambiente

5.1.1 Staging

Backend Staging:

bash
ssh root@gunei.xyz
cd /opt/apps/gunei-erp/backend/staging
docker compose pull backend
docker compose up -d backend
docker compose ps
docker logs gunei-backend-staging --tail 20
curl http://localhost:3000/status

Frontend Staging:

bash
ssh root@gunei.xyz
cd /opt/apps/gunei-erp/frontend/staging
docker compose pull frontend
docker compose up -d frontend
docker compose ps
docker logs gunei-frontend-staging --tail 20
curl http://localhost:3001/health

5.1.2 Production (cuando esté activo)

Backend Production:

bash
ssh root@gunei.xyz
cd /opt/apps/gunei-erp/backend/production
docker compose pull backend
docker compose up -d backend
docker compose ps
docker logs gunei-backend-production --tail 20
curl http://localhost:3100/status

Frontend Production:

bash
ssh root@gunei.xyz
cd /opt/apps/gunei-erp/frontend/production
docker compose pull frontend
docker compose up -d frontend
docker compose ps
docker logs gunei-frontend-production --tail 20
curl http://localhost:3101/health

5.2 Ver Logs por Ambiente

5.2.1 Logs en Tiempo Real

Staging:

bash
# Frontend staging
docker logs gunei-frontend-staging -f

# Backend staging
docker logs gunei-backend-staging -f

# Cloud SQL Proxy
docker logs cloud-sql-proxy -f

# Caddy (compartido)
docker logs caddy-shared -f

Production:

bash
# Frontend production
docker logs gunei-frontend-production -f

# Backend production
docker logs gunei-backend-production -f

5.2.2 Logs Históricos

bash
# Últimas N líneas
docker logs gunei-frontend-staging --tail 50

# Desde hace X tiempo
docker logs gunei-backend-staging --since 1h
docker logs gunei-backend-staging --since 30m

# Entre timestamps
docker logs gunei-backend-staging --since 2026-01-12T10:00:00 --until 2026-01-12T12:00:00

# Buscar errores
docker logs gunei-backend-staging | grep -i error
docker logs gunei-backend-staging | grep -i "database\|connection"

5.2.3 Logs de Caddy por Ambiente

bash
# Logs del frontend staging
docker exec caddy-shared cat /var/log/caddy/staging-frontend.log | tail -20

# Logs del backend staging
docker exec caddy-shared cat /var/log/caddy/staging-backend.log | tail -20

# Logs del frontend production (cuando exista)
docker exec caddy-shared cat /var/log/caddy/production-frontend.log | tail -20

# Buscar por IP
docker exec caddy-shared grep "186.158" /var/log/caddy/staging-backend.log

# Filtrar 500 errors
docker exec caddy-shared grep "status\":500" /var/log/caddy/staging-backend.log

5.3 Reiniciar Servicios por Ambiente

5.3.1 Reinicio Individual

Staging:

bash
docker restart gunei-frontend-staging
docker restart gunei-backend-staging

Production:

bash
docker restart gunei-frontend-production
docker restart gunei-backend-production

Infraestructura (afecta todos los ambientes):

bash
docker restart cloud-sql-proxy
docker restart caddy-shared

5.3.2 Reinicio con docker-compose

Staging:

bash
cd /opt/apps/gunei-erp/frontend/staging
docker compose restart frontend

cd /opt/apps/gunei-erp/backend/staging
docker compose restart backend

Production:

bash
cd /opt/apps/gunei-erp/frontend/production
docker compose restart frontend

cd /opt/apps/gunei-erp/backend/production
docker compose restart backend

5.3.3 Reinicio Completo del Stack

Orden recomendado (reinicio de todos los ambientes):

bash
# 1. Infraestructura primero
docker restart cloud-sql-proxy
sleep 5

# 2. Backends (ambos ambientes)
docker restart gunei-backend-staging
docker restart gunei-backend-production  # si está corriendo
sleep 5

# 3. Frontends (ambos ambientes)
docker restart gunei-frontend-staging
docker restart gunei-frontend-production  # si está corriendo

# 4. Caddy último (routing)
docker restart caddy-shared

Solo staging:

bash
docker restart cloud-sql-proxy
sleep 5
docker restart gunei-backend-staging
sleep 3
docker restart gunei-frontend-staging
docker restart caddy-shared

5.4 Rollback por Ambiente

Si un deployment falla, podés volver a una versión anterior del ambiente específico.

5.4.1 Rollback con Tag Específico

Staging:

bash
# Listar tags disponibles en GHCR
# https://github.com/orgs/Gunei-Dev/packages

# Pull un tag específico
docker pull ghcr.io/gunei-dev/gunei-erp-front:develop-abc1234

# Actualizar docker-compose.yml temporalmente
cd /opt/apps/gunei-erp/frontend/staging
nano docker-compose.yml
# Cambiar: image: ghcr.io/gunei-dev/gunei-erp-front:develop-abc1234

# Recrear contenedor
docker compose up -d frontend

Production (similar):

bash
cd /opt/apps/gunei-erp/frontend/production
# Seguir proceso similar con tags de production

5.5 Actualizar Configuraciones por Ambiente

5.5.1 Variables de Entorno (Backend)

Staging:

bash
# Editar .env de staging
cd /opt/apps/gunei-erp/backend/staging
nano .env

# Aplicar cambios (recrear contenedor)
docker compose up -d backend

# Verificar
docker exec gunei-backend-staging env | grep NUEVA_VARIABLE

Production:

bash
cd /opt/apps/gunei-erp/backend/production
nano .env
docker compose up -d backend
docker exec gunei-backend-production env | grep NUEVA_VARIABLE

5.5.2 Variables de Entorno (Frontend)

Staging:

bash
# Editar docker-compose.yml
cd /opt/apps/gunei-erp/frontend/staging
nano docker-compose.yml

# Modificar sección environment:
# environment:
#   - NUEVA_VAR=valor

# Aplicar cambios
docker compose up -d frontend

# Verificar
docker inspect gunei-frontend-staging | grep -A 10 Env

Production:

bash
cd /opt/apps/gunei-erp/frontend/production
nano docker-compose.yml
docker compose up -d frontend
docker inspect gunei-frontend-production | grep -A 10 Env

5.5.3 Caddyfile (afecta todos los ambientes)

bash
# Editar Caddyfile
nano /opt/infrastructure/caddy/Caddyfile

# Reload sin downtime
docker exec caddy-shared caddy reload --config /etc/caddy/Caddyfile

# Si hay errores, verificar sintaxis
docker exec caddy-shared caddy validate --config /etc/caddy/Caddyfile

# Si el reload falla, reiniciar
docker restart caddy-shared

5.6 Ver Estado del Sistema por Ambiente

5.6.1 Todos los Contenedores

bash
# Ver todos los contenedores
docker ps

# Ver con formato custom
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

# Ver solo servicios de Gunei
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "gunei|caddy|cloud-sql"

# Ver por ambiente
docker ps | grep staging
docker ps | grep production

5.6.2 Recursos (CPU, RAM, Network)

bash
# Stats en tiempo real (todos los ambientes)
docker stats

# Stats sin stream (una vez)
docker stats --no-stream

# Stats de un contenedor específico
docker stats gunei-frontend-staging --no-stream
docker stats gunei-backend-staging --no-stream

# Comparar ambos ambientes
docker stats gunei-frontend-staging gunei-frontend-production --no-stream

5.6.3 Estado de la Red

bash
# Ver la red gunei-network
docker network inspect gunei-network

# Ver qué contenedores están en la red
docker network inspect gunei-network | grep -A 3 "Containers"

# Verificar conectividad entre servicios
docker exec gunei-frontend-staging ping -c 3 gunei-backend-staging
docker exec gunei-backend-staging ping -c 3 cloud-sql-proxy

5.6.4 Disco y Volúmenes

bash
# Espacio usado por Docker
docker system df

# Espacio por contenedor
docker ps --size

# Espacio en volúmenes
docker volume ls
docker volume inspect postgres_data

# Espacio en host
df -h /opt/infrastructure
df -h /opt/apps
df -h /var/lib/docker

6. Gestión de Ambientes

6.1 Estrategia de Ambientes

Development (Local) → Staging (VPS) → Production (VPS)
         ↓                  ↓                  ↓
   Developer Laptop    Ambiente de prueba  Ambiente live

Staging:

  • Para testing y validación
  • Deploy automático desde branch develop
  • Puede tener downtime
  • Database: erp-dev (GCP Cloud SQL)
  • URLs: gunei.xyz, api.gunei.xyz

Production:

  • Para usuarios reales
  • Deploy automático desde branch main (o manual)
  • Zero-downtime deployments
  • Database: erp (GCP Cloud SQL)
  • URLs: prod.gunei.xyz, api-prod.gunei.xyz (pendiente configurar)

6.2 Promover de Staging a Production

6.2.1 Workflow Recomendado

bash
# 1. Validar que staging funciona correctamente
curl https://gunei.xyz/health
curl https://api.gunei.xyz/status

# 2. Mergear develop a main (con PR en GitHub)
git checkout main
git merge develop
git push origin main

# 3. Esto triggerea CI/CD de production automáticamente
# O deploy manual si no está configurado CI/CD

# 4. Migrar datos si es necesario
# (Copiar data de staging a production)

6.2.2 Migración de Data entre Ambientes

Copiar data de staging a production:

bash
# 1. Crear backup de staging (via backend container)
docker exec gunei-backend-staging pg_dump -h cloud-sql-proxy -p 5450 -U dba_dev erp-dev > staging_snapshot.sql

# 2. Para operaciones de recreación de DB, usar GCP Console o gcloud CLI
# https://console.cloud.google.com/sql/instances/gunei-erp

# 3. Restaurar snapshot en production (via backend container)
docker exec -i gunei-backend-production psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp < staging_snapshot.sql

# 4. Verificar
docker exec gunei-backend-production psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp -c "SELECT COUNT(*) FROM users;"

6.3 Testing entre Ambientes

6.3.1 Smoke Tests

Staging:

bash
# Backend
curl https://api.gunei.xyz/status
curl https://api.gunei.xyz/ping

# Frontend
curl https://gunei.xyz/health

# Database (via backend container)
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "SELECT 1;"

Production (cuando esté activo):

bash
# Backend
curl https://api-prod.gunei.xyz/status
curl https://api-prod.gunei.xyz/ping

# Frontend
curl https://prod.gunei.xyz/health

# Database (via backend container)
docker exec gunei-backend-production psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp -c "SELECT 1;"

6.3.2 Integration Tests

bash
# Test completo del flujo staging
# 1. Frontend health
test_frontend=$(curl -s https://gunei.xyz/health | jq -r .status)

# 2. Backend health
test_backend=$(curl -s https://api.gunei.xyz/status | jq -r .status)

# 3. Database connectivity
docker exec gunei-backend-staging psql $DATABASE_URL -c "SELECT 1;"

# 4. Resultado
if [ "$test_frontend" = "healthy" ] && [ "$test_backend" = "ok" ]; then
  echo "✅ Staging environment OK"
else
  echo "❌ Staging environment FAIL"
fi

6.4 Aislamiento entre Ambientes

Garantías de aislamiento:

  1. Database: Databases completamente separadas en GCP Cloud SQL

    • erp-dev (staging) vs erp (production)
    • Mismo usuario (dba_dev) pero databases aisladas
    • No hay foreign keys entre databases
  2. Containers: Contenedores independientes

    • gunei-backend-staging vs gunei-backend-production
    • Puertos diferentes en host
    • Logs separados
  3. Configuración: Variables de entorno por ambiente

    • .env separados
    • API keys diferentes (opcional)
    • Secrets independientes
  4. Networking: Misma red pero comunicación controlada

    • Todos en gunei-network
    • Routing por Caddy según dominio
    • No hay comunicación cross-environment

7. Monitoreo y Alertas

7.1 Scripts de Monitoreo Actualizados

El VPS tiene scripts de monitoreo en /root/scripts/ actualizados para multi-ambiente:

7.1.1 monitor-logs.sh (actualizado)

bash
#!/bin/bash

echo "==================================="
echo "🔍 Gunei ERP - Monitor de Logs"
echo "==================================="
echo ""

# Función para mostrar logs con colores
show_logs() {
    service=$1
    title=$2
    echo "📦 === $title ==="
    if docker ps | grep -q $service; then
        docker logs --tail 20 $service 2>&1 | tail -10
    else
        echo "⚠️  Container $service no está corriendo"
    fi
    echo ""
}

# Infraestructura
show_logs "cloud-sql-proxy" "Cloud SQL Proxy"
show_logs "caddy-shared" "Caddy Shared"

# Staging
echo "🔷 === STAGING ==="
show_logs "gunei-backend-staging" "Backend Staging"
show_logs "gunei-frontend-staging" "Frontend Staging"

# Production (si existe)
if docker ps | grep -q "gunei-backend-production"; then
    echo "🔶 === PRODUCTION ==="
    show_logs "gunei-backend-production" "Backend Production"
    show_logs "gunei-frontend-production" "Frontend Production"
fi

# Estado de los containers
echo "📊 === Estado de Containers ==="
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo ""

# Uso de recursos
echo "💾 === Uso de Recursos ==="
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

Uso:

bash
/root/scripts/monitor-logs.sh

7.1.2 health-check.sh (actualizado)

bash
#!/bin/bash

echo "🏥 Health Check - Gunei ERP (Multi-Environment)"
echo "================================================"
echo ""

# Colores
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

# Función para verificar un servicio
check_service() {
    service=$1
    url=$2

    if curl -f -s -o /dev/null "$url"; then
        echo -e "${GREEN}$service: OK${NC}"
        return 0
    else
        echo -e "${RED}$service: FAIL${NC}"
        return 1
    fi
}

# Infraestructura
echo "🏗️  Infraestructura:"
if docker ps | grep -q cloud-sql-proxy; then
    echo -e "${GREEN}✅ Cloud SQL Proxy: OK${NC}"
else
    echo -e "${RED}❌ Cloud SQL Proxy: FAIL${NC}"
fi

# Staging
echo ""
echo "🔷 Staging Environment:"
check_service "Backend Staging /status" "http://localhost:3000/status"
check_service "Frontend Staging /health" "http://localhost:3001/health"
check_service "HTTPS api.gunei.xyz" "https://api.gunei.xyz/status"
check_service "HTTPS gunei.xyz" "https://gunei.xyz/health"

# Production (si existe)
if docker ps | grep -q "gunei-backend-production"; then
    echo ""
    echo "🔶 Production Environment:"
    check_service "Backend Production /status" "http://localhost:3100/status"
    check_service "Frontend Production /health" "http://localhost:3101/health"
    # URLs públicas cuando estén configuradas:
    # check_service "HTTPS api-prod.gunei.xyz" "https://api-prod.gunei.xyz/status"
    # check_service "HTTPS prod.gunei.xyz" "https://prod.gunei.xyz/health"
fi

# Database connectivity (via backend containers)
echo ""
echo "🗄️  Database Connectivity (GCP Cloud SQL):"
if docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "SELECT 1;" > /dev/null 2>&1; then
    echo -e "${GREEN}✅ DB Staging (erp-dev): OK${NC}"
else
    echo -e "${RED}❌ DB Staging (erp-dev): FAIL${NC}"
fi

if docker ps | grep -q "gunei-backend-production"; then
    if docker exec gunei-backend-production psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp -c "SELECT 1;" > /dev/null 2>&1; then
        echo -e "${GREEN}✅ DB Production (erp): OK${NC}"
    else
        echo -e "${RED}❌ DB Production (erp): FAIL${NC}"
    fi
else
    echo -e "${YELLOW}⚠️  DB Production: Backend no deployado${NC}"
fi

# Verificar containers corriendo
echo ""
echo "📦 Docker Containers:"
docker ps --format "table {{.Names}}\t{{.Status}}"

# Uso de disco
echo ""
echo "💾 System Resources:"
df -h / | tail -1 | awk '{print "Disk: "$3" / "$2" ("$5" used)"}'
free -h | grep Mem | awk '{print "RAM:  "$3" / "$2" (used/total)"}'

Uso:

bash
/root/scripts/health-check.sh

7.1.3 alert-check.sh (actualizado)

bash
#!/bin/bash

LOG_FILE="/var/log/gunei-health.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')

# Ejecutar health check y capturar salida
output=$(/root/scripts/health-check.sh 2>&1)

# Buscar FAILs
if echo "$output" | grep -q "FAIL"; then
    echo "[$DATE] ❌ ALERT: Service failure detected!" >> $LOG_FILE
    echo "$output" >> $LOG_FILE
    echo "---" >> $LOG_FILE
    
    # TODO: Enviar notificación Discord aquí
    # curl -X POST "$DISCORD_WEBHOOK" ...
else
    echo "[$DATE] ✅ All services OK" >> $LOG_FILE
fi

Setup automático con cron:

bash
# Editar crontab
crontab -e

# Agregar línea para ejecutar cada 5 minutos
*/5 * * * * /root/scripts/alert-check.sh

7.2 Notificaciones Discord (sin cambios)

Las notificaciones Discord funcionan igual que antes:

  • ✅ GitHub Actions (deployments)
  • ⏳ Pendiente: Alertas automáticas del VPS

7.3 Health Checks de Docker

Cada servicio tiene healthchecks configurados que funcionan por ambiente:

Staging:

yaml
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:3001/health"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 40s

Production:

yaml
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:3001/health"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 40s

Ver health status:

bash
# Ver todos los contenedores con health
docker ps

# Ver health de un contenedor específico
docker inspect gunei-frontend-staging --format='{{.State.Health.Status}}'
docker inspect gunei-backend-staging --format='{{.State.Health.Status}}'

# Ver health de production (cuando esté activo)
docker inspect gunei-frontend-production --format='{{.State.Health.Status}}'
docker inspect gunei-backend-production --format='{{.State.Health.Status}}'

# Ver historial de health checks
docker inspect gunei-frontend-staging --format='{{json .State.Health}}' | jq

8. Troubleshooting

8.1 Problemas Específicos por Ambiente

8.1.1 "Container keeps restarting" - Staging

Síntomas:

bash
docker ps
# STATUS: Restarting (1) Less than a second ago  gunei-backend-staging

Diagnóstico:

bash
# Ver logs
docker logs gunei-backend-staging --tail 100

# Ver últimos restarts
docker inspect gunei-backend-staging --format='{{json .State}}' | jq

# Errores comunes:
# - Port 3000 already in use (conflicto con otra cosa)
# - Missing environment variable
# - Database connection failed (credenciales incorrectas)
# - Permission denied

Soluciones:

bash
# Si el puerto está en uso
netstat -tlnp | grep :3000
kill <PID>

# Si falta variable de entorno
docker inspect gunei-backend-staging | grep -A 10 Env
# Agregar en /opt/apps/gunei-erp/backend/staging/.env

# Si la DB no responde
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "SELECT 1;"

8.1.2 "502 Bad Gateway" - Problemas de Routing

Síntomas:

  • Browser muestra "502 Bad Gateway"
  • Caddy está corriendo pero no puede alcanzar backend/frontend

Diagnóstico:

bash
# Verificar que el servicio está corriendo
docker ps | grep gunei-frontend-staging

# Test desde Caddy (nombre de container correcto)
docker exec caddy-shared wget -O- http://gunei-frontend-staging:3001/health
docker exec caddy-shared wget -O- http://gunei-backend-staging:3000/status

# Ver logs de Caddy
docker logs caddy-shared --tail 50 | grep -i error

Soluciones:

bash
# Si el contenedor no está corriendo
docker start gunei-frontend-staging

# Si está corriendo pero no responde
docker restart gunei-frontend-staging

# Verificar la red
docker network inspect gunei-network | grep gunei-frontend-staging

# Verificar el Caddyfile
docker exec caddy-shared cat /etc/caddy/Caddyfile | grep -A 5 "gunei.xyz"

8.1.3 "Database Connection Failed" - Problemas de Cloud SQL Proxy

Síntomas:

  • Backend logs muestran "connection refused" o "authentication failed"
  • Endpoints devuelven 500 Internal Server Error

Diagnóstico:

bash
# Verificar cloud-sql-proxy está corriendo
docker ps | grep cloud-sql-proxy

# Ver logs del proxy
docker logs cloud-sql-proxy --tail 50

# Test de conexión desde backend staging
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "SELECT 1;"

# Verificar que el proxy está escuchando
docker exec cloud-sql-proxy netstat -tlnp | grep 5450

Soluciones:

bash
# Si cloud-sql-proxy no está corriendo
cd /opt/infrastructure/cloud-sql-proxy
docker compose up -d

# Verificar credenciales en .env
cat /opt/apps/gunei-erp/backend/staging/.env | grep DATABASE_URL

# Formato esperado:
# DATABASE_URL=postgresql://dba_dev:PASSWORD@cloud-sql-proxy:5450/erp-dev

# Si hay problemas con credentials.json
ls -la /opt/infrastructure/cloud-sql-proxy/credentials.json

# Verificar logs por errores de autenticación
docker logs cloud-sql-proxy | grep -i "error\|auth\|permission"

# Restart del proxy
docker restart cloud-sql-proxy

8.1.4 Conflictos entre Staging y Production

Síntomas:

  • Un ambiente funciona pero el otro no
  • Puertos en conflicto
  • Conexiones a database incorrecta

Diagnóstico:

bash
# Ver puertos de ambos ambientes
docker ps --format "table {{.Names}}\t{{.Ports}}"

# Resultado esperado:
# gunei-backend-staging    0.0.0.0:3000->3000/tcp
# gunei-backend-production 0.0.0.0:3100->3000/tcp
# gunei-frontend-staging   0.0.0.0:3001->3001/tcp
# gunei-frontend-production 0.0.0.0:3101->3001/tcp

# Verificar DATABASE_URL de cada ambiente
docker exec gunei-backend-staging env | grep DATABASE_URL
docker exec gunei-backend-production env | grep DATABASE_URL

Soluciones:

bash
# Si hay conflicto de puertos, parar el servicio que no usas
docker stop gunei-backend-staging  # si querés correr solo production

# O cambiar el puerto en docker-compose.yml
cd /opt/apps/gunei-erp/backend/production
nano docker-compose.yml
# ports:
#   - "3100:3000"  # Asegurar que es diferente

# Verificar que cada ambiente apunta a su database correcta
# Staging debe tener: erp-dev
# Production debe tener: erp

8.2 Comandos de Debugging por Ambiente

8.2.1 Inspeccionar Contenedores

bash
# Ver toda la configuración de staging
docker inspect gunei-frontend-staging

# Ver solo variables de entorno
docker inspect gunei-frontend-staging --format='{{json .Config.Env}}' | jq

# Ver solo networking
docker inspect gunei-frontend-staging --format='{{json .NetworkSettings.Networks}}' | jq

# Ver health status
docker inspect gunei-frontend-staging --format='{{json .State.Health}}' | jq

# Comparar configuración entre ambientes
diff <(docker inspect gunei-frontend-staging) <(docker inspect gunei-frontend-production)

8.2.2 Entrar a un Contenedor

bash
# Staging
docker exec -it gunei-frontend-staging sh
docker exec -it gunei-backend-staging sh

# Production
docker exec -it gunei-frontend-production sh
docker exec -it gunei-backend-production sh

# Cloud SQL Proxy (shell access)
docker exec -it cloud-sql-proxy sh

# Como root
docker exec -it -u root gunei-frontend-staging sh

# Ejecutar comando único
docker exec gunei-backend-staging ls -la /app
docker exec gunei-backend-staging cat /app/package.json
docker exec gunei-backend-staging env

8.2.3 Verificar Conectividad entre Ambientes

bash
# Frontend staging → Backend staging
docker exec gunei-frontend-staging curl http://gunei-backend-staging:3000/status

# Backend staging → Cloud SQL Proxy
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "SELECT 1;"

# Backend production → Cloud SQL Proxy (mismo proxy, diferente DB)
docker exec gunei-backend-production psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp -c "SELECT 1;"

# Verificar DNS interno
docker exec gunei-frontend-staging nslookup cloud-sql-proxy
docker exec gunei-backend-staging getent hosts cloud-sql-proxy

8.3 Recovery Procedures por Ambiente

8.3.1 Recovery de Staging

bash
# 1. Verificar infraestructura
docker ps | grep -E "cloud-sql-proxy|caddy-shared"

# 2. Reiniciar servicios de staging en orden
docker restart cloud-sql-proxy
sleep 5

docker restart gunei-backend-staging
sleep 5

docker restart gunei-frontend-staging
docker restart caddy-shared

# 3. Verificar
/root/scripts/health-check.sh

8.3.2 Recovery de Production (cuando esté activo)

bash
# 1. Verificar infraestructura (igual que staging)
docker ps | grep -E "cloud-sql-proxy|caddy-shared"

# 2. Reiniciar servicios de production en orden
docker restart cloud-sql-proxy
sleep 5

docker restart gunei-backend-production
sleep 5

docker restart gunei-frontend-production
docker restart caddy-shared

# 3. Verificar
/root/scripts/health-check.sh

8.3.3 Recovery Completo de Todo el Sistema

bash
# CUIDADO: Esto afecta AMBOS ambientes

# 1. Verificar Docker daemon
systemctl status docker
systemctl restart docker

# 2. Iniciar infraestructura
docker start cloud-sql-proxy
sleep 5
docker start caddy-shared
sleep 5

# 3. Iniciar staging
docker start gunei-backend-staging
sleep 3
docker start gunei-frontend-staging
sleep 3

# 4. Iniciar production (si está deployado)
docker start gunei-backend-production
sleep 3
docker start gunei-frontend-production

# 5. Verificar
docker ps
/root/scripts/health-check.sh

9. Apéndices

9.1 Cheat Sheet: Docker (sin cambios)

bash
# === CONTAINERS ===

# Listar contenedores corriendo
docker ps

# Listar todos (incluidos detenidos)
docker ps -a

# Ver logs
docker logs <container> -f

# Iniciar/detener
docker start <container>
docker stop <container>
docker restart <container>

# Eliminar
docker rm <container>
docker rm -f <container>  # Forzar

# Ejecutar comando
docker exec <container> <command>
docker exec -it <container> sh

# Ver stats
docker stats <container> --no-stream

# === IMAGES ===

# Listar imágenes
docker images

# Pull image
docker pull <image>:<tag>

# Eliminar image
docker rmi <image>

# Construir image
docker build -t <name>:<tag> .

# Tag image
docker tag <source> <target>

# === COMPOSE ===

# Iniciar servicios
docker compose up -d

# Detener servicios
docker compose down

# Ver logs
docker compose logs -f <service>

# Recrear un servicio
docker compose up -d --force-recreate <service>

# Ver servicios
docker compose ps

# === NETWORKS ===

# Listar redes
docker network ls

# Inspeccionar red
docker network inspect <network>

# === CLEANUP ===

# Limpiar containers detenidos
docker container prune

# Limpiar imágenes sin usar
docker image prune

# Limpiar todo (cuidado!)
docker system prune -a

# Ver espacio usado
docker system df

9.2 Cheat Sheet: Caddy (actualizado)

bash
# === CONFIGURATION ===

# Recargar configuración (nombre actualizado)
docker exec caddy-shared caddy reload --config /etc/caddy/Caddyfile

# Validar configuración
docker exec caddy-shared caddy validate --config /etc/caddy/Caddyfile

# Formatear Caddyfile
docker exec caddy-shared caddy fmt --overwrite /etc/caddy/Caddyfile

# === CERTIFICATES ===

# Listar certificados
docker exec caddy-shared ls -la /data/caddy/certificates/

# Ver detalles de certificado
docker exec caddy-shared cat /data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/gunei.xyz/gunei.xyz.json

# === LOGS ===

# Ver logs del contenedor
docker logs caddy-shared -f

# Ver access logs por ambiente
docker exec caddy-shared cat /var/log/caddy/staging-frontend.log
docker exec caddy-shared cat /var/log/caddy/staging-backend.log
docker exec caddy-shared cat /var/log/caddy/production-frontend.log  # cuando exista
docker exec caddy-shared cat /var/log/caddy/production-backend.log   # cuando exista

# Tail logs
docker exec caddy-shared tail -f /var/log/caddy/staging-backend.log

# === TESTING ===

# Test health desde Caddy (nombres actualizados)
docker exec caddy-shared wget -O- http://gunei-frontend-staging:3001/health
docker exec caddy-shared wget -O- http://gunei-backend-staging:3000/status

# === TROUBLESHOOTING ===

# Ver configuración cargada
docker exec caddy-shared caddy run --config /etc/caddy/Caddyfile --dry-run

9.3 Cheat Sheet: Cloud SQL (vía Proxy)

bash
# === CONEXIONES (desde containers en gunei-network) ===

# Conectarse a staging (erp-dev)
docker exec -it gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev

# Conectarse a production (erp)
docker exec -it gunei-backend-production psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp

# === QUERIES ADMINISTRATIVAS ===

# Listar databases
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "\l"

# Ver tablas de staging
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "\dt"

# Ver tablas de production
docker exec gunei-backend-production psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp -c "\dt"

# Ejecutar query en staging
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "SELECT COUNT(*) FROM users;"

# Ver conexiones activas
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "SELECT datname, usename, application_name, state FROM pg_stat_activity;"

# === BACKUPS ===

# GCP maneja backups automáticos - ver en GCP Console
# https://console.cloud.google.com/sql/instances/gunei-erp/backups

# Dump local de staging (via proxy)
docker exec gunei-backend-staging pg_dump -h cloud-sql-proxy -p 5450 -U dba_dev erp-dev > backup_staging_$(date +%Y%m%d).sql

# Dump local de production (via proxy)
docker exec gunei-backend-production pg_dump -h cloud-sql-proxy -p 5450 -U dba_dev erp > backup_production_$(date +%Y%m%d).sql

# === HEALTH CHECKS ===

# Verificar cloud-sql-proxy está corriendo
docker ps | grep cloud-sql-proxy

# Ver logs del proxy
docker logs cloud-sql-proxy -f

# Test de conexión staging
docker exec gunei-backend-staging psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp-dev -c "SELECT 1;"

# Test de conexión production
docker exec gunei-backend-production psql -h cloud-sql-proxy -p 5450 -U dba_dev -d erp -c "SELECT 1;"

# === CLOUD SQL PROXY ===

# Restart del proxy
docker restart cloud-sql-proxy

# Ver estado del proxy
docker exec cloud-sql-proxy ps aux | grep cloud-sql-proxy

# Ver logs completos
docker logs cloud-sql-proxy --tail 100

9.4 Variables de Entorno por Ambiente

9.4.1 Frontend Staging

VariableValorDescripción
NODE_ENVstagingModo de ejecución
API_BASE_URLhttps://api.gunei.xyzURL del backend (server-side)
PUBLIC_BASE_URLhttps://api.gunei.xyzURL del backend (client-side)
PORT3001Puerto del servidor
ORIGINhttps://gunei.xyzOrigen para CORS
TZAmerica/Argentina/Buenos_AiresTimezone

9.4.2 Frontend Production

VariableValorDescripción
NODE_ENVproductionModo de ejecución
API_BASE_URLhttps://api-prod.gunei.xyzURL del backend (server-side)
PUBLIC_BASE_URLhttps://api-prod.gunei.xyzURL del backend (client-side)
PORT3001Puerto interno (mapeado a 3101)
ORIGINhttps://prod.gunei.xyzOrigen para CORS
TZAmerica/Argentina/Buenos_AiresTimezone

9.4.3 Backend Staging

Variables en /opt/apps/gunei-erp/backend/staging/.env:

bash
# Database - GCP Cloud SQL via Proxy
DATABASE_URL=postgresql://dba_dev:PASSWORD@cloud-sql-proxy:5450/erp-dev

# Application
NODE_ENV=staging
PORT=3000

# AFIP (ejemplos)
AFIP_KEY=...
AFIP_SECRET=...

# JWT
JWT_SECRET=...

9.4.4 Backend Production

Variables en /opt/apps/gunei-erp/backend/production/.env:

bash
# Database - GCP Cloud SQL via Proxy
DATABASE_URL=postgresql://dba_dev:PASSWORD@cloud-sql-proxy:5450/erp

# Application
NODE_ENV=production
PORT=3000                                # Puerto interno (mapeado a 3100)

# AFIP
AFIP_KEY=...
AFIP_SECRET=...

# JWT
JWT_SECRET=...

9.5 Puertos y Endpoints por Ambiente

9.5.1 Staging

ServicioPuerto InternoPuerto ExpuestoURL Pública
Caddy Shared80, 44380, 443-
Cloud SQL Proxy5450- (solo gunei-network)-
Frontend Staging30013001https://gunei.xyz
Backend Staging30003000https://api.gunei.xyz

Endpoints importantes:

EndpointMétodoDescripción
https://gunei.xyz/healthGETFrontend staging health check
https://api.gunei.xyz/statusGETBackend staging health check
https://api.gunei.xyz/pingGETBackend staging ping

9.5.2 Production (cuando esté activo)

ServicioPuerto InternoPuerto ExpuestoURL Pública
Caddy Shared80, 44380, 443-
Cloud SQL Proxy5450- (solo gunei-network)-
Frontend Production30013101https://prod.gunei.xyz (pendiente)
Backend Production30003100https://api-prod.gunei.xyz (pendiente)

Endpoints importantes:

EndpointMétodoDescripción
https://prod.gunei.xyz/healthGETFrontend production health check
https://api-prod.gunei.xyz/statusGETBackend production health check
https://api-prod.gunei.xyz/pingGETBackend production ping

9.6 Mapeo de Migración (Referencia)

Estructura anterior → Estructura nueva:

AnteriorNuevoNotas
/opt/caddy//opt/infrastructure/caddy/Ahora compartido
/opt/postgres/-Eliminado, migrado a GCP Cloud SQL
/opt/infrastructure/postgres//opt/infrastructure/cloud-sql-proxy/PostgreSQL local → GCP Cloud SQL
/root/gunei-erp-back//opt/apps/gunei-erp/backend/staging/Backend staging
/opt/frontend//opt/apps/gunei-erp/frontend/staging/Frontend staging
-/opt/apps/gunei-erp/backend/production/Nuevo (production)
-/opt/apps/gunei-erp/frontend/production/Nuevo (production)

Contenedores anteriores → Contenedores nuevos:

AnteriorNuevoNotas
caddycaddy-sharedMismo Caddy, nombre actualizado
postgres-sharedcloud-sql-proxyPostgreSQL local → GCP Cloud SQL via proxy
gunei-backendgunei-backend-stagingBackend en ambiente staging
gunei-frontendgunei-frontend-stagingFrontend en ambiente staging
-gunei-backend-productionNuevo (production)
-gunei-frontend-productionNuevo (production)

Databases anteriores → Databases nuevas:

AnteriorNuevoNotas
gunei_erp_staging (postgres-shared:5433)erp-dev (GCP Cloud SQL via proxy:5450)Migrado a GCP
gunei_erp_production (postgres-shared:5433)erp (GCP Cloud SQL via proxy:5450)Migrado a GCP

Infraestructura de Base de Datos:

AnteriorNuevoNotas
PostgreSQL 17 local (VPS)GCP Cloud SQL (PostgreSQL)Backups automáticos por GCP
Backups manuales/cronBackups automáticos GCPConfigurado en GCP Console
Puerto 5433Puerto 5450 (via proxy)Cloud SQL Proxy en gunei-network

Red anterior → Red nueva:

AnteriorNuevoNotas
red-npmgunei-networkMisma funcionalidad, nombre enterprise

9.7 Contactos y Recursos (sin cambios)

9.7.1 Equipo

  • Adrian Gallegos: Project Manager
  • Miguel Culaciati: Frontend Developer
  • Facundo Sardi: Backend Developer
  • Miguel Culaciati: Devops

9.7.2 Recursos Externos

Documentación:

GitHub Repos:

Servicios:

9.7.3 Próximos Pasos

  1. Activar Production: Configurar dominios y deployar production
  2. Grafana + Prometheus: Métricas visuales avanzadas
  3. Migra + DbMate workflow: Documentar proceso completo de migraciones
  4. Security hardening: fail2ban, firewall rules, SSH hardening
  5. Email notifications: Alternativa/complemento a Discord
  6. GCP Monitoring: Integrar alertas de Cloud SQL con Discord

🎉 Fin del Runbook

Versión: 2.1 - GCP Cloud SQL Migration Fecha: 22 Enero 2026 Autor: Mikle + Claude Proyecto: Gunei ERP

Cambios principales en v2.1:

  • ✅ Migración de PostgreSQL local a GCP Cloud SQL
  • ✅ Cloud SQL Proxy como container en gunei-network
  • ✅ Backups automáticos gestionados por GCP
  • ✅ Eliminación de postgres-shared container
  • ✅ Actualización de scripts de monitoreo para cloud-sql-proxy
  • ✅ Nueva documentación de troubleshooting para Cloud SQL

Cambios anteriores (v2.0):

  • ✅ Arquitectura por ambientes (staging/production)
  • ✅ Estructura de directorios enterprise
  • ✅ Red Docker unificada (gunei-network)
  • ✅ Scripts de monitoreo multi-ambiente
  • ✅ Documentación completa de cada ambiente