Skip to content

Guía de Despliegue - Intranet Despertares

Modelo Híbrido (Este Proyecto)

La intranet usa arquitectura híbrida:

DóndeQué se despliega
AWSSolo Cognito, S3 y SES. IaC con Terraform o SST.
ServidorAPI Java (Spring Boot) y PostgreSQL. Deploy clásico (VPS/EC2, Docker, systemd, etc.).
  • Despliegue AWS: crear User Pool (Cognito), bucket S3 y configurar SES (dominio, envío).
  • Despliegue servidor: Java 17+, PostgreSQL, configurar perfiles Spring y env (Cognito, S3, SES, DATABASE_URL) y levantar la API (jar o contenedor).

El resto de esta guía (ambientes, CI/CD, dominios, costos) aplica adaptando: en AWS solo existen esos 3 servicios; no hay Lambda ni API Gateway.


Resumen

AspectoDetalle
AWS (solo 3 servicios)Cognito, S3, SES
Herramienta IaC AWSTerraform o SST v3
ServidorAPI Java (Spring Boot) + PostgreSQL (VPS/EC2)
CI/CDGitHub Actions (API + opcional Terraform/SST)
Ambientesdev, staging, production
Región AWSus-east-1 (recomendado para SES)
Costo estimadoAWS ~$2-3/mes + servidor ~$20-40/mes

¿Por qué SST o Terraform para AWS?

En este proyecto solo se despliegan en AWS: Cognito, S3 y SES (sin Lambda ni DynamoDB).

  • SST: Rápido para 3 recursos AWS; opcional si prefieren IaC en código.
  • Terraform: HCL, útil si ya lo usan para otros proyectos o quieren un solo lenguaje de IaC para servidor + AWS.

Para solo 3 servicios, ambos son válidos; elegir según preferencia del equipo.


Arquitectura de Ambientes

┌─────────────────────────────────────────────────────────────────────────┐
│                         AWS Account                                      │
│                                                                          │
│  ┌─────────────────────┐  ┌─────────────────────┐  ┌─────────────────┐  │
│  │   dev               │  │   staging           │  │   production    │  │
│  │   ────────────────  │  │   ────────────────  │  │   ────────────  │  │
│  │                     │  │                     │  │                 │  │
│  │  • Auto-deploy      │  │  • Deploy manual    │  │  • Deploy con   │  │
│  │    en cada push     │  │    o PR a staging   │  │    aprobación   │  │
│  │  • Datos de prueba  │  │  • Datos sanitized  │  │  • Datos reales │  │
│  │  • Sin protección   │  │  • Protección media │  │  • Full protect │  │
│  │  • Logs verbose     │  │  • Logs normales    │  │  • Logs mínimos │  │
│  │                     │  │                     │  │                 │  │
│  │  dev.despertares    │  │  staging.despertares│  │  despertares    │  │
│  │  .edu.pe            │  │  .edu.pe            │  │  .edu.pe        │  │
│  └─────────────────────┘  └─────────────────────┘  └─────────────────┘  │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Requisitos Previos

1. Cuenta AWS

bash
# Crear cuenta AWS (si no existe)
# https://aws.amazon.com/

# Crear usuario IAM para CI/CD
# Políticas requeridas:
# - AdministratorAccess (para desarrollo inicial)
# - O políticas específicas (ver sección de seguridad)

2. AWS CLI Configurado

bash
# Instalar AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Configurar credenciales
aws configure
# AWS Access Key ID: [tu-access-key]
# AWS Secret Access Key: [tu-secret-key]
# Default region name: us-east-1
# Default output format: json

# Verificar configuración
aws sts get-caller-identity

3. Java (backend)

bash
# Instalar OpenJDK 17+
sudo apt update
sudo apt install openjdk-17-jdk

# Verificar
java -version
javac -version

# Maven o Gradle (según el proyecto)
# Maven: sudo apt install maven
# Gradle: sdk install gradle  o  gradle wrapper en el repo

