Backups a las 3 AM. Limpieza de logs cada domingo. Renovación de SSL automática. Todo esto se hace con cron, el programador de tareas de Linux.
Esta guía te enseña a dominar cron y automatizar tu VPS como un profesional.
¿Qué es cron?
Cron es el programador de tareas de Linux. Ejecuta comandos o scripts en horarios que tú defines.
Casos de uso comunes
| Tarea | Frecuencia típica |
|---|---|
| Backups | Diario a las 3 AM |
| Limpieza de logs | Semanal |
| Renovar SSL | Cada 60 días |
| Enviar reportes | Diario/Semanal |
| Verificar servicios | Cada 5 minutos |
| Limpiar caché | Cada hora |
| Actualizar datos | Cada 15 minutos |
Sintaxis de cron
Los 5 campos de tiempo
┌───────────── minuto (0 - 59)
│ ┌───────────── hora (0 - 23)
│ │ ┌───────────── día del mes (1 - 31)
│ │ │ ┌───────────── mes (1 - 12)
│ │ │ │ ┌───────────── día de la semana (0 - 7, 0 y 7 = domingo)
│ │ │ │ │
* * * * * comando
Caracteres especiales
| Carácter | Significado | Ejemplo |
|---|---|---|
* | Cualquier valor | * * * * * = cada minuto |
, | Lista de valores | 1,15,30 = minutos 1, 15 y 30 |
- | Rango | 1-5 = lunes a viernes |
/ | Intervalo | */15 = cada 15 |
Ejemplos de horarios
# Cada minuto
* * * * *
# Cada hora (minuto 0)
0 * * * *
# Cada día a las 3:00 AM
0 3 * * *
# Cada lunes a las 9:00 AM
0 9 * * 1
# Cada 15 minutos
*/15 * * * *
# Cada hora de 9 a 17 (horario laboral)
0 9-17 * * *
# Primer día de cada mes a las 00:00
0 0 1 * *
# Cada domingo a las 2:30 AM
30 2 * * 0
# Cada 5 minutos de lunes a viernes
*/5 * * * 1-5
Gestionar cron jobs
Ver cron jobs actuales
# Ver cron del usuario actual
crontab -l
# Ver cron de otro usuario (como root)
crontab -u www-data -l
# Ver cron del sistema
cat /etc/crontab
ls /etc/cron.d/
ls /etc/cron.daily/
ls /etc/cron.weekly/
Editar cron jobs
# Editar cron del usuario actual
crontab -e
# Editar cron de otro usuario
sudo crontab -u www-data -e
Eliminar cron jobs
# Eliminar todos los cron del usuario
crontab -r
# Eliminar con confirmación
crontab -i -r
Ejemplos prácticos
Backup diario
# Backup a las 3 AM todos los días
0 3 * * * /root/scripts/backup.sh >> /var/log/backup.log 2>&1
#!/bin/bash
# /root/scripts/backup.sh
FECHA=$(date +%Y%m%d)
tar -czf /backups/www-$FECHA.tar.gz /var/www/
mysqldump -u root database | gzip > /backups/db-$FECHA.sql.gz
find /backups -mtime +7 -delete
Limpieza de logs semanal
# Cada domingo a las 4 AM
0 4 * * 0 /root/scripts/limpiar-logs.sh
#!/bin/bash
# /root/scripts/limpiar-logs.sh
journalctl --vacuum-time=7d
find /var/log -name "*.gz" -mtime +30 -delete
> /var/log/nginx/access.log
systemctl reload nginx
Renovar SSL automáticamente
# Dos veces al día (recomendado por Let's Encrypt)
0 0,12 * * * certbot renew --quiet --post-hook "systemctl reload nginx"
Monitor de servicios cada 5 minutos
*/5 * * * * /root/scripts/check-services.sh
#!/bin/bash
# /root/scripts/check-services.sh
for service in nginx mysql php8.2-fpm; do
if ! systemctl is-active --quiet $service; then
systemctl restart $service
echo "$service reiniciado $(date)" >> /var/log/service-monitor.log
# Opcional: notificar
curl -s "https://api.telegram.org/bot$TOKEN/sendMessage" \
-d chat_id="$CHAT_ID" \
-d text="⚠️ $service reiniciado en $(hostname)"
fi
done
Limpiar caché cada hora
0 * * * * /root/scripts/limpiar-cache.sh
#!/bin/bash
# /root/scripts/limpiar-cache.sh
# WordPress
wp cache flush --path=/var/www/wordpress --allow-root 2>/dev/null
# Redis
redis-cli FLUSHDB 2>/dev/null
# Archivos temporales
find /tmp -type f -mtime +1 -delete
Enviar reporte diario
# Cada día a las 8 AM
0 8 * * * /root/scripts/reporte-diario.sh | mail -s "Reporte VPS $(date +%Y-%m-%d)" [email protected]
#!/bin/bash
# /root/scripts/reporte-diario.sh
echo "=== ESTADO DEL SERVIDOR ==="
echo ""
echo "Uptime: $(uptime -p)"
echo ""
echo "=== DISCO ==="
df -h | grep -E "^/dev"
echo ""
echo "=== MEMORIA ==="
free -h
echo ""
echo "=== TOP PROCESOS ==="
ps aux --sort=-%mem | head -6
echo ""
echo "=== ERRORES ÚLTIMAS 24H ==="
journalctl -p err --since yesterday --no-pager | tail -20
Sincronizar archivos con remoto
# Cada 6 horas
0 */6 * * * rsync -avz /var/www/ usuario@backup-server:/backups/www/ >> /var/log/rsync.log 2>&1
Actualizar datos desde API
# Cada 15 minutos
*/15 * * * * curl -s https://api.ejemplo.com/data > /var/www/app/data.json
Buenas prácticas
1. Siempre redirigir output
# Malo: sin redirección (genera emails de cron)
0 3 * * * /root/scripts/backup.sh
# Bueno: redirigir a log
0 3 * * * /root/scripts/backup.sh >> /var/log/backup.log 2>&1
# O descartar output
0 3 * * * /root/scripts/backup.sh > /dev/null 2>&1
2. Usar rutas absolutas
# Malo
0 3 * * * backup.sh
# Bueno
0 3 * * * /root/scripts/backup.sh
# En scripts, también rutas absolutas
#!/bin/bash
/usr/bin/mysqldump -u root database > /backups/db.sql
3. Establecer PATH en crontab
# Al inicio del crontab
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 3 * * * /root/scripts/backup.sh
4. Usar flock para evitar ejecuciones simultáneas
# Si el script tarda más que el intervalo
*/5 * * * * flock -n /tmp/mi-script.lock /root/scripts/mi-script.sh
5. Añadir timeout
# Matar si tarda más de 1 hora
0 3 * * * timeout 3600 /root/scripts/backup.sh
6. Notificar errores
0 3 * * * /root/scripts/backup.sh || echo "Backup falló" | mail -s "ERROR Backup" [email protected]
Variables de entorno en cron
El problema
Cron ejecuta con un entorno mínimo, sin las variables que tienes en tu shell.
Solución 1: Definir en crontab
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=[email protected]
HOME=/root
0 3 * * * /root/scripts/backup.sh
Solución 2: Cargar en el script
#!/bin/bash
source /root/.bashrc
# o
export PATH=/usr/local/bin:$PATH
export DATABASE_URL="mysql://user:pass@localhost/db"
Solución 3: Archivo de entorno
# /root/.cron-env
DATABASE_URL=mysql://user:pass@localhost/db
API_KEY=tu-api-key
0 3 * * * . /root/.cron-env && /root/scripts/backup.sh
Directorios especiales de cron
/etc/cron.d/
Scripts del sistema con sintaxis de crontab:
# /etc/cron.d/mi-app
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user command
*/5 * * * * root /opt/mi-app/check.sh
/etc/cron.daily/, weekly/, monthly/
Scripts que se ejecutan automáticamente:
# Crear script en /etc/cron.daily/
sudo nano /etc/cron.daily/mi-tarea
#!/bin/bash
# Tu script aquí
sudo chmod +x /etc/cron.daily/mi-tarea
Systemd Timers: La alternativa moderna
Ventajas sobre cron
| Cron | Systemd Timers |
|---|---|
| Sintaxis críptica | Más legible |
| Sin logs integrados | Logs con journalctl |
| Sin dependencias | Puede depender de otros servicios |
| Ejecuta aunque esté apagado… no | Puede ejecutar al arrancar |
Crear un timer
1. El servicio:
# /etc/systemd/system/backup.service
[Unit]
Description=Backup diario
[Service]
Type=oneshot
ExecStart=/root/scripts/backup.sh
2. El timer:
# /etc/systemd/system/backup.timer
[Unit]
Description=Ejecutar backup diario
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
3. Activar:
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
# Verificar
systemctl list-timers
Sintaxis OnCalendar
OnCalendar=daily # Cada día a medianoche
OnCalendar=weekly # Cada lunes a medianoche
OnCalendar=*-*-* 03:00:00 # Cada día a las 3 AM
OnCalendar=Mon *-*-* 09:00:00 # Cada lunes a las 9 AM
OnCalendar=*:0/15 # Cada 15 minutos
Ver logs del timer
journalctl -u backup.service
Debugging de cron
Ver si cron está corriendo
systemctl status cron
Ver logs de cron
grep CRON /var/log/syslog
# o
journalctl -u cron
Probar script manualmente
# Ejecutar como lo haría cron
sudo -u root /bin/bash -c '/root/scripts/backup.sh'
# Con el entorno de cron
env -i /bin/bash -c '/root/scripts/backup.sh'
Errores comunes
| Error | Causa | Solución |
|---|---|---|
| No ejecuta | Permisos | chmod +x script.sh |
| Comando no encontrado | PATH | Usar ruta absoluta |
| Sin output | No redirigido | Añadir >> log 2>&1 |
| Ejecuta doble | Overlap | Usar flock |
Preguntas frecuentes
¿Cómo sé si mi cron job se ejecutó?
Revisa /var/log/syslog con 'grep CRON /var/log/syslog' o añade logging a tu script redirigiendo output a un archivo de log.
¿Por qué mi cron job no funciona pero el script sí manualmente?
Probablemente es un problema de PATH o variables de entorno. Cron ejecuta con un entorno mínimo. Usa rutas absolutas y define variables necesarias.
¿Cron o systemd timers?
Cron es más simple y universal. Systemd timers son más potentes y mejor integrados en sistemas modernos. Para tareas simples, cron está bien.
¿Qué pasa si el servidor está apagado cuando toca ejecutar?
Con cron tradicional, se pierde la ejecución. Con systemd timers y 'Persistent=true', se ejecuta al arrancar si se perdió.
¿Cómo evito que un cron job se ejecute dos veces si tarda mucho?
Usa flock para crear un lock: 'flock -n /tmp/mi-script.lock /ruta/script.sh'. Si ya está corriendo, la segunda ejecución se cancela.
Nuestra recomendación
Cron jobs esenciales para cualquier VPS:
- Backup diario (3 AM)
- Renovación SSL (cada 12h)
- Monitor de servicios (cada 5 min)
- Limpieza de logs (semanal)
Mejores prácticas:
- Siempre redirigir output a logs
- Usar rutas absolutas
- Probar scripts manualmente primero
- Usar flock para evitar overlaps
¿Necesitas automatización profesional? La administración gestionada de Avantys incluye configuración y monitorización de tareas automáticas.
Conclusión
Cron es una herramienta poderosa que todo administrador de VPS debe dominar. Automatiza las tareas repetitivas y tu servidor funcionará mejor con menos intervención.
Empieza con backups automáticos y ve añadiendo más tareas según necesites.
¿Necesitas un VPS para automatizar? Explora los VPS de Avantys con soporte para todas tus tareas programadas.
¿Quieres que lo hagamos por ti?
En Avantys gestionamos tu web, hosting y crecimiento digital de punta a punta. Tú a lo importante.