Appearance
Guía de Despliegue - Intranet Despertares
Modelo Híbrido (Este Proyecto)
La intranet usa arquitectura híbrida:
| Dónde | Qué se despliega |
|---|---|
| AWS | Solo Cognito, S3 y SES. IaC con Terraform o SST. |
| Servidor | API 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
| Aspecto | Detalle |
|---|---|
| AWS (solo 3 servicios) | Cognito, S3, SES |
| Herramienta IaC AWS | Terraform o SST v3 |
| Servidor | API Java (Spring Boot) + PostgreSQL (VPS/EC2) |
| CI/CD | GitHub Actions (API + opcional Terraform/SST) |
| Ambientes | dev, staging, production |
| Región AWS | us-east-1 (recomendado para SES) |
| Costo estimado | AWS ~$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-identity3. 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 repo4. 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 --version5. SST CLI (solo para desplegar recursos AWS)
bash
# SST se instala como dependencia del proyecto
# No requiere instalación globalConfiguració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 install2. 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=xxx3. 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: xxxxxxxxxxxxxxxxxxxxxxxxxxAmbiente 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 stagingAmbiente 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 destructivosEliminar Ambiente
bash
# Eliminar ambiente de desarrollo
pnpm sst remove --stage dev
# ⚠️ NUNCA hacer esto en producción sin backup
# pnpm sst remove --stage production # PELIGROSOCI/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 PRs1. 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 dev2. 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 staging3. 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 buildConfiguració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 6938fd4d98bab03faadb97b34396831e3780aea1Trust 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 restrictivasConfiguració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)
| Servicio | Uso | Costo |
|---|---|---|
| Lambda | Mínimo (dev local) | ~$0 |
| API Gateway | Mínimo | ~$0 |
| DynamoDB | On-demand, poco uso | ~$1 |
| S3 | < 1GB | ~$0.02 |
| CloudFront | Poco tráfico | ~$0 |
| Cognito | < 50K MAU | GRATIS |
| Total Dev | ~$1-2/mes |
Staging
| Servicio | Uso | Costo |
|---|---|---|
| Lambda | Testing moderado | ~$1 |
| API Gateway | ~100K requests | ~$0.35 |
| DynamoDB | On-demand, 2GB | ~$2 |
| S3 | ~5GB | ~$0.12 |
| CloudFront | ~20GB | ~$2 |
| Cognito | < 50K MAU | GRATIS |
| Total Staging | ~$5-8/mes |
Producción
| Servicio | Uso Estimado | Costo |
|---|---|---|
| Lambda | 500K requests | ~$0.20 |
| API Gateway | 500K requests | ~$1.75 |
| DynamoDB | On-demand, 10GB | ~$5 |
| S3 | 20GB + requests | ~$1 |
| CloudFront | 100GB transfer | ~$8 |
| SES | 10K emails | ~$1 |
| Cognito | < 50K MAU | GRATIS |
| CloudWatch | Logs + 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 KMStypescript
// 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
| Problema | Solución |
|---|---|
AccessDenied en deploy | Verificar credenciales AWS y permisos IAM |
| Lambda timeout | Aumentar timeout en config o optimizar código |
| Cold start lento | Reducir bundle size, usar provisioned concurrency |
| CORS errors | Verificar configuración CORS en API Gateway |
| Cognito token inválido | Verificar 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 stagingChecklist 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 estadoDocumento técnico - Intranet DespertaresÚltima actualización: 30/01/2026