Skip to content

Monitoreo, logs y respaldos ​

Un sitio en producción no es "publicar y olvidar". Necesitas enterarte antes que el cliente cuando algo falla, saber por qué falló y poder recuperar todo si ocurre un desastre (un hackeo, un rm equivocado, un disco que muere). Esta lección cubre las tres cosas: logs, monitoreo y respaldos.

Concepto ​

  • Logs: el diario del servidor. Cada petición, error o evento queda registrado.
  • Monitoreo: vigilancia automática: ¿responde el sitio?, ¿cuánta CPU/disco se usa?, ¿hay errores en la app?
  • Alertas: notificaciones (correo, Telegram, Slack) cuando algo sale de lo normal.
  • Respaldos: copias de archivos y bases de datos guardadas fuera del servidor.

La regla de oro

Un respaldo que nunca has restaurado no es un respaldo, es una esperanza. Prueba la restauración periódicamente.

Parte 1: dónde están los logs ​

ServicioUbicación
Nginx/var/log/nginx/access.log, /var/log/nginx/error.log (o los que definas por sitio)
Apache/var/log/apache2/access.log, /var/log/apache2/error.log
PHPEl definido en error_log de php.ini (p. ej. /var/log/php_errors.log)
PHP-FPM/var/log/php8.3-fpm.log
MySQL/var/log/mysql/error.log
PM2~/.pm2/logs/ o los definidos en ecosystem.config.js
Laravelstorage/logs/laravel.log
Sistema / serviciosjournalctl
Accesos SSH/var/log/auth.log
Dockerdocker compose logs

Comandos para investigar ​

bash
# Errores en vivo
sudo tail -f /var/log/nginx/error.log

# Últimos errores 500 en el log de acceso
sudo awk '$9 >= 500' /var/log/nginx/access.log | tail -20

# Páginas más solicitadas
sudo awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -15

# IPs con más peticiones (detectar bots o ataques)
sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10

# Intentos fallidos de SSH
sudo grep "Failed password\|Invalid user" /var/log/auth.log | tail -20

# Logs de un servicio desde hace 2 horas
sudo journalctl -u nginx --since "2 hours ago"

# Logs de PM2
pm2 logs api-tienda --lines 200

Rotación de logs con logrotate ​

Sin rotación, los logs crecen hasta llenar el disco. Ubuntu ya rota los de Nginx, Apache y MySQL. Para tus propios logs crea una regla:

