⚙ Porter PaaS · Kubernetes en tu nube

NutriFlow — Guía de Despliegue con Porter

De Heroku Eco a AWS EKS gestionado con experiencia git push. Infraestructura en tu cuenta, datos bajo tu control, escala automática incluida.

Stack: Node.js + React + Prisma
Cloud: AWS (EKS + RDS + ElastiCache)
3 devs · Sin expertise Kubernetes
Migración: Heroku → Porter
Arquitectura en AWS con Porter

Porter provisiona un cluster EKS en tu cuenta AWS y gestiona los despliegues. Tu equipo solo hace git push o lanza el CLI.

GitHub repo → push to main → GitHub Actions Porter CLI Action
Porter Dashboard / API → gestiona → AWS EKS (Kubernetes)
web (API)   worker (PDFs)   cron (backup)
RDS PostgreSQL 16    ElastiCache Redis 7
Todo en tu cuenta AWS · Tu CIF en la factura · Tus datos bajo tu control
Setup inicial (una sola vez)

Configura Porter conectado a tu cuenta AWS. Porter provisiona el cluster EKS automáticamente.

1
Instalar Porter CLI y autenticarse
Instala via Homebrew y autentica con tu cuenta de Porter dashboard
2
Conectar cuenta AWS en el dashboard
dashboard.porter.run → Settings → Cloud Providers → Add AWS → IAM Role con permisos EKS, RDS, ElastiCache
3
Porter crea el cluster EKS (10-15 min)
Elige región eu-west-1 (Irlanda) para cumplimiento GDPR de datos de salud
4
Configurar contexto del proyecto en CLI
Vincula tu CLI local al proyecto y cluster creados
5
Provisionar addons (RDS + Redis) y desplegar la app
Una vez el cluster está listo, crea los addons y despliega con porter app update nutriflow-api
Terminal — Setup inicial bash
# 1. Instalar Porter CLI
brew install porter-dev/porter/porter

# 2. Autenticarse
porter auth login

# 3. Configurar proyecto y cluster (obtenlos del dashboard)
porter config set-project --project nutriflow-prod-01
porter config set-cluster --cluster eks-eu-west-1-nutriflow

# 4. Provisionar base de datos PostgreSQL 16 en RDS propio
porter addon create postgresql \
  --name nutriflow-main-db \
  --version 16 \
  --plan db.t4g.medium \
  --storage 100

# 5. Provisionar Redis para caché de sesiones
porter addon create redis \
  --name nutriflow-cache \
  --version 7 \
  --plan cache.t4g.small

# 6. Inyectar connection strings como secretos
porter app env set nutriflow-api \
  DATABASE_URL=$(porter addon get nutriflow-main-db --connection-string)
porter app env set nutriflow-api \
  REDIS_URL=$(porter addon get nutriflow-cache --connection-string)

# 7. Primer despliegue
porter app update nutriflow-api
porter.yaml — Producción completa

Configuración declarativa con API web (autoscaling), worker de PDFs y cron de backup diario.

Infraestructura como código. Todo lo que configures en porter.yaml se versiona en git. Evita cambios sólo desde el dashboard para mantener reproducibilidad.
porter.yaml yaml
version: v2

apps:
  nutriflow-api:
    build:
      method: pack          # Cloud Native Buildpacks — sin Dockerfile necesario
      context: .
      builder: heroku/builder:22

    services:

      # ───── API Web (Express) ─────────────────────────────
      web:
        type: web
        port: 3000
        cpus: 0.5
        memory: 512Mi
        replicas:
          min: 2       # Alta disponibilidad: nunca menos de 2
          max: 10      # Escala hasta 10 en picos de uso
        autoscaling:
          enabled: true
          targetCPU: 60
          targetMemory: 70
        health_check:
          enabled: true
          path: /health
          initialDelaySeconds: 15
        domains:
          - name: api.nutriflow.app

      # ───── Worker generación de PDFs (CPU-intensivo) ─────
      pdf-worker:
        type: worker
        cpus: 1.5         # Más CPU: Puppeteer/WeasyPrint consume bastante
        memory: 1.5Gi
        replicas:
          min: 1
          max: 4
        autoscaling:
          enabled: true
          targetCPU: 70
        command: "node dist/workers/pdf-generator.js"

      # ───── Cron backup + resumen diario ──────────────────
      daily-backup:
        type: job
        cpus: 0.25
        memory: 256Mi
        command: "node dist/cron/backup-and-report.js"
        schedule: "0 2 * * *"     # 02:00 UTC (03:00 Madrid)
        timeout: 600              # 10 minutos máximo

    env:
      NODE_ENV: production
      LOG_LEVEL: info
      TZ: Europe/Madrid
      # Secretos — valores nunca en git, se inyectan desde Porter
      DATABASE_URL:
        secret: true
      REDIS_URL:
        secret: true
      JWT_SECRET:
        secret: true
      SENDGRID_API_KEY:
        secret: true
      S3_BUCKET_REPORTS:
        secret: true

    predeploy:
      - "npx prisma migrate deploy"   # Migraciones ANTES del nuevo pod
Preview Environments por Pull Request

Cada PR crea un entorno aislado con su propia base de datos para que QA valide antes del merge.