4. Node.js y pnpm (solo si se construye el frontend en el mismo repo/pipeline)

bash
# Instalar Node.js 20.x
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
npm install -g pnpm
node --version
pnpm --version

5. SST CLI (solo para desplegar recursos AWS)

bash
# SST se instala como dependencia del proyecto
# No requiere instalación global

Configuración Inicial del Proyecto

1. Clonar e Instalar

bash
# Clonar repositorio
git clone https://github.com/intik/despertares-intranet.git
cd despertares-intranet

# Instalar dependencias
pnpm install

2. Variables de Entorno

Crear archivo .env en la raíz (no commitear):

bash
# .env (local development)
AWS_PROFILE=despertares
AWS_REGION=us-east-1

# Opcionales para desarrollo
SUNAT_API_KEY=xxx
SUNAT_API_SECRET=xxx

3. Configuración SST por Ambiente

typescript
// sst.config.ts
export default $config({
  app(input) {
    return {
      name: "despertares",
      removal: input?.stage === "production" ? "retain" : "remove",
      protect: ["production"].includes(input?.stage),
      home: "aws",
      providers: {
        aws: {
          region: "us-east-1",
          // Profile diferente por ambiente (opcional)
          // profile: input?.stage === "production" ? "despertares-prod" : "despertares-dev",
        },
      },
    };
  },
  async run() {
    // ... infraestructura
  },
});

Despliegue Manual

Ambiente de Desarrollo

bash
# Desarrollo local con hot reload
pnpm sst dev

# Esto:
# 1. Despliega infraestructura a AWS (stage: dev por defecto)
# 2. Inicia servidor local que conecta con Lambda
# 3. Hot reload cuando cambias código
# 4. Logs en tiempo real

# Output esperado:
# ✓ Complete
# 
# API: https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com
# UserPoolId: us-east-1_XXXXXXXXX
# UserPoolClientId: xxxxxxxxxxxxxxxxxxxxxxxxxx

Ambiente de Staging

bash
# Desplegar a staging
pnpm sst deploy --stage staging

# Ver recursos desplegados
pnpm sst diff --stage staging

# Ver outputs
pnpm sst output --stage staging

Ambiente de Producción

bash
# Desplegar a producción (con protección)
pnpm sst deploy --stage production

# IMPORTANTE: En producción, SST:
# - Retiene recursos al eliminar (removal: "retain")
# - Protege contra eliminación accidental (protect: true)
# - Requiere confirmación para cambios destructivos

Eliminar Ambiente

bash
# Eliminar ambiente de desarrollo
pnpm sst remove --stage dev

# ⚠️ NUNCA hacer esto en producción sin backup
# pnpm sst remove --stage production  # PELIGROSO

CI/CD con GitHub Actions

Estructura de Workflows

.github/
└── workflows/
    ├── deploy-dev.yml       # Deploy automático a dev
    ├── deploy-staging.yml   # Deploy manual a staging
    ├── deploy-prod.yml      # Deploy con aprobación a prod
    └── pr-check.yml         # Validación en PRs

1. Deploy a Dev (Automático)

yaml
# .github/workflows/deploy-dev.yml
name: Deploy to Dev

on:
  push:
    branches:
      - develop
      - 'feature/**'

env:
  AWS_REGION: us-east-1

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    permissions:
      id-token: write
      contents: read
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN_DEV }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Deploy to Dev
        run: pnpm sst deploy --stage dev

      - name: Output URLs
        run: pnpm sst output --stage dev

2. Deploy a Staging (Manual)

yaml
# .github/workflows/deploy-staging.yml
name: Deploy to Staging

on:
  workflow_dispatch:
    inputs:
      confirm:
        description: 'Type "staging" to confirm deployment'
        required: true
  push:
    branches:
      - staging

