Tutorial

cron vs systemd timers: automatizar su VPS Linux

Automatización12 min de lectura13 pasos

En un VPS Linux, la automatización no es un lujo: copias de seguridad nocturnas, renovación de certificados TLS, purga de logs, sondas de salud — esas tareas se ejecutan sin usted o no se ejecutan. Cron reina desde hace décadas, pero los systemd timers ofrecen una integración más profunda con el gestor de servicios. Esta guía compara ambas herramientas, describe cuándo elegir una u otra y le acompaña paso a paso para crear o migrar un timer de systemd.

Contenido· ¿Por qué automatizar las tareas en un VPS?1/12
  1. 01¿Por qué automatizar las tareas en un VPS?
  2. 02Casos de uso típicos en un VPS
  3. 03cron: recordatorio rápido
  4. 04systemd timers: introducción
  5. 05cron vs systemd timers: tabla comparativa
  6. 06¿Cuándo conservar cron?
  7. 07¿Cuándo pasar a los systemd timers?
  8. 08Crear un timer de systemd desde cero (ejemplo: copia de seguridad diaria)
  9. 09Migrar una tarea cron existente a systemd
  10. 10Validar y depurar sus expresiones OnCalendar
  11. 11Problemas frecuentes con los timers de systemd
  12. 12Conclusión

¿Por qué automatizar las tareas en un VPS?

Un VPS es un servidor que funciona de forma continua, a menudo sin supervisión humana directa. Precisamente por eso la automatización de las tareas recurrentes es fundamental: nadie estará ahí a las 3 de la madrugada para lanzar la copia de seguridad o renovar el certificado Let's Encrypt. Una tarea olvidada puede traducirse en una pérdida de datos, en un certificado caducado que deja el sitio fuera de línea o en un disco saturado por falta de rotación de logs.

En la práctica, cinco categorías de tareas aparecen en todo VPS Linux serio: rotación de logs de Nginx para evitar que /var/log/nginx/ llegue a varios gigabytes en pocas semanas; limpieza de imágenes Docker huérfanas (docker image prune -af) para recuperar espacio en disco sin intervención manual; copias de seguridad de PostgreSQL hacia almacenamiento remoto; verificación del certificado SSL antes de su expiración para lanzar una renovación de certbot; y finalmente scripts de scraping ligero o indexación planificados que deben ejecutarse a hora fija. Las dos grandes herramientas disponibles en Linux — cron y los systemd timers — permiten planificar estas operaciones de forma fiable.