Flujo QA de NutriFlow: El nutricionista que hace de tester recibe la URL del entorno preview directamente en el comentario del PR. Valida los nuevos formularios de pacientes con datos de seed sin tocar producción.
porter.yaml — sección previews yaml
previews:
  enabled: true

  apps:
    nutriflow-api:
      build:
        method: pack
        builder: heroku/builder:22
      services:
        web:
          type: web
          port: 3000
          cpus: 0.25          # Recursos reducidos para previews
          memory: 256Mi
          replicas:
            min: 1
            max: 1
      predeploy:
        - "npx prisma migrate deploy"
        - "npx prisma db seed"   # Datos de ejemplo para QA

  addons:
    - type: postgresql
      name: preview-db
      plan: db.t4g.micro       # Instancia mínima, se destruye al cerrar PR
GitHub Actions — CI/CD Pipeline

Pipeline completo: tests, build y deploy automático a producción en push a main.

.github/workflows/deploy.yml yaml
name: Deploy NutriFlow a Porter
on:
  push:
    branches: [main]
  pull_request:
    types: [opened, synchronize, reopened]

jobs:

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - run: npm ci
      - run: npm test
      - run: npm run build

  deploy-production:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy a Porter (producción)
        uses: porter-dev/porter-cli-action@v0.1.0
        with:
          command: app update nutriflow-api
        env:
          PORTER_TOKEN: ${{ secrets.PORTER_TOKEN }}
          PORTER_PROJECT: ${{ secrets.PORTER_PROJECT }}
          PORTER_CLUSTER: ${{ secrets.PORTER_CLUSTER }}

  deploy-preview:
    needs: test
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy entorno preview para PR
        uses: porter-dev/porter-cli-action@v0.1.0
        with:
          command: preview update
        env:
          PORTER_TOKEN: ${{ secrets.PORTER_TOKEN }}
          PORTER_PROJECT: ${{ secrets.PORTER_PROJECT }}
          PORTER_CLUSTER: ${{ secrets.PORTER_CLUSTER }}
Comandos de operación diaria

Equivalentes a Heroku para el equipo de NutriFlow, sin necesidad de saber Kubernetes.

Operación Heroku (antes) Porter (ahora) Estado
Desplegar git push heroku main porter app update nutriflow-api igual de simple
Ver logs heroku logs -t porter app logs nutriflow-api --service web -f tiempo real
Comando one-off heroku run node seed.js porter app run nutriflow-api -- node dist/seed.js idéntico
Escalar réplicas heroku ps:scale web=3 porter app update nutriflow-api --service web --replicas 3 más potente
Variables de entorno heroku config:set KEY=val porter app env set nutriflow-api KEY=val equivalente
Estado de la app heroku ps porter app get nutriflow-api más detallado
Terminal — Debugging y operaciones bash
# Ver estado de todos los servicios
porter app get nutriflow-api

# Seguir logs del worker de PDFs
porter app logs nutriflow-api --service pdf-worker --follow

# Ejecutar migración manual de emergencia
porter app run nutriflow-api -- npx prisma migrate deploy

# Abrir REPL en el entorno de producción
porter app run nutriflow-api -- node -e "require('./dist/repl')"

# Ver todos los addons y su estado
porter addon list

# Rotar un secreto
porter app env set nutriflow-api --secret JWT_SECRET=nuevovalor_aqui

# Listar entornos preview activos
porter preview list
Estimación de costes AWS mensuales

Todo corre en tu cuenta AWS. La factura llega de Amazon, no de Heroku. Monitorea en la consola AWS Cost Explorer.

Importante: Porter crea recursos reales en AWS. Activa alertas de presupuesto en AWS Budgets para no tener sorpresas. Recomendado: alerta a 150€/mes para NutriFlow en fase early.
Infraestructura base
EKS cluster (1 nodo t3.medium)~55€
RDS PostgreSQL 16 db.t4g.medium~45€
ElastiCache Redis cache.t4g.small~18€
Load Balancer ALB~16€
Porter plan (gestión)~10€
Total estimado ~144€/mes
Comparativa vs Heroku
Heroku Eco (actual)
~50€/mes
1 dyno web + 1 worker + Postgres mini
Porter + AWS (nuevo)
~144€/mes
HA, autoscaling, RDS propio, control total
Lo que ganas por +94€/mes:
✓ Alta disponibilidad (min 2 réplicas)
✓ Autoscaling real (hasta 10 pods)
✓ Datos en tu cuenta (compliance HIPAA-like)
✓ Preview environments para QA
✓ Sin límites de dyno hours
Checklist de migración Heroku → Porter
Antes de migrar
  • Exportar dump completo de Heroku Postgres
  • Listar todas las variables de entorno (heroku config)
  • Documentar addons activos (Scheduler, etc.)
  • Crear cuenta AWS y activar MFA
  • Elegir región eu-west-1 para cumplimiento GDPR
  • Configurar AWS Budgets alert a 200€
Durante y después
  • Crear porter.yaml y hacer primer despliegue en staging
  • Importar dump a RDS (pg_restore)
  • Verificar health check /health responde 200
  • Migrar DNS de api.nutriflow.app a Porter
  • Smoke test con Postman en producción nueva
  • Mantener Heroku activo 1 semana como fallback