env:
  AWS_REGION: us-east-1

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: github.event.inputs.confirm == 'staging' || github.event_name == 'push'
    
    permissions:
      id-token: write
      contents: read
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Run tests
        run: pnpm test

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN_STAGING }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Deploy to Staging
        run: pnpm sst deploy --stage staging

      - name: Output URLs
        run: pnpm sst output --stage staging

3. Deploy a Producción (Con Aprobación)

yaml
# .github/workflows/deploy-prod.yml
name: Deploy to Production

on:
  workflow_dispatch:
    inputs:
      confirm:
        description: 'Type "production" to confirm deployment'
        required: true

env:
  AWS_REGION: us-east-1

jobs:
  # Job 1: Validación
  validate:
    runs-on: ubuntu-latest
    steps:
      - name: Validate confirmation
        if: github.event.inputs.confirm != 'production'
        run: |
          echo "❌ Deployment cancelled. You must type 'production' to confirm."
          exit 1

  # Job 2: Tests
  test:
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Run tests
        run: pnpm test

      - name: Run lint
        run: pnpm lint

  # Job 3: Aprobación manual
  approval:
    needs: test
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Waiting for approval
        run: echo "Deployment approved"

  # Job 4: Deploy
  deploy:
    needs: approval
    runs-on: ubuntu-latest
    
    permissions:
      id-token: write
      contents: read
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN_PROD }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Deploy to Production
        run: pnpm sst deploy --stage production

      - name: Output URLs
        run: pnpm sst output --stage production

      - name: Notify success
        run: echo "✅ Production deployment completed successfully"

4. Validación en PRs

yaml
# .github/workflows/pr-check.yml
name: PR Check

on:
  pull_request:
    branches:
      - main
      - develop
      - staging

jobs:
  check:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Type check
        run: pnpm typecheck

      - name: Lint
        run: pnpm lint

      - name: Test
        run: pnpm test

      - name: Build
        run: pnpm build

Configuración de AWS IAM para CI/CD

Crear Role para GitHub Actions (OIDC)

bash
# 1. Crear Identity Provider para GitHub
aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1

Trust Policy para el Role

json
// trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:intik/despertares-intranet:*"
        }
      }
    }
  ]
}

Permisos del Role (Policy)

json
// permissions-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SSTPulumi",
      "Effect": "Allow",
      "Action": [
        "s3:*",
        "dynamodb:*",
        "lambda:*",
        "apigateway:*",
        "cloudfront:*",
        "cognito-idp:*",
        "cognito-identity:*",
        "ses:*",
        "iam:*",
        "logs:*",
        "cloudformation:*",
        "ssm:*",
        "secretsmanager:*",
        "events:*",
        "sqs:*",
        "sns:*"
      ],
      "Resource": "*"
    }
  ]
}

Crear el Role

bash
# Crear role para dev
aws iam create-role \
  --role-name despertares-github-actions-dev \
  --assume-role-policy-document file://trust-policy.json

# Attach policy
aws iam put-role-policy \
  --role-name despertares-github-actions-dev \
  --policy-name DeployPolicy \
  --policy-document file://permissions-policy.json

# Repetir para staging y production con políticas más restrictivas

Configuración de Dominio

1. Registrar Dominio (si no existe)

El dominio despertares.edu.pe debe estar registrado. Si no lo está:

  • Registrar en NIC Perú o registrador autorizado
  • Configurar nameservers hacia Route 53 o mantener en registrador

2. Configurar en Route 53 (Recomendado)

typescript
// infra/dns.ts
export const hostedZone = aws.route53.getZone({
  name: "despertares.edu.pe",
});

// Subdominios por ambiente
const subdomains = {
  dev: "dev.despertares.edu.pe",
  staging: "staging.despertares.edu.pe", 
  production: "despertares.edu.pe",
};

3. Certificado SSL (ACM)

typescript
// infra/certificate.ts
// El certificado DEBE estar en us-east-1 para CloudFront

export const certificate = new aws.acm.Certificate("MainCert", {
  domainName: "despertares.edu.pe",
  subjectAlternativeNames: [
    "*.despertares.edu.pe",
  ],
  validationMethod: "DNS",
}, { provider: awsUsEast1 }); // Forzar us-east-1

