/var/log/ ├── syslog # log general del sistema (el más importante) ├── auth.log # autenticación SSH, sudo, PAM ├── kern.log # mensajes del kernel ├── dmesg # mensajes de arranque del kernel ├── dpkg.log # instalaciones y actualizaciones de paquetes ├── apt/ │ ├── history.log # historial de apt │ └── term.log # output de instalaciones ├── nginx/ │ ├── access.log # peticiones HTTP │ └── error.log # errores de Nginx ├── mysql/ │ └── error.log # errores de MariaDB ├── fail2ban.log # bans y actividad de Fail2ban ├── ufw.log # actividad del firewall UFW ├── cron.log # ejecuciones de cron (en algunas distros) └── mail.log # actividad del servidor de email
En Ubuntu 22.04 con systemd, muchos servicios escriben solo en journald (no en archivos). Usá journalctl para verlos.
# Seguir log en tiempo real tail -f /var/log/syslog tail -f /var/log/nginx/error.log tail -f /var/log/auth.log # Ver las últimas N líneas tail -100 /var/log/syslog tail -50 /var/log/nginx/access.log # Seguir múltiples logs a la vez tail -f /var/log/nginx/access.log \ /var/log/nginx/error.log # Ver desde el principio head -50 /var/log/syslog cat /var/log/dpkg.log # Ver log comprimido (rotado) zcat /var/log/syslog.1.gz zcat /var/log/nginx/access.log.2.gz | tail -100 # Ver todos los logs rotados de un servicio ls -lh /var/log/nginx/ zcat /var/log/nginx/access.log*.gz | wc -l
# Ver todos los logs del sistema (más recientes al final) journalctl # Seguir en tiempo real (como tail -f) journalctl -f # Ver solo los errores y superiores journalctl -p err journalctl -p warning # Niveles de prioridad disponibles: # emerg(0) alert(1) crit(2) err(3) # warning(4) notice(5) info(6) debug(7) # Últimas N líneas journalctl -n 100 journalctl -n 50 -f # últimas 50 y seguir # Sin paginación (output directo) journalctl --no-pager journalctl --no-pager -n 50 # Output en formato corto, con timestamps journalctl -o short-precise # Output en JSON (para scripts) journalctl -o json-pretty -n 5
# Por unidad de systemd (servicio) journalctl -u nginx journalctl -u nginx -f journalctl -u nginx -u php8.2-fpm # múltiples servicios # Por rango de tiempo journalctl --since "2024-05-28 00:00:00" journalctl --since "1 hour ago" journalctl --since "yesterday" journalctl --since today journalctl --since "2024-05-01" --until "2024-05-31" # Por boot del sistema journalctl -b # boot actual journalctl -b -1 # boot anterior journalctl -b -2 # antepenúltimo journalctl --list-boots # listar todos los boots # Por proceso o PID journalctl _PID=1234 journalctl _COMM=nginx # Combinando filtros journalctl -u nginx -p err --since "1 hour ago"
# Ver cuánto espacio ocupa el journal journalctl --disk-usage # Eliminar logs viejos por tamaño journalctl --vacuum-size=500M # Eliminar logs más viejos que N días journalctl --vacuum-time=30d journalctl --vacuum-time=7d # Configurar límite permanente de tamaño # /etc/systemd/journald.conf [Journal] SystemMaxUse=500M SystemKeepFree=1G MaxRetentionSec=30day Compress=yes # Aplicar cambios systemctl restart systemd-journald
En un VPS con poco disco, limitar journald a 500MB evita que los logs consuman todo el espacio con el tiempo.
# Buscar texto en log grep "error" /var/log/nginx/error.log grep -i "error" /var/log/syslog # insensible a mayúsculas # Buscar y mostrar contexto (líneas antes/después) grep -A 3 -B 3 "critical" /var/log/syslog # Excluir líneas grep -v "GET /favicon" /var/log/nginx/access.log # Múltiples patrones grep -E "error|warning|critical" /var/log/syslog # Buscar en múltiples archivos grep "Failed password" /var/log/auth.log* # Contar ocurrencias grep -c "404" /var/log/nginx/access.log # Solo mostrar la parte que coincide grep -o "\"GET.*HTTP" /var/log/nginx/access.log | head -20 # Buscar en logs comprimidos zgrep "error" /var/log/nginx/error.log.*.gz
# Formato de access.log de Nginx: # IP - - [fecha] "método ruta protocolo" status bytes "referer" "agente" # Extraer solo las IPs awk '{print $1}' /var/log/nginx/access.log # IPs más frecuentes awk '{print $1}' /var/log/nginx/access.log \ | sort | uniq -c | sort -rn | head -20 # Rutas más solicitadas awk '{print $7}' /var/log/nginx/access.log \ | sort | uniq -c | sort -rn | head -20 # Códigos de estado y su frecuencia awk '{print $9}' /var/log/nginx/access.log \ | sort | uniq -c | sort -rn # Solo errores 4xx y 5xx awk '$9 ~ /^[45]/' /var/log/nginx/access.log # IPs con más errores 404 awk '$9==404{print $1}' /var/log/nginx/access.log \ | sort | uniq -c | sort -rn | head -10 # Total de bytes transferidos awk '{sum+=$10} END {print sum/1024/1024 " MB"}' \ /var/log/nginx/access.log # Tráfico por hora awk '{print $4}' /var/log/nginx/access.log \ | cut -d: -f2 | sort | uniq -c
GoAccess es un analizador de logs en tiempo real con interfaz en terminal o exportación a HTML.
# Instalar apt install goaccess # Análisis interactivo en terminal goaccess /var/log/nginx/access.log \ --log-format=COMBINED # Generar reporte HTML estático goaccess /var/log/nginx/access.log \ --log-format=COMBINED \ -o /var/www/html/report.html # Análisis en tiempo real (se actualiza solo) tail -f /var/log/nginx/access.log \ | goaccess --log-format=COMBINED - # Analizar logs comprimidos también zcat /var/log/nginx/access.log.*.gz \ | cat - /var/log/nginx/access.log \ | goaccess --log-format=COMBINED -
El reporte HTML de GoAccess muestra gráficas de visitas, IPs, rutas, códigos de estado y agentes de usuario — muy útil para entender el tráfico del servidor.
# Ver intentos fallidos de SSH grep "Failed password" /var/log/auth.log | tail -30 # IPs con más intentos fallidos grep "Failed password" /var/log/auth.log \ | awk '{print $11}' \ | sort | uniq -c | sort -rn | head -15 # Usuarios que intentaron loguearse grep "Failed password" /var/log/auth.log \ | awk '{print $9}' \ | sort | uniq -c | sort -rn | head -10 # Logins exitosos grep "Accepted publickey" /var/log/auth.log grep "Accepted password" /var/log/auth.log # Actividad de sudo grep "sudo" /var/log/auth.log | grep "COMMAND" # Actividad de Fail2ban grep "Ban\|Unban" /var/log/fail2ban.log | tail -30 # Últimas conexiones last -20 lastb -20 # intentos fallidos
Logrotate evita que los logs crezcan indefinidamente. Comprime, archiva y elimina logs viejos automáticamente. Se ejecuta vía cron o systemd.
# Configuración global /etc/logrotate.conf # Configuraciones por servicio (sobreescriben la global) /etc/logrotate.d/nginx /etc/logrotate.d/mysql /etc/logrotate.d/mi-app # Ver configuración de un servicio cat /etc/logrotate.d/nginx
# /etc/logrotate.d/mi-app /var/log/mi-app/*.log { daily # rotar cada día missingok # no error si no existe rotate 14 # mantener 14 versiones compress # comprimir con gzip delaycompress # comprimir recién en la siguiente rotación notifempty # no rotar si está vacío create 640 www-data adm # crear nuevo archivo con estos permisos sharedscripts # ejecutar postrotate una sola vez postrotate # Reabrir archivos de log en Nginx [ -f /run/nginx.pid ] && kill -USR1 $(cat /run/nginx.pid) endscript } # Otras opciones útiles: # weekly → rotar cada semana # monthly → rotar cada mes # size 100M → rotar cuando supere 100MB # maxage 30 → eliminar logs de más de 30 días # dateext → agregar fecha en el nombre del archivo rotado
# Probar configuración sin ejecutar logrotate -d /etc/logrotate.d/mi-app # Forzar rotación manual logrotate -f /etc/logrotate.d/mi-app # Ejecutar rotación global ahora logrotate /etc/logrotate.conf # Ver estado de la última rotación cat /var/lib/logrotate/status
# Accesos en tiempo real tail -f /var/log/nginx/access.log # Solo errores tail -f /var/log/nginx/error.log # Errores críticos del día grep "$(date +%Y/%m/%d)" /var/log/nginx/error.log \ | grep -E "emerg|alert|crit|error" # Cambiar nivel de log en nginx.conf # error_log /var/log/nginx/error.log warn; # Niveles: debug info notice warn error crit alert emerg # Formato personalizado de access log log_format detailed '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$request_time $upstream_response_time'; access_log /var/log/nginx/access.log detailed;
# Log de errores tail -f /var/log/mysql/error.log journalctl -u mariadb -f # Habilitar slow query log (en /etc/mysql/mariadb.conf.d/50-server.cnf) slow_query_log = 1 slow_query_log_file = /var/log/mysql/slow.log long_query_time = 2 # queries de más de 2 segundos log_queries_not_using_indexes = 1 # Ver slow queries tail -f /var/log/mysql/slow.log # Analizar slow queries con mysqldumpslow mysqldumpslow -s t /var/log/mysql/slow.log | head -20 # Habilitar general log (CUIDADO: muy verboso, solo para debug) mysql -u root -p -e "SET GLOBAL general_log = 'ON';" mysql -u root -p -e "SET GLOBAL general_log = 'OFF';"
# Mensajes de arranque del kernel dmesg dmesg | tail -30 dmesg | grep -i error dmesg -T # con timestamps legibles # Log de instalaciones de paquetes cat /var/log/dpkg.log | grep "install" cat /var/log/apt/history.log # Log del sistema general tail -f /var/log/syslog grep "OOM\|out of memory" /var/log/syslog # detectar falta de RAM grep "disk\|I/O error" /var/log/syslog # errores de disco # Errores críticos del sistema hoy journalctl -p err --since today --no-pager # Últimos reinicios del sistema journalctl --list-boots last reboot | head -10
# Logs de un contenedor docker logs mi-app docker logs -f mi-app # en tiempo real docker logs --tail 100 mi-app docker logs --since 1h mi-app # última hora docker logs --timestamps mi-app # Logs con Docker Compose docker compose logs -f docker compose logs -f app # servicio específico docker compose logs --tail 50 db # Ver dónde guarda los logs Docker docker inspect mi-app \ | grep LogPath # Configurar límite de tamaño de logs en docker-compose.yml logging: driver: json-file options: max-size: "50m" max-file: "5" # Enviar logs de Docker a journald logging: driver: journald
Sin límite de tamaño, los logs de Docker en /var/lib/docker/containers/ pueden llenar el disco. Configurar siempre max-size.
# Procesos y uso de CPU/RAM top htop # versión mejorada (apt install htop) # Uso de disco df -h # espacio por partición du -sh /var/www/* # tamaño por sitio du -sh /var/log/* | sort -rh # logs más grandes du -sh /* 2>/dev/null | sort -rh | head -15 # I/O de disco en tiempo real iostat -x 1 # apt install sysstat iotop # apt install iotop # Red nethogs # uso de red por proceso iftop # tráfico por conexión ss -tulnp # puertos escuchando ss -tnp state established # conexiones activas # RAM free -h vmstat 1 10 # cada 1s durante 10 iteraciones # Todo en uno — resumen del sistema watch -n 2 'df -h && echo "---" && free -h && echo "---" && uptime'
#!/bin/bash # /usr/local/bin/monitor-vps.sh # Verificar recursos críticos y alertar si superan umbral EMAIL="tu@email.com" HOSTNAME=$(hostname) ALERTA=0 MENSAJE="" # Disco — alertar si alguna partición supera 85% while read -r linea; do USO=$(echo "$linea" | awk '{print $5}' | tr -d '%') MOUNT=$(echo "$linea" | awk '{print $6}') if [ "$USO" -gt 85 ] 2>/dev/null; then MENSAJE+="⚠ DISCO $MOUNT al ${USO}%\n" ALERTA=1 fi done < <(df -h | tail -n +2) # RAM — alertar si uso supera 90% RAM_TOTAL=$(free | awk '/Mem:/{print $2}') RAM_USADA=$(free | awk '/Mem:/{print $3}') RAM_PCT=$(( RAM_USADA * 100 / RAM_TOTAL )) if [ "$RAM_PCT" -gt 90 ]; then MENSAJE+="⚠ RAM al ${RAM_PCT}%\n" ALERTA=1 fi # Servicios críticos for SVC in nginx mariadb; do if ! systemctl is-active --quiet "$SVC"; then MENSAJE+="🔴 SERVICIO CAÍDO: $SVC\n" ALERTA=1 fi done # Enviar alerta si hay problemas if [ "$ALERTA" -eq 1 ]; then echo -e "$MENSAJE" \ | mail -s "⚠ Alerta VPS $HOSTNAME" "$EMAIL" fi
chmod +x /usr/local/bin/monitor-vps.sh
# Ejecutar cada 15 minutos via cron
# */15 * * * * /usr/local/bin/monitor-vps.sh
# Instalar logwatch apt install logwatch # Ver reporte del día en la terminal logwatch --output stdout --range today --detail high # Enviar reporte por email logwatch --mailto tu@email.com --range yesterday --detail med # Configurar envío diario automático # /etc/logwatch/conf/logwatch.conf MailTo = tu@email.com Range = yesterday Detail = Med Output = mail # Logwatch ya configura su propio cron en /etc/cron.daily/ ls /etc/cron.daily/logwatch
Logwatch resume la actividad de SSH, sudo, Nginx, cron, paquetes instalados y más en un email diario — sin tener que revisar cada log manualmente.