Runbooks
Procedimientos operativos. Para cada uno: qué hace, cuándo se ejecuta, comandos exactos, qué verificar al final.
- Deploy de
tcgcards-web(frontend) - Deploy de
tcgcards-api(backend) - Rollback de un deploy
- Restaurar backup de Mongo
- Rotar un secret
- Agregar/quitar admin a un usuario
- Forzar la ejecución de un cron job
- Actualizar/agregar credenciales Mercado Pago
- Investigar un error en producción
- Cambiar dominio
Deploy tcgcards-web (frontend)
Sección titulada «Deploy tcgcards-web (frontend)»Cuándo: cada vez que quieres que un cambio del repo tcgcards-web llegue a tcgcards.cl.
Procedimiento:
- Estar en
mainy tener los cambios pusheados a GitHub. - Esperar. Vercel hace el deploy automáticamente al detectar el push.
Si quieres forzar un deploy sin nuevos commits (raro):
cd /Users/yoel/Documents/Repos/tcgcards-webvercel --prodCómo verificar:
- https://vercel.com →
tcgcards-web→ Deployments → ver el último con status “Ready”. - Abrir https://tcgcards.cl en incognito → verificar el cambio en producción.
Tiempo total: ~2-3 min.
Deploy tcgcards-api (backend)
Sección titulada «Deploy tcgcards-api (backend)»Cuándo: cada vez que quieres que un cambio del repo tcgcards-api llegue a producción. Esto no es automático.
Procedimiento:
cd /Users/yoel/Documents/Repos/tcgcards-api
# 1. Verificar que tests + build pasen localnpm testnpm run build
# 2. Pushear a main (si tienes cambios sin pushear)git push
# 3. Deploy a Cloud Rungcloud run deploy tcgcards-api \ --source . \ --region us-central1 \ --project api-cards-prod \ --quietCómo verificar:
# Status del serviciogcloud run services describe tcgcards-api \ --region us-central1 \ --project api-cards-prod \ --format="value(status.conditions[0].type,status.conditions[0].status,status.latestReadyRevisionName)"# Esperado: "Ready True tcgcards-api-XXXXX-xxx"
# Sanity check endpoint públicocurl -s -o /dev/null -w "%{http_code}\n" \ "https://tcgcards-api-1033181994095.us-central1.run.app/api/v1/health"# Esperado: 200Tiempo total: 5-8 min (build + container push + revision routing).
Rollback de un deploy
Sección titulada «Rollback de un deploy»Web (Vercel)
Sección titulada «Web (Vercel)»- https://vercel.com →
tcgcards-web→ Deployments - Encuentra el deploy anterior bueno.
- Click en los
...→ “Promote to Production”.
Toma ~30 segundos. No requiere CLI.
Backend (Cloud Run)
Sección titulada «Backend (Cloud Run)»# 1. Listar revisiones recientesgcloud run revisions list \ --service tcgcards-api \ --region us-central1 \ --project api-cards-prod \ --limit 5
# 2. Apuntar 100% del tráfico a una revisión específicagcloud run services update-traffic tcgcards-api \ --to-revisions=tcgcards-api-00128-gpd=100 \ --region us-central1 \ --project api-cards-prod(Reemplaza tcgcards-api-00128-gpd por la revisión deseada.)
Cómo verificar: comando describe igual que en deploy.
Restaurar backup de Mongo
Sección titulada «Restaurar backup de Mongo»Cuándo: corrupción de datos, borrado accidental, o restauración en ambiente paralelo.
RPO efectivo: ~1 hora (backups hourly via Cloud Scheduler).
Setup local (one-time)
Sección titulada «Setup local (one-time)»brew tap mongodb/brewbrew install mongodb-database-toolsbrew install mongodb-communitybrew services start mongodb-communityRestore desde el último backup
Sección titulada «Restore desde el último backup»# 1. Buscar el dump más reciente en GCSmkdir -p /tmp/restore-drillLATEST=$(gcloud storage ls gs://tcgcards-mongo-backups/ \ --project=api-cards-prod | sort | tail -1)echo "Último backup: $LATEST"
# 2. Descargargcloud storage cp "$LATEST" /tmp/restore-drill/dump.archive.gz \ --project=api-cards-prod
# 3. Validar integridadgzip -t /tmp/restore-drill/dump.archive.gz
# 4. Restore contra Mongo local (DESTRUCTIVO si tienes data local)mongorestore --archive=/tmp/restore-drill/dump.archive.gz --gzip --dropCómo verificar:
mongosh> use api-cards> db.cards.countDocuments() // debería coincidir con prod> db.orders.countDocuments()Para restore a un cluster Atlas distinto del de producción, usar --uri="mongodb+srv://..." en mongorestore.
Runbook detallado del drill exitoso: tcgcards-api/docs/runbooks/restore-drill-2026-05-11.md.
Rotar un secret
Sección titulada «Rotar un secret»Secret en GCP Secret Manager (backend)
Sección titulada «Secret en GCP Secret Manager (backend)»# 1. Generar el nuevo valor (ejemplo para secret aleatorio de 32 bytes hex)NEW_VALUE=$(openssl rand -hex 32)
# 2. Agregar como nueva versiónecho -n "$NEW_VALUE" | gcloud secrets versions add INTERNAL_JWT_SECRET \ --data-file=- \ --project=api-cards-prod
# 3. Verificargcloud secrets versions access latest \ --secret=INTERNAL_JWT_SECRET \ --project=api-cards-prod
# 4. Redeploy del Cloud Run para que apliquecd /Users/yoel/Documents/Repos/tcgcards-apigcloud run deploy tcgcards-api \ --source . \ --region us-central1 \ --project api-cards-prod \ --quietSecret en Vercel (frontend)
Sección titulada «Secret en Vercel (frontend)»# Listarvercel env ls
# Quitar el viejovercel env rm INTERNAL_JWT_SECRET production
# Agregar el nuevo (interactive, pide valor)vercel env add INTERNAL_JWT_SECRET production
# Redeploy para que apliquevercel --prodCambiar valor sensible y mantener compatibilidad
Sección titulada «Cambiar valor sensible y mantener compatibilidad»Para minimizar downtime al rotar secrets compartidos:
- Generar nuevo valor.
- Setear en backend + redeploy.
- Setear en frontend + redeploy.
- (Entre paso 2 y 3 hay ~2 min de incompatibilidad. Hacer en horario de menor tráfico.)
Agregar/quitar admin a un usuario
Sección titulada «Agregar/quitar admin a un usuario»No hay UI para esto. Se hace por Mongo directo.
# Conectarmongosh "<MONGODB_URI>"> use api-cards
# Hacer admin> db.users.updateOne({ username: "ejemplo" }, { $set: { isAdmin: true } })
# Quitar admin> db.users.updateOne({ username: "ejemplo" }, { $set: { isAdmin: false } })
# Verificar> db.users.findOne({ username: "ejemplo" }, { isAdmin: 1, username: 1 })El cambio aplica inmediatamente — el siguiente request del user con sesión activa lo verá como admin. No hace falta reiniciar nada.
Forzar la ejecución de un cron job
Sección titulada «Forzar la ejecución de un cron job»gcloud scheduler jobs run orders-tick \ --project=api-cards-prod \ --location=us-central1Reemplaza orders-tick por el job que quieras. Lista completa:
mongo-backup-hourlyorders-tickorders-awaiting-payment-cleanupwallet-reconcile-dailyrefunds-reconcile-daily
Cómo verificar:
# Ver logs del Cloud Run del último minutogcloud run services logs read tcgcards-api \ --region=us-central1 \ --project=api-cards-prod \ --limit=50Buscar líneas con prefijo cron. (e.g., cron.canceled, cron.completed).
Actualizar/agregar credenciales Mercado Pago
Sección titulada «Actualizar/agregar credenciales Mercado Pago»Cuándo: se necesita rotar MP_ACCESS_TOKEN, el webhook secret, o cuando MP cambia algo del lado de ellos.
# 1. Obtener token nuevo desde el panel MP → Credenciales productivas (o test si testing)# El token es de la forma APP_USR-XXXXXX-XXXXXX-XXXXXX-XXXXXX
# 2. Actualizar el secretecho -n "APP_USR-..." | gcloud secrets versions add MP_ACCESS_TOKEN \ --data-file=- \ --project=api-cards-prod
# 3. Redeploycd /Users/yoel/Documents/Repos/tcgcards-apigcloud run deploy tcgcards-api --source . --region us-central1 --project api-cards-prod --quietPara el MP_WEBHOOK_SECRET:
- En el panel MP, configurar el webhook con el endpoint:
https://tcgcards-api-1033181994095.us-central1.run.app/api/v1/webhooks/mp - MP genera un secret — copiarlo
echo -n "SECRET" | gcloud secrets versions add MP_WEBHOOK_SECRET --data-file=- --project=api-cards-prod- Redeploy.
Investigar un error en producción
Sección titulada «Investigar un error en producción»1. Sentry (errores capturados)
Sección titulada «1. Sentry (errores capturados)»https://sentry.io → proyecto correspondiente (web o api) → buscar el error por mensaje o usuario.
2. Logs estructurados del backend
Sección titulada «2. Logs estructurados del backend»gcloud run services logs read tcgcards-api \ --region=us-central1 \ --project=api-cards-prod \ --limit=100 \ --format=json | jq '.[] | select(.severity == "ERROR")'3. Mongo directo (datos del user/orden afectada)
Sección titulada «3. Mongo directo (datos del user/orden afectada)»mongosh "<MONGODB_URI>"> use api-cards> db.orders.findOne({ _id: "order-xxxxx" })> db.users.findOne({ email: "afectado@example.com" }, { walletBalance: 1, status: 1 })4. PostHog (frontend events)
Sección titulada «4. PostHog (frontend events)»https://app.posthog.com → buscar por user properties (email/username).
Cambiar dominio
Sección titulada «Cambiar dominio»Si alguna vez se cambia el dominio tcgcards.cl a otro:
- DNS en Cloudflare: apuntar el nuevo dominio a Vercel + Cloud Run.
- Vercel: agregar el nuevo dominio en el proyecto
tcgcards-web→ Settings → Domains. - Env vars del web: actualizar
NEXT_PUBLIC_SITE_URL→ redeploy Vercel. - Env vars del api: actualizar
WEB_PUBLIC_URL(afecta los emails que mandamos) → redeploy Cloud Run. - OAuth Google: agregar
https://nuevo-dominio.cl/api/auth/callback/googlea los Authorized Redirect URIs en Google Cloud Console. - MP webhooks: registrar el nuevo URL en el panel MP.
- Resend: verificar el nuevo dominio (SPF + DKIM en Cloudflare DNS) para poder mandar emails como
notificaciones@nuevo-dominio. - Email Routing en Cloudflare: repetir setup
soporte@ydisputas@para el nuevo dominio. - Sentry: agregar el nuevo dominio a CORS allowlist en cada proyecto.
- Cloudflare Access (docs): actualizar la aplicación de Zero Trust si el dominio de docs también cambia.
Es un cambio grande — hacer en ventana de mantenimiento, con OK explícito.