// Validación DNS automática
const validationRecords = certificate.domainValidationOptions.apply(options =>
  options.map(option => 
    new aws.route53.Record(`cert-validation-${option.domainName}`, {
      zoneId: hostedZone.zoneId,
      name: option.resourceRecordName,
      type: option.resourceRecordType,
      records: [option.resourceRecordValue],
      ttl: 60,
    })
  )
);

// Esperar validación
export const certificateValidation = new aws.acm.CertificateValidation("MainCertValidation", {
  certificateArn: certificate.arn,
  validationRecordFqdns: validationRecords.apply(records => 
    records.map(r => r.fqdn)
  ),
});

4. CloudFront con Dominio Personalizado

typescript
// infra/cdn.ts
export const cdn = new sst.aws.StaticSite("Web", {
  path: "packages/web",
  build: {
    command: "pnpm build",
    output: "dist",
  },
  domain: {
    name: $app.stage === "production" 
      ? "despertares.edu.pe"
      : `${$app.stage}.despertares.edu.pe`,
    dns: sst.aws.dns(),
    cert: certificate.arn,
  },
  environment: {
    VITE_API_URL: api.url,
    VITE_STAGE: $app.stage,
  },
});

Monitoreo y Logging

1. CloudWatch Logs

SST configura automáticamente logs para Lambda. Para acceder:

bash
# Ver logs en tiempo real (desarrollo)
pnpm sst dev
# Los logs aparecen en la terminal

# Ver logs de staging/production
pnpm sst log --stage staging

# O desde AWS Console:
# CloudWatch > Log Groups > /aws/lambda/despertares-*

2. Configurar Alertas

typescript
// infra/monitoring.ts
import * as aws from "@pulumi/aws";

// Alarma para errores de Lambda
export const lambdaErrorAlarm = new aws.cloudwatch.MetricAlarm("LambdaErrors", {
  alarmDescription: "Lambda function errors",
  comparisonOperator: "GreaterThanThreshold",
  evaluationPeriods: 1,
  metricName: "Errors",
  namespace: "AWS/Lambda",
  period: 300, // 5 minutos
  statistic: "Sum",
  threshold: 5,
  alarmActions: [snsTopicArn],
  dimensions: {
    FunctionName: apiFunction.name,
  },
});

// Alarma para latencia alta
export const latencyAlarm = new aws.cloudwatch.MetricAlarm("HighLatency", {
  alarmDescription: "API Gateway high latency",
  comparisonOperator: "GreaterThanThreshold",
  evaluationPeriods: 2,
  metricName: "Latency",
  namespace: "AWS/ApiGateway",
  period: 300,
  statistic: "Average",
  threshold: 3000, // 3 segundos
  alarmActions: [snsTopicArn],
});

3. Dashboard CloudWatch

typescript
// infra/dashboard.ts
export const dashboard = new aws.cloudwatch.Dashboard("MainDashboard", {
  dashboardName: `despertares-${$app.stage}`,
  dashboardBody: JSON.stringify({
    widgets: [
      {
        type: "metric",
        x: 0, y: 0, width: 12, height: 6,
        properties: {
          title: "API Requests",
          metrics: [
            ["AWS/ApiGateway", "Count", "ApiId", api.id],
          ],
          period: 300,
        },
      },
      {
        type: "metric",
        x: 12, y: 0, width: 12, height: 6,
        properties: {
          title: "Lambda Errors",
          metrics: [
            ["AWS/Lambda", "Errors", "FunctionName", apiFunction.name],
          ],
          period: 300,
        },
      },
      {
        type: "metric",
        x: 0, y: 6, width: 12, height: 6,
        properties: {
          title: "DynamoDB Consumed Capacity",
          metrics: [
            ["AWS/DynamoDB", "ConsumedReadCapacityUnits", "TableName", table.name],
            ["AWS/DynamoDB", "ConsumedWriteCapacityUnits", "TableName", table.name],
          ],
          period: 300,
        },
      },
    ],
  }),
});