Casos de uso típicos en un VPS

  • Copias de seguridad — dump de MySQL o PostgreSQL a almacenamiento remoto (pg_dump -Fc mydb | gzip > /backup/mydb_$(date +%F).sql.gz), archivado de ficheros con rsync o rclone hacia un bucket S3
  • Renovación de certificados TLS — certbot renew --quiet o acme.sh --cron planificados dos veces al día, con recarga automática de Nginx tras el éxito
  • Rotación y purga de logs — borrado de los ficheros de más de 30 días (find /var/log -mtime +30 -delete), compresión semanal, vaciado de logs de aplicación en producción
  • Limpieza de Docker — docker image prune -af, eliminación de volúmenes huérfanos, rotación de logs de contenedores para liberar /var/lib/docker
  • Sondas de salud — comprobación de que un servicio escucha (curl -sf http://localhost:3000/health), reinicio automático si hace falta, alerta por webhook
  • Sincronización de datos — rsync hacia un segundo nodo, actualización de cachés, sincronización de ficheros estáticos hacia un CDN
  • Tareas de aplicación planificadas — envío de correos recordatorios, cálculo de informes periódicos, indexación de contenido, scripts de scraping ligero

cron: recordatorio rápido

Cron está presente en casi todos los sistemas Linux. Para planificar una tarea se edita la crontab del usuario actual con crontab -e. La sintaxis se basa en cinco campos separados por espacios, seguidos del comando:

# minuto (0-59), hora (0-23), día del mes (1-31), mes (1-12), día de la semana (0-7)
  0  3  *  *  *  /usr/local/bin/backup.sh

Los atajos @daily, @weekly, @monthly y @reboot simplifican los casos habituales. Los ficheros en /etc/cron.d/ pertenecen al sistema e indican el usuario de ejecución directamente. Para revisar las ejecuciones pasadas: /var/log/syslog con grep CRON /var/log/syslog, o journalctl -t CRON. Un detalle importante: el PATH por defecto de cron es mínimo — /usr/bin:/bin — lo que obliga a usar rutas absolutas en los scripts, o a redefinir PATH= al inicio de la crontab. La variable MAILTO controla el envío de salidas por correo; ponerla vacía (MAILTO="") lo desactiva silenciosamente. Cron no conserva ningún historial nativo de las ejecuciones pasadas y no gestiona las tareas perdidas si la máquina estaba apagada.

systemd timers: introducción

Un timer de systemd es una pareja de dos ficheros de unidad: un fichero .service que describe qué ejecutar y un fichero .timer que describe cuándo lanzarlo. El timer se activa con systemctl enable --now monservice.timer y lo gobierna el gestor de servicios.

La directiva OnCalendar= acepta una sintaxis rica y legible:
- daily — todos los días a medianoche
- Mon *-*-* 03:00:00 — cada lunes a las 3 AM
- *-*-* 02:30:00 — todos los días a las 2:30
- *:0/15 — cada 15 minutos

La directiva AccuracySec= (por defecto: 1 minuto) permite a systemd agrupar los timers próximos para ahorrar despertares del procesador. La directiva Persistent=true en el bloque [Timer] es la funcionalidad diferencial respecto a cron: si la máquina estaba apagada a la hora prevista, el timer recupera la ejecución perdida en el siguiente arranque. Todas las salidas pasan por journald y se consultan con journalctl -u monservice.service. El comando systemctl list-timers muestra todos los timers activos, su próximo vencimiento y la fecha de su última activación.

cron vs systemd timers: tabla comparativa

Desplace la tabla

Criteriocronsystemd timer
Sintaxis de planificación5 campos `* * * * *``OnCalendar=` legible (`daily`, `Mon 03:00`)
LogsSalida por correo o redirección manual, `/var/log/syslog`journald nativo, `journalctl -u`
Tareas perdidas (máquina apagada)Perdidas (salvo `anacron`)`Persistent=true` las reproduce tras reiniciar
Dependencias entre serviciosNinguna`After=`, `Requires=`, `Wants=`
Aislamiento y recursosHereda el entorno del shell, PATH mínimoCgroups, `MemoryMax=`, `CPUQuota=`
Notificación de falloNinguna de forma nativa`OnFailure=notify.service` dispara una alerta
Ejecución como un usuario concretoCampo user en `/etc/cron.d/``User=` y `Group=` en el `.service`
DepuraciónDifícil sin logs`systemctl status`, `journalctl -xe`
DisponibilidadTodos los sistemas Unix (contenedores, BSD, Alpine)Sistemas con systemd (Debian, Ubuntu, RHEL…)

¿Cuándo conservar cron?

Cron sigue siendo la buena elección en varias situaciones concretas. En sistemas antiguos o minimalistas sin systemd — algunos contenedores Docker ligeros, imágenes Alpine, sistemas BSD — cron suele ser la única herramienta disponible. En un entorno multiusuario donde cada desarrollador gestiona sus propias tareas con crontab -e sin permisos de root, cron es más sencillo de delegar.

Para scripts de una sola línea muy simples — un certbot renew semanal, un rsync nocturno sin dependencias — la crontab sigue siendo legible y mantenible sin multiplicar los ficheros. Cron es también la buena elección para incorporar a un equipo no especializado en Linux: la sintaxis de los cinco campos, documentada en todas partes, es más accesible que una unidad systemd. Por último, si sus tareas cron llevan años funcionando sin problemas, la migración no aporta valor inmediato. El criterio decisivo es la necesidad de logs estructurados, dependencias entre servicios o restricciones de recursos.

¿Cuándo pasar a los systemd timers?

Los timers de systemd se imponen en cuanto la tarea supera el marco de un simple comando aislado. ¿Necesita journald para trazar cada ejecución, sus salidas y su código de retorno sin fontanería manual? Systemd timers. ¿Su script de copia de seguridad debe ejecutarse solo después de que PostgreSQL esté listo, declarando After=postgresql.service? Systemd timers. ¿Quiere limitar la memoria de un trabajo de sincronización para que no ahogue a los demás procesos (MemoryMax=512M)? Systemd timers.

Las dependencias entre servicios son el argumento más fuerte: un backup lanzado cuando la base de datos no está disponible tras un reinicio produce un fichero vacío sin error visible bajo cron. Systemd garantiza el orden. El logging integrado via journald elimina todo el ruido de las redirecciones manuales. La gestión de errores con OnFailure= permite disparar una notificación (correo, webhook) sin contaminar el script. Y si el servidor se reinicia a las 2:58 cuando la copia de las 3 tendría que haberse ejecutado, Persistent=true garantiza que se ejecutará en el siguiente arranque.

Crear un timer de systemd desde cero (ejemplo: copia de seguridad diaria)

  1. Crear el fichero service

    Abra /etc/systemd/system/backup.service e introduzca:

    [Unit]
    Description=Copia de seguridad diaria de PostgreSQL
    After=postgresql.service
    
    [Service]
    Type=oneshot
    User=postgres
    ExecStart=/usr/local/bin/backup.sh

    El tipo oneshot indica que el servicio termina una vez ejecutado el script — el tipo estándar para las tareas planificadas.

  2. Crear el fichero timer

    Cree /etc/systemd/system/backup.timer con:

    [Unit]
    Description=Disparador diario de la copia de seguridad
    
    [Timer]
    OnCalendar=*-*-* 03:00:00
    Persistent=true
    
    [Install]
    WantedBy=timers.target

    Persistent=true garantiza que si la máquina estaba apagada a las 3, la copia se ejecutará en el siguiente arranque.

  3. Recargar systemd y activar el timer

    Ejecute systemctl daemon-reload para que systemd tenga en cuenta los nuevos ficheros, y luego active e inicie el timer de inmediato:

    systemctl enable --now backup.timer
  4. Comprobar con systemctl status

    Compruebe el estado del timer con systemctl status backup.timer — la salida debe indicar active (waiting) y mostrar la próxima fecha de disparo. Consulte también el estado del servicio tras la primera ejecución con systemctl status backup.service.

  5. Listar todos los timers activos

    El comando systemctl list-timers --all muestra una tabla de todos los timers del sistema con tres columnas clave: NEXT (próxima ejecución), LEFT (tiempo restante) y LAST (última ejecución). Es su panel de control para verificar que sus timers se disparan como se espera.

  6. Consultar los logs

    Acceda a todas las salidas del servicio con journalctl -u backup.service para el historial completo, o journalctl -u backup.service -n 50 --since today para las 50 últimas líneas del día.

  7. Añadir notificación de error (opcional)

    Para recibir alertas en caso de fallo, añada en el bloque [Unit] del fichero .service:

    OnFailure=notify-failure@%n.service

    Este servicio puede enviar un correo, un webhook de Slack o un ping ntfy según su infraestructura. Es lo que cron no puede hacer de forma nativa sin fontanería en el propio script.

  8. Probar el servicio manualmente

    Antes de esperar el disparo planificado, pruebe la ejecución inmediata con systemctl start backup.service (sin .timer). Consulte el resultado de inmediato con journalctl -u backup.service -n 20. Si el script tiene éxito, la configuración está validada.

Migrar una tarea cron existente a systemd

  1. Identificar la tarea cron que migrar

    Liste sus crontabs con crontab -l (usuario actual) y cat /etc/cron.d/* (sistema). Anote el comando exacto, el usuario de ejecución y la planificación en cinco campos. Por ejemplo: 30 2 * * 1 root /usr/bin/certbot renew --quiet significa cada lunes a las 2:30 como root.

  2. Traducir la planificación a OnCalendar

    El formato OnCalendar= de systemd se escribe DayOfWeek Year-Month-Day Hour:Minute:Second. 30 2 * * 1 se convierte en Mon *-*-* 02:30:00.

    Para validar la traducción antes de aplicarla, use systemd-analyze calendar:

    systemd-analyze calendar 'Mon *-*-* 02:30:00'

    El comando muestra las 10 próximas ocurrencias calculadas, eliminando cualquier ambigüedad sobre el comportamiento real.

  3. Crear los ficheros .service y .timer

    Cree /etc/systemd/system/certbot-renew.service con Type=oneshot, ExecStart=/usr/bin/certbot renew --quiet y User=root. Cree después /etc/systemd/system/certbot-renew.timer con OnCalendar=Mon *-*-* 02:30:00 y Persistent=true. Ejecute systemctl daemon-reload && systemctl enable --now certbot-renew.timer.

  4. Probar el servicio antes de desactivar cron

    Pruebe de inmediato con systemctl start certbot-renew.service y consulte el resultado con journalctl -u certbot-renew.service -n 20. Compruebe que el timer aparece en systemctl list-timers con una fecha próxima coherente. Solo desactive la línea cron tras la validación.

  5. Desactivar la línea cron

    Una vez validado el timer, comente o borre la línea en la crontab original. Para un fichero en /etc/cron.d/, renómbrelo con extensión .disabled o bórrelo. Deje coexistir ambos durante unos días si la tarea es crítica — cron y los systemd timers no interfieren.

Validar y depurar sus expresiones OnCalendar

Antes de activar un timer en producción, valide siempre su expresión OnCalendar= con systemd-analyze calendar:

systemd-analyze calendar "daily"
systemd-analyze calendar "Mon *-*-* 02:30:00"
systemd-analyze calendar "*:0/15"

Cada comando muestra la próxima ocurrencia y las diez siguientes — lo que hace inmediatamente visible una expresión mal formada o un desfase horario inesperado. Para una visión de todos sus timers activos con su próxima fecha de disparo y el tiempo restante, systemctl list-timers --all es su panel operacional diario.

Problemas frecuentes con los timers de systemd

Cinco problemas se repiten al poner en marcha los timers.

1. "Unit not found" al hacer systemctl enable — el fichero .timer o .service no está en /etc/systemd/system/ o tiene un nombre incorrecto. Compruebe la ortografía exacta con ls /etc/systemd/system/*.timer. Un daemon-reload es obligatorio antes de cualquier enable o start sobre un fichero nuevo.

2. Timer activo pero que nunca se dispara — ¿muestra systemctl list-timers una fecha coherente en la columna NEXT? Si NEXT está vacío o en el pasado, un Persistent=true olvidado combinado con una máquina que no ha reiniciado desde la creación del timer es una causa habitual. Compruebe systemctl status backup.timer: active (waiting) es el estado normal.

3. ExecStart no encontrado o permiso denegado — use siempre la ruta absoluta en ExecStart= (/usr/bin/certbot y no certbot). Verifique que el script es ejecutable (chmod +x /usr/local/bin/backup.sh) y que el usuario declarado en User= tiene acceso a él.

4. Permisos en el script — si User=postgres pero el script pertenece a root sin permiso de ejecución para postgres, el servicio fallará con el código 203/EXEC. Consulte journalctl -u backup.service -n 20 para ver el mensaje exacto.

5. Conflicto entre cron y systemd en la misma tarea — si está migrando de forma progresiva, asegúrese de no tener ambos activos a la vez para una tarea destructiva (purga de logs, rotación de ficheros). Los dos se ejecutarán independientemente y duplicarán las operaciones. La comprobación es sencilla: crontab -l | grep backup y systemctl is-active backup.timer.

Conclusión

Cron y los systemd timers conviven sin problema en el mismo servidor: no tiene que migrarlo todo de golpe. Conserve cron para sus tareas sencillas y sus scripts de usuario existentes, y adopte los timers de systemd para las nuevas automatizaciones que se benefician de los logs de journald, de las dependencias entre servicios o del aislamiento por cgroups. Los comandos systemd-analyze calendar y systemctl list-timers son sus mejores aliados para validar y supervisar sus planificaciones.

Un VPS Linux listo para sus automatizaciones

Nuestros VPS cloud con Ubuntu y Debian incluyen systemd, journald y toda la pila moderna. Despliegue sus timers, scripts y copias de seguridad sobre una infraestructura fiable, con acceso root completo y soporte ágil.

¿Necesita ayuda?

Consulte nuestro centro de ayuda y nuestra FAQ, o contacte con nuestro equipo: llamada, WhatsApp o correo electrónico. Soporte en francés, inglés y árabe.

Escribir por WhatsAppse abre en una pestaña nueva