El cron que dejó de correr
El respaldo de anoche, el corte de caja, los recordatorios a tus clientes. Un cron que no corre no truena: simplemente no pasa nada, y nadie lo nota hasta que hace falta lo que debió hacer. Aquí, cómo saber si corrió, las causas más comunes y cómo enterarte el mismo día.
1. ¿Corrió? Dónde revisarlo
En un servidor con Linux
- Revisa el crontab del usuario correcto:
crontab -lcomo ese usuario, osudo crontab -l -u deploy. Un crontab de root no es el de tu usuario. - Busca si cron lo lanzó:
grep CRON /var/log/syslogen Debian y Ubuntu, ojournalctl -u cron. - Eso dice que empezó, no que terminó bien. Para saber cómo terminó necesitas la salida del comando (abajo, cómo guardarla).
En Vercel
- En tu proyecto, Settings → Cron Jobs muestra cada cron y la liga a sus logs.
- Los cron solo corren en el despliegue de producción, no en las vistas previas.
En GitHub Actions
- En la pestaña Actions, filtra por el evento
schedule. Si no hay corridas recientes, revisa si el workflow está desactivado.
2. Las causas más comunes
| Lo que pasa | Qué suele ser | Cómo se arregla |
|---|---|---|
| A mano funciona, en cron no | Cron corre con un entorno mínimo: un PATH corto, sin las variables de tu .env y desde otro directorio. | Rutas absolutas (/usr/bin/node), cd al proyecto en la misma línea y tus variables cargadas de forma explícita. |
| Corre, pero a otra hora | El servidor está en UTC. 0 2 * * * corre a las 8 de la noche en la Ciudad de México. GitHub Actions también usa UTC. | Convierte la hora, o fija la zona (en GitHub Actions, la clave timezone). |
| La línea se corta a la mitad | En el crontab, % significa salto de línea: date +%F no llega completo. | Escríbelo como \%, o pasa el comando a un script. |
| Dejó de correr después de un despliegue o un reinicio | El crontab vivía en el servidor viejo, el servicio de cron no arrancó, o tu contenedor no tiene cron. | Que el crontab viva en tu repositorio y se instale al desplegar, o usa el programador de tu plataforma. |
| Corre, pero falla en silencio | La salida se va al correo local del servidor, que nadie lee, o a ningún lado. | Manda la salida a un archivo: >> /var/log/corte.log 2>&1. |
| A veces corre dos veces a la vez | Tarda más que su intervalo y la siguiente corrida empieza antes de que acabe la anterior. | flock -n para que no se encimen. |
| En Vercel, a cualquier minuto o una sola vez | En el plan Hobby, los cron corren una vez al día y en cualquier minuto de su hora. Una expresión más frecuente falla al desplegar. | Pro, si necesitas el minuto exacto o más de una vez al día. |
| En GitHub Actions, tarde o nunca | Con mucha carga, schedule se retrasa y puede saltarse corridas. Solo corre en la rama principal, y en repositorios públicos se desactiva tras 60 días sin actividad. | No lo uses para lo que no puede fallar, o vigílalo desde afuera. |
3. Hazlo visible
Una línea de crontab que se defiende sola: entra al proyecto, no se encima con la corrida anterior y guarda lo que pasó.
# cada 15 minutos, en el directorio del proyecto, sin encimarse, con su log
*/15 * * * * cd /srv/tienda && /usr/bin/flock -n /tmp/conciliar.lock /usr/bin/node scripts/conciliar.js >> /var/log/conciliar.log 2>&1
Y que el script diga cuándo empezó, cuándo terminó y cuánto tardó, y salga con código distinto de cero si falló. Con eso, cuando algo pase, sabrás qué fue.
4. Alguien tiene que esperarlo desde afuera
Todo lo anterior sirve cuando el cron corre. Pero si no corre, no escribe nada: no hay log, no hay error, no hay nada que revisar. Un cron no puede avisar que no corrió.
La única forma de enterarte es que algo de afuera espere su señal y, si no llega a tiempo, avise. Es la idea del «latido»: el cron avisa «ya corrí», y otro sistema nota cuando ese aviso falta.
5. Que te avise solo
Con Estiaje, conectas el SDK en tu script y no declaras nada más:
- Un cron diario se reconoce solo. Después de 3 días, Estiaje ve que tu servicio corre una vez al día, a la misma hora, y lo marca como cron. Si 45 minutos después de su hora no ha llegado nada, te avisa al teléfono: «No corrió».
- Los que corren seguido (cada 5 o cada 15 minutos) entran en «Callado»: Estiaje aprende su ritmo hora por hora y avisa si deja de mandar.
- El que empieza pero no termina: marca el inicio y el final, y declara que esperas el final.
import { estiajeFromEnv } from '@estiaje/sdk';
const estiaje = estiajeFromEnv({ service: 'corte-de-caja' });
const corrida = new Date().toISOString().slice(0, 10);
estiaje.event('corte.empezo', { corrida });
try {
await hacerCorte();
estiaje.outcome('corte.termino', { corrida });
} catch (err) {
estiaje.outcome('corte.fallo', { corrida, motivo: err.message });
process.exitCode = 1;
}
await estiaje.flush(); // un script termina enseguida: espera el envío
Y en el panel, la expectativa: si a la hora de corte.empezo no llega corte.termino ni corte.fallo, te avisa. Un fallo que tu código detectó también se cuenta, aparte.
¿Python, PHP, Ruby o Go? En docs está la receta de cada uno, con cómo esperar el envío antes de salir.
Preguntas frecuentes
¿Por qué mi cron funciona a mano pero no desde el crontab?
Porque cron corre con un entorno mínimo: un PATH corto, sin las variables de tu sesión ni de tu .env, y desde otro directorio. Usa rutas absolutas, haz cd al proyecto en la misma línea y carga tus variables de forma explícita.
¿Qué zona horaria usa cron?
La del servidor, que casi siempre está en UTC: 0 2 * * * corre a las 8 de la noche en la Ciudad de México. GitHub Actions también usa UTC, salvo que pongas la clave timezone.
¿Cómo veo si mi cron corrió en Linux?
En Debian y Ubuntu, con grep CRON /var/log/syslog o journalctl -u cron. Ahí ves si cron lanzó el comando, pero no si terminó bien: para eso hay que mandar su salida a un archivo de log.
¿Por qué no corre mi cron de Vercel?
Los cron de Vercel solo corren en el despliegue de producción. En el plan Hobby solo pueden correr una vez al día y en cualquier minuto de su hora: uno a la 1:00 corre entre la 1:00 y la 1:59.
Fuentes: la documentación de Vercel Cron Jobs (vercel.com/docs/cron-jobs) y de GitHub Actions (docs.github.com, el evento schedule), revisadas el 12 de octubre de 2026.