Costos Estimados por Ambiente

Desarrollo (dev)

ServicioUsoCosto
LambdaMínimo (dev local)~$0
API GatewayMínimo~$0
DynamoDBOn-demand, poco uso~$1
S3< 1GB~$0.02
CloudFrontPoco tráfico~$0
Cognito< 50K MAUGRATIS
Total Dev~$1-2/mes

Staging

ServicioUsoCosto
LambdaTesting moderado~$1
API Gateway~100K requests~$0.35
DynamoDBOn-demand, 2GB~$2
S3~5GB~$0.12
CloudFront~20GB~$2
Cognito< 50K MAUGRATIS
Total Staging~$5-8/mes

Producción

ServicioUso EstimadoCosto
Lambda500K requests~$0.20
API Gateway500K requests~$1.75
DynamoDBOn-demand, 10GB~$5
S320GB + requests~$1
CloudFront100GB transfer~$8
SES10K emails~$1
Cognito< 50K MAUGRATIS
CloudWatchLogs + metrics~$3
Total Prod~$20-30/mes

Costo Total Mensual

┌─────────────────────────────────────────┐
│  Ambiente    │  Costo Estimado          │
├─────────────────────────────────────────┤
│  Dev         │  ~$1-2/mes               │
│  Staging     │  ~$5-8/mes               │
│  Production  │  ~$20-30/mes             │
├─────────────────────────────────────────┤
│  TOTAL       │  ~$26-40/mes             │
└─────────────────────────────────────────┘

Seguridad

1. Secrets Management

bash
# Usar SST Secrets (recomendado)
pnpm sst secret set SUNAT_API_KEY "xxx" --stage production
pnpm sst secret set SUNAT_API_SECRET "xxx" --stage production

# Los secrets se almacenan en AWS SSM Parameter Store
# Encriptados con KMS
typescript
// Uso en código
const sunatApiKey = new sst.Secret("SunatApiKey");
const sunatApiSecret = new sst.Secret("SunatApiSecret");

// Vincular a Lambda
api.route("$default", {
  handler: "...",
  link: [sunatApiKey, sunatApiSecret],
});

// En el handler
import { Resource } from "sst";
const apiKey = Resource.SunatApiKey.value;

2. Políticas de IAM Mínimas

typescript
// infra/security.ts
// Lambda solo tiene acceso a recursos específicos

const lambdaRole = new aws.iam.Role("ApiLambdaRole", {
  assumeRolePolicy: JSON.stringify({
    Version: "2012-10-17",
    Statement: [{
      Action: "sts:AssumeRole",
      Effect: "Allow",
      Principal: { Service: "lambda.amazonaws.com" },
    }],
  }),
});

// Solo acceso a la tabla específica
new aws.iam.RolePolicy("DynamoAccess", {
  role: lambdaRole.id,
  policy: table.arn.apply(arn => JSON.stringify({
    Version: "2012-10-17",
    Statement: [{
      Effect: "Allow",
      Action: [
        "dynamodb:GetItem",
        "dynamodb:PutItem",
        "dynamodb:UpdateItem",
        "dynamodb:DeleteItem",
        "dynamodb:Query",
        "dynamodb:Scan",
      ],
      Resource: [arn, `${arn}/index/*`],
    }],
  })),
});

3. WAF (Web Application Firewall)