txt
# /etc/logrotate.d/mis-apps
/home/deploy/logs/*.log {
    daily
    rotate 14          # conservar 14 días
    compress           # comprimir los viejos con gzip
    delaycompress
    missingok
    notifempty
    copytruncate       # no requiere reiniciar la app
}

Prueba la configuración sin aplicar: sudo logrotate -d /etc/logrotate.d/mis-apps.

Parte 2: monitoreo ​

Recursos del servidor ​

bash
htop                     # CPU, RAM y procesos en vivo
df -h                    # disco
sudo ncdu /              # qué carpetas ocupan más (sudo apt install ncdu)
free -h                  # memoria
uptime                   # carga promedio (1, 5, 15 min)

Script de alerta sencilla de disco, para ejecutar con cron:

bash
#!/usr/bin/env bash
# ~/scripts/alerta-disco.sh — avisa si el disco supera el 85%
LIMITE=85
USO=$(df / --output=pcent | tail -1 | tr -dc '0-9')

if [ "$USO" -ge "$LIMITE" ]; then
  # Aviso por Telegram (crea un bot con @BotFather y obtén tu chat_id)
  curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage" \
    -d chat_id="${TELEGRAM_CHAT_ID}" \
    -d text="⚠️ $(hostname): disco al ${USO}%"
fi

Monitoreo de disponibilidad (uptime) ​

Servicios externos que visitan tu sitio cada pocos minutos y te avisan si no responde:

  • UptimeRobot: muy popular, capa gratuita con monitores cada 5 minutos.
  • Better Stack (Uptime): monitoreo, páginas de estado e incidentes.
  • Uptime Kuma: alternativa autohospedada y de código abierto.

Uptime Kuma con Docker en tu VPS:

yaml
# ~/uptime-kuma/docker-compose.yml
services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    ports:
      - "127.0.0.1:3001:3001"   # detrás de Nginx con HTTPS
    volumes:
      - ./data:/app/data
bash
cd ~/uptime-kuma && docker compose up -d

Luego un server block de Nginx con proxy_pass http://127.0.0.1:3001; (con cabeceras de WebSocket) y Certbot.

Monitorea desde fuera

Si tu único monitor vive en el mismo servidor que vigila, cuando el servidor caiga… el monitor también. Usa al menos un servicio externo para lo crítico.

Errores de aplicación con Sentry ​

Los logs dicen que algo falló; Sentry te dice dónde (archivo, línea, usuario, navegador) y te agrupa los errores. Tiene SDK para JavaScript, Node, PHP, Laravel, Python y más.

bash
npm install @sentry/node
js
// instrument.js — cárgalo antes que el resto de la app
const Sentry = require('@sentry/node');

Sentry.init({
  dsn: process.env.SENTRY_DSN,          // lo obtienes al crear el proyecto en Sentry
  environment: process.env.NODE_ENV,
  tracesSampleRate: 0.1,                // 10% de las peticiones para rendimiento
});
js
// server.js
require('./instrument');
const Sentry = require('@sentry/node');
const express = require('express');
const app = express();

app.get('/', (req, res) => res.send('ok'));

// Después de todas las rutas: captura los errores no manejados
Sentry.setupExpressErrorHandler(app);

app.listen(3000);

Parte 3: respaldos ​

La regla 3-2-1 ​

  • 3 copias de tus datos (la original + 2 respaldos).
  • En 2 medios distintos (disco del servidor + almacenamiento en la nube).
  • 1 copia fuera del sitio (otro proveedor u otra región).

Qué respaldar ​

  1. Bases de datos (lo más importante y lo que más cambia).
  2. Archivos subidos por usuarios (uploads/, storage/app/).
  3. Configuración: .env, server blocks, ecosystem.config.js, crontab.
  4. El código ya vive en Git (si no, ¡ese es el primer paso!).

Script de respaldo completo ​

bash
#!/usr/bin/env bash
# ~/scripts/respaldo.sh — respalda base de datos y archivos con rotación
set -euo pipefail

FECHA=$(date +%F_%H%M)
DESTINO="/home/deploy/respaldos"
DIAS_RETENCION=14

# --- MySQL ---
DB_NOMBRE="tienda"
# Las credenciales se leen de ~/.my.cnf (ver abajo) para no escribirlas aquí

# --- Archivos ---
CARPETA_SITIO="/var/www/tienda.com"

mkdir -p "$DESTINO"

echo "[$(date)] Respaldando base de datos $DB_NOMBRE"
mysqldump --single-transaction --routines --triggers "$DB_NOMBRE" \
  | gzip > "$DESTINO/db_${DB_NOMBRE}_${FECHA}.sql.gz"

# Para PostgreSQL usarías:
# pg_dump -U tienda -h localhost tienda | gzip > "$DESTINO/db_tienda_${FECHA}.sql.gz"

echo "[$(date)] Respaldando archivos"
tar -czf "$DESTINO/archivos_${FECHA}.tar.gz" \
  --exclude="node_modules" --exclude="vendor" --exclude=".git" \
  -C "$(dirname "$CARPETA_SITIO")" "$(basename "$CARPETA_SITIO")"

echo "[$(date)] Borrando respaldos de más de $DIAS_RETENCION días"
find "$DESTINO" -type f -name "*.gz" -mtime +"$DIAS_RETENCION" -delete

echo "[$(date)] Subiendo a almacenamiento externo"
rclone copy "$DESTINO" remoto:respaldos-tienda --max-age 24h

echo "[$(date)] Respaldo terminado"

Credenciales de MySQL en ~/.my.cnf (permisos 600) para que mysqldump no pida contraseña:

ini
[client]
user=tienda_app
password=Cambia-Esta-Contraseña-123!
bash
chmod 600 ~/.my.cnf
chmod +x ~/scripts/respaldo.sh

Subir a la nube con rclone ​

rclone copia archivos a más de 70 servicios: Amazon S3, Backblaze B2, Cloudflare R2, Google Drive, Dropbox, OneDrive…

bash
sudo apt install rclone -y
rclone config        # asistente interactivo: crea un "remote" llamado "remoto"
rclone lsd remoto:   # comprobar que conecta

Respaldos inmutables

Si un atacante entra a tu servidor, puede borrar también los respaldos remotos usando las credenciales de rclone. Usa un bucket con versionado o bloqueo de objetos (Object Lock), o una llave de API que solo permita escribir, no borrar.

Programar con cron ​

bash
crontab -e
bash
# Respaldo diario a las 3:15 a. m., guardando su propio log
15 3 * * * /home/deploy/scripts/respaldo.sh >> /home/deploy/logs/respaldo.log 2>&1

# Alerta de disco cada hora
0 * * * * TELEGRAM_TOKEN=xxx TELEGRAM_CHAT_ID=yyy /home/deploy/scripts/alerta-disco.sh

Recordatorio del formato de cron: minuto hora día-del-mes mes día-de-la-semana comando. Puedes comprobar expresiones en crontab.guru.

Restaurar (¡practícalo!) ​

bash
# Base de datos MySQL
gunzip -c db_tienda_2026-10-18_0315.sql.gz | mysql tienda

# PostgreSQL
gunzip -c db_tienda_2026-10-18_0315.sql.gz | psql -U tienda -h localhost tienda

# Archivos
sudo tar -xzf archivos_2026-10-18_0315.tar.gz -C /var/www/

# Desde la nube
rclone copy remoto:respaldos-tienda/db_tienda_2026-10-18_0315.sql.gz ./

Un buen hábito: una vez al mes, restaura el último respaldo en un servidor o base de prueba y verifica que el sitio funciona.

Snapshots del proveedor ​

DigitalOcean, Hetzner, Vultr y otros ofrecen backups automáticos o snapshots del servidor completo por un costo pequeño. Actívalos: son la forma más rápida de volver atrás tras un desastre. Pero no sustituyen a los respaldos externos (si pierdes la cuenta, pierdes también los snapshots).

Respaldos en hosting compartido (cPanel) ​

  • cPanel → Respaldo: descarga un respaldo completo de la cuenta o solo de bases de datos.
  • Muchos hostings incluyen respaldos automáticos (p. ej. JetBackup); verifica cuánto tiempo los conservan.
  • Puedes programar un cron en cPanel con mysqldump hacia una carpeta fuera de public_html.

Error común

Guardar los respaldos dentro de public_html. Cualquiera que adivine el nombre del archivo puede descargar tu base de datos completa. Siempre fuera de la carpeta pública.

Ejercicios ​

  1. Escribe la línea de crontab que ejecute respaldo.sh todos los domingos a las 2:00 a. m.
  2. Escribe un comando que muestre cuántas respuestas 404 hubo hoy en el log de Nginx.
  3. Explica cómo aplicarías la regla 3-2-1 al sitio de un cliente en un VPS.
Ver solución
bash
0 2 * * 0 /home/deploy/scripts/respaldo.sh >> /home/deploy/logs/respaldo.log 2>&1
bash
sudo grep "$(date +%d/%b/%Y)" /var/log/nginx/access.log | awk '$9 == 404' | wc -l
  1. Copia 1: los datos vivos en el VPS. Copia 2: respaldos diarios comprimidos en otra carpeta del VPS (o snapshots del proveedor). Copia 3: esos respaldos subidos con rclone a otro proveedor (Backblaze B2, S3 o R2) con versionado. Y prueba de restauración mensual.

Resumen ​

  • Conoce dónde está cada log y usa tail -f, grep, awk y journalctl para investigar.
  • Rota tus logs con logrotate para no llenar el disco.
  • Monitorea disponibilidad desde fuera (UptimeRobot, Better Stack, Uptime Kuma) y errores con Sentry.
  • Respaldos 3-2-1: mysqldump/pg_dump + tar + rclone + cron.
  • Prueba la restauración periódicamente.
  • Siguiente: Rendimiento, caché y CDN.

Academia Web — aprende, copia y construye.