Cron en UTC para un agente IA: Vercel, Workers y Actions
Resumen
Cómo no mandar el digest a las 3 AM en Guatemala: crons de Vercel, Cloudflare y GitHub Actions corren en UTC. America/Guatemala es UTC−6 todo el año, sin DST. Convierte 9:00 CST a 15:00 UTC y apaga el cron en Preview.

Qué resuelve
Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.
El cron no habla español. Habla UTC. Si pones 0 9 * * * creyendo 9 AM en Puerto Barrios, el digest sale a las 3 AM local (UTC−6). Kill switch apaga el canal. Esta guía cubre a qué hora dispara. Fuentes oficiales consultadas el 3 de septiembre de 2026.
Guatemala no tiene DST. America/Guatemala = UTC−6 todo el año. No copies un snippet de America/New_York.
La regla
Escribe el cron en UTC. Documenta la hora local al lado. Preview sin cron de prod (preview). Workers: cron solo en el env de prod.
9:00 CST → 0 15 * * * (15:00 UTC).
Vercel
Docs de Cron Jobs (consultadas hoy): la zona horaria del cron es siempre UTC. Vercel dispara un HTTP GET a la URL de production con el path de vercel.json. User-Agent vercel-cron/1.0. Header x-vercel-cron-schedule con la expresión (ejemplo de la doc 0 5 * * *). El handler es un Route Handler; maxDuration tiene que caber el turno (CPU). Un cron a las 15:00 UTC que tarda 4 min no es “9:04 AM”: es wall clock del Function.
No pongas el cron en Preview: cada PR dispara el digest. Production only. Si el GET llega a un preview, el digest sale dos veces.
Workers
Docs de Cron Triggers: el disparo va en hora UTC. Handler scheduled(). Limits (cuenta, Free vs Paid): 5 Cron Triggers en Free, 250 en Paid. CPU time por Cron Trigger: Free 10 ms; Paid 30 s si el intervalo es menor a 1 h, 15 min si el intervalo es de 1 h o más. [triggers].crons en el Worker de prod, no en my-worker-dev. El mismo digest a clientes desde dev es un incidente.
GitHub Actions
Docs de events: POSIX cron. Por defecto el schedule corre en UTC. Intervalo mínimo: una vez cada 5 minutos. Opcional: timezone: con string IANA. Ejemplo de la doc: cron: '30 5 * * 1-5' + timezone: "America/New_York". Guatemala no tiene DST; si copias ese snippet, cambia a America/Guatemala o deja UTC y suma 6. TZ=America/Guatemala en un step no mueve el schedule: solo la hora dentro del script.
Actions sirve para jobs que no caben en Workers Free. No para “cada minuto”: el techo documentado es 5 min.
Tabla

| Querías (CST) | Cron UTC | Plataforma (docs 2026-09-03) |
|---|---|---|
| 09:00 | 0 15 * * * | Vercel UTC / Workers UTC / Actions UTC |
| 21:00 | 0 3 * * * | al día siguiente UTC |
| cada 15 min | */15 * * * * | Actions: techo 5 min; Workers Free: 5 crons |
| una vez domingo 9 | 0 15 * * 0 | POSIX: 0 = domingo |
| 09:00 IANA | 0 9 * * * + timezone: America/Guatemala | solo Actions; Vercel/Workers no |
Errores comunes

| Síntoma | Causa | Fix |
|---|---|---|
| Sale 6 h temprano | cron pensado en CST | +6 al hour UTC |
| Preview spamea | cron en todos los deploys | solo Production / --env |
| Worker Free 1102 | digest pesado en 10 ms CPU | Paid o Actions |
| Actions “TZ=Guatemala” y sigue mal | TZ no mueve el schedule | UTC en cron: |
| Domingo vs lunes | 0 vs 1 | POSIX: 0 domingo |
Relación con el resto
- No mezclar env: preview.
- Apagar el cron: kill switch.
- Hostname del webhook diurno: TLS.
- Cold start a las 15:00 UTC: cold starts.
Checklist
- Tabla CST → UTC en el README del agente
- Cron solo en Production
-
maxDuration/ CPU de cron ≥ un turno - Ensayo: un cron de prueba a +10 min UTC, luego bórralo
- Guatemala sin DST: no “ajusta en marzo”
- Workers: cron no está en
dev
FAQ
¿CRON_TZ en Vercel? No. La doc fija UTC y no hay override.
¿timezone: en Actions? Sí, IANA. En Vercel y Workers, no. Tres runtimes, tres reglas.
¿Fly/Railway? Un cron del sistema o un scheduler. TZ=America/Guatemala en el contenedor sí afecta a cron de Linux. No mezcles eso con vercel.json.
¿9 AM “hora México”? México tiene DST en algunas zonas. Guatemala no. Docs de Actions avisan: en spring-forward, un job a las 2:30 salta a las 3:00. En Guatemala ese aviso no aplica. No copies la tabla de America/New_York.
El ensayo: programa un ping a UTC ahora+10 min, mira el log con timestamp UTC y CST. Si no coinciden con +6, el cron está mal.
Si el job es “cada hora en horario de oficina CST”, no uses 24 slots. Usa 15–21 UTC (0 15-21 * * *) y listo. Fuera de esa ventana el agente duerme: menos factura y menos sorpresas.
Una sola fuente de verdad: un comentario // 09:00 America/Guatemala = 15:00 UTC junto al cron. Si el comentario y la expresión no cuadran, gana UTC y corriges el comentario. No “arregles” a ojo a las 2 AM.
Vercel no dispara el cron contra Preview: el GET va a production. Si ves invocaciones en un PR, no es el scheduler de Vercel; es un test o un rewrite. Workers sí puede tener cron en un env dev si lo declaraste ahí: revísalo en wrangler.
Fly/Railway con cron de Linux dentro del contenedor: ahí sí TZ. Vercel/Workers/Actions: nunca. Mezclar los tres en un mismo repo sin etiquetar cuál es cuál es el bug.
Siguiente paso: si la hora ya es correcta y el job no corre, logs y healthchecks. Sin runtime: curso.
Lecturas relacionadas
Sigue explorando Deploy y otras piezas para builders.