typescript
// infra/waf.ts (opcional para producción)
export const waf = new aws.wafv2.WebAcl("ApiWaf", {
  scope: "CLOUDFRONT",
  defaultAction: { allow: {} },
  rules: [
    {
      name: "RateLimiting",
      priority: 1,
      action: { block: {} },
      statement: {
        rateBasedStatement: {
          limit: 2000, // requests por 5 min por IP
          aggregateKeyType: "IP",
        },
      },
      visibilityConfig: {
        sampledRequestsEnabled: true,
        cloudWatchMetricsEnabled: true,
        metricName: "RateLimiting",
      },
    },
    {
      name: "AWSManagedRulesCommonRuleSet",
      priority: 2,
      overrideAction: { none: {} },
      statement: {
        managedRuleGroupStatement: {
          vendorName: "AWS",
          name: "AWSManagedRulesCommonRuleSet",
        },
      },
      visibilityConfig: {
        sampledRequestsEnabled: true,
        cloudWatchMetricsEnabled: true,
        metricName: "CommonRuleSet",
      },
    },
  ],
  visibilityConfig: {
    sampledRequestsEnabled: true,
    cloudWatchMetricsEnabled: true,
    metricName: "ApiWaf",
  },
});

Backup y Recuperación

DynamoDB Point-in-Time Recovery

typescript
// infra/database.ts
export const mainTable = new sst.aws.Dynamo("MainTable", {
  // ... fields e indexes
  
  // Habilitar PITR para producción
  pointInTimeRecovery: $app.stage === "production",
});

S3 Versioning

typescript
// infra/storage.ts
export const privateBucket = new sst.aws.Bucket("PrivateFiles", {
  access: "private",
  versioning: $app.stage === "production",
});

Backup Manual (Script)

bash
# scripts/backup.sh
#!/bin/bash

STAGE=${1:-production}
DATE=$(date +%Y%m%d_%H%M%S)

# Backup DynamoDB
aws dynamodb export-table-to-point-in-time \
  --table-arn "arn:aws:dynamodb:us-east-1:ACCOUNT:table/despertares-${STAGE}-MainTable" \
  --s3-bucket "despertares-backups" \
  --s3-prefix "dynamodb/${DATE}"

echo "Backup completed: ${DATE}"

Troubleshooting

Problemas Comunes

ProblemaSolución
AccessDenied en deployVerificar credenciales AWS y permisos IAM
Lambda timeoutAumentar timeout en config o optimizar código
Cold start lentoReducir bundle size, usar provisioned concurrency
CORS errorsVerificar configuración CORS en API Gateway
Cognito token inválidoVerificar User Pool ID y Client ID

Comandos de Diagnóstico

bash
# Ver estado del stack
pnpm sst status --stage staging

# Ver diferencias antes de deploy
pnpm sst diff --stage staging

# Ver logs de Lambda
pnpm sst log --stage staging

# Consola interactiva
pnpm sst console --stage staging

# Ver outputs
pnpm sst output --stage staging

Checklist de Despliegue

Pre-Deploy

  • [ ] Tests pasando (pnpm test)
  • [ ] Lint sin errores (pnpm lint)
  • [ ] Variables de entorno configuradas
  • [ ] Secrets configurados (pnpm sst secret list)
  • [ ] Backup de producción (si aplica)

Deploy

  • [ ] Deploy a staging primero
  • [ ] Verificar funcionalidad en staging
  • [ ] Aprobar deploy a producción
  • [ ] Ejecutar deploy a producción
  • [ ] Verificar outputs

Post-Deploy

  • [ ] Verificar health check API
  • [ ] Verificar login funciona
  • [ ] Revisar logs por errores
  • [ ] Notificar al equipo

Resumen de Comandos

bash
# Desarrollo
pnpm sst dev                          # Dev local con hot reload

# Staging
pnpm sst deploy --stage staging       # Deploy staging
pnpm sst log --stage staging          # Ver logs
pnpm sst remove --stage staging       # Eliminar

# Producción
pnpm sst deploy --stage production    # Deploy prod
pnpm sst output --stage production    # Ver URLs
pnpm sst console --stage production   # Consola web

# Secrets
pnpm sst secret set KEY value --stage production
pnpm sst secret list --stage production

# Utilidades
pnpm sst diff --stage staging         # Ver cambios
pnpm sst status --stage staging       # Ver estado

Documento técnico - Intranet DespertaresÚltima actualización: 30/01/2026