GuíasGuides · Nivel 2Level 2

Bash: tu primer script de comprobación de un servidor LinuxBash: your first health-check script for a Linux server

Un script que mira disco, memoria, carga y servicios, avisa si algo pasa de un umbral y se programa en cron. Con las cinco reglas que evitan sustos.A script that checks disk, memory, load and services, warns when something crosses a threshold, and runs from cron. With the five rules that avoid nasty surprises.

Qué vas a conseguir

Un script de Bash que puedes dejar en cualquier servidor Linux y que, cada mañana, te dice si el disco se llena, si un servicio se ha caído o si la carga está disparada. Y, de paso, las reglas de escritura que separan un script que ayuda de uno que un día borra algo.

Antes de empezar

  • Un servidor Linux (Ubuntu, RHEL, Debian) con acceso por SSH y sudo.
  • Un editor: nano vale para empezar; vim cuando quieras.
  • Para probar, un directorio propio: mkdir -p ~/scripts.

1. Las cinco reglas

  1. Empieza siempre igual: #!/usr/bin/env bash y set -euo pipefail. Para en el primer error (-e), falla si usas una variable no definida (-u) y no esconde errores en una tubería (-o pipefail).
  2. Comillas en las variables: "$ruta", nunca $ruta. Un espacio en un nombre de archivo y un rm sin comillas es cómo se pierden carpetas.
  3. Nunca rm -rf sobre una variable sin comprobar antes que no está vacía y que apunta a donde crees: [[ -n "$dir" && "$dir" != "/" ]].
  4. Primero en modo lectura. Un script que solo lee y avisa no puede romper nada. Lo que escribe (borrar logs, reiniciar servicios) se añade después, con --apply explícito.
  5. Pásalo por shellcheck antes de ejecutarlo en un servidor. Encuentra los errores clásicos que el ojo no ve.

2. El script

nano ~/scripts/check-server.sh
#!/usr/bin/env bashset -euo pipefail DISK_MAX=85          # % de uso de disco a partir del que avisaLOAD_MAX=4           # carga media de 5 minSERVICES=(sshd cron) # servicios que deben estar activosSTATUS=0 warn() { echo "AVISO: $*"; STATUS=1; } echo "== $(hostname) · $(date '+%d/%m/%Y %H:%M') ==" # Disco: cada sistema de ficheros real por encima del umbralwhile read -r fs use mount; do  use=${use%\%}  (( use >= DISK_MAX )) && warn "disco $mount al $use% ($fs)"done < <(df -h --output=source,pcent,target -x tmpfs -x devtmpfs | tail -n +2) # Memoria y cargaread -r _ total used _ < <(free -m | awk '/^Mem:/')echo "memoria: $used/$total MB"load=$(cut -d' ' -f2 /proc/loadavg)awk -v l="$load" -v m="$LOAD_MAX" 'BEGIN{exit !(l>=m)}' && warn "carga a 5 min: $load" # Serviciosfor s in "${SERVICES[@]}"; do  systemctl is-active --quiet "$s" || warn "servicio $s no está activo"done (( STATUS == 0 )) && echo "OK: sin avisos"exit $STATUS

Notas sobre lo que hace: df --output evita tener que contar columnas; tail -n +2 salta la cabecera; ${use%\%} quita el %; read ... < <(...) lee de un comando sin abrir una subshell (así STATUS sí cambia dentro del bucle). La comparación de la carga va con awk porque Bash no compara decimales.

3. Probar

bash · servidor
chmod +x ~/scripts/check-server.shshellcheck ~/scripts/check-server.sh && echo "shellcheck ok"shellcheck ok~/scripts/check-server.sh== srv-files-01 · 21/10/2026 08:00 ==AVISO: disco /var al 91% (/dev/sda3)memoria: 3105/7812 MBAVISO: servicio cron no está activoecho $?1

El código de salida (1 con avisos, 0 sin ellos) es lo que permite que cron, un monitor o otro script sepan si ha ido bien sin leer el texto.

4. Programarlo

bash · servidor
sudo cp ~/scripts/check-server.sh /usr/local/bin/check-serversudo crontab -e# cada día a las 7:00, guardando la salida en un log que rota0 7 * * * /usr/local/bin/check-server >> /var/log/check-server.log 2>&1sudo tail -n 20 /var/log/check-server.log

En cron no hay tu PATH ni tus alias: usa rutas completas y prueba el script con env -i bash -c /usr/local/bin/check-server para ver cómo se comporta sin tu entorno. Para que avise por correo, MAILTO=it@contoso.com al principio del crontab (si el servidor tiene correo configurado), o mejor, que escriba en el log que ya recoge tu monitorización.

Siguientes pasos

  • Añade un --apply que haga algo útil y seguro: rotar un log, reiniciar el servicio caído con systemctl restart.
  • Pon el script en el repositorio del equipo (guía de Git) para que cambie con revisión y no por SSH a mano.
  • En el Nivel 3 verás Ansible: cuando el mismo script tiene que correr en 40 servidores, es su trabajo.

¿Falta algo o en tu entorno es distinto? Escríbeme y lo corrijo.

What you'll get

A Bash script you can drop on any Linux server which, every morning, tells you whether the disk is filling up, a service has died or the load is through the roof. And, along the way, the writing rules that separate a script that helps from one that deletes something one day.

Before you start

  • A Linux server (Ubuntu, RHEL, Debian) with SSH access and sudo.
  • An editor: nano is fine to start; vim whenever you like.
  • A folder of your own to test in: mkdir -p ~/scripts.

1. The five rules

  1. Always start the same way: #!/usr/bin/env bash and set -euo pipefail. It stops at the first error (-e), fails on an undefined variable (-u) and does not hide errors inside a pipe (-o pipefail).
  2. Quote your variables: "$path", never $path. A space in a file name plus an unquoted rm is how folders get lost.
  3. Never rm -rf on a variable without first checking it is not empty and points where you think: [[ -n "$dir" && "$dir" != "/" ]].
  4. Read-only first. A script that only reads and warns cannot break anything. Whatever writes (deleting logs, restarting services) is added later, behind an explicit --apply.
  5. Run it through shellcheck before executing it on a server. It finds the classic mistakes the eye misses.

2. The script

nano ~/scripts/check-server.sh
#!/usr/bin/env bashset -euo pipefail DISK_MAX=85          # % disk usage that triggers a warningLOAD_MAX=4           # 5-minute load averageSERVICES=(sshd cron) # services that must be activeSTATUS=0 warn() { echo "WARN: $*"; STATUS=1; } echo "== $(hostname) · $(date '+%d/%m/%Y %H:%M') ==" # Disk: every real filesystem above the thresholdwhile read -r fs use mount; do  use=${use%\%}  (( use >= DISK_MAX )) && warn "disk $mount at $use% ($fs)"done < <(df -h --output=source,pcent,target -x tmpfs -x devtmpfs | tail -n +2) # Memory and loadread -r _ total used _ < <(free -m | awk '/^Mem:/')echo "memory: $used/$total MB"load=$(cut -d' ' -f2 /proc/loadavg)awk -v l="$load" -v m="$LOAD_MAX" 'BEGIN{exit !(l>=m)}' && warn "5-min load: $load" # Servicesfor s in "${SERVICES[@]}"; do  systemctl is-active --quiet "$s" || warn "service $s is not active"done (( STATUS == 0 )) && echo "OK: no warnings"exit $STATUS

Notes on what it does: df --output avoids counting columns; tail -n +2 skips the header; ${use%\%} strips the %; read ... < <(...) reads from a command without opening a subshell (so STATUS really changes inside the loop). The load comparison goes through awk because Bash does not compare decimals.

3. Test it

bash · server
chmod +x ~/scripts/check-server.shshellcheck ~/scripts/check-server.sh && echo "shellcheck ok"shellcheck ok~/scripts/check-server.sh== srv-files-01 · 21/10/2026 08:00 ==WARN: disk /var at 91% (/dev/sda3)memory: 3105/7812 MBWARN: service cron is not activeecho $?1

The exit code (1 with warnings, 0 without) is what lets cron, a monitor or another script know whether it went well without reading the text.

4. Schedule it

bash · server
sudo cp ~/scripts/check-server.sh /usr/local/bin/check-serversudo crontab -e# every day at 07:00, appending to a log that gets rotated0 7 * * * /usr/local/bin/check-server >> /var/log/check-server.log 2>&1sudo tail -n 20 /var/log/check-server.log

Cron has neither your PATH nor your aliases: use full paths and test the script with env -i bash -c /usr/local/bin/check-server to see how it behaves without your environment. To get an email, put MAILTO=it@contoso.com at the top of the crontab (if the server has mail configured), or better, have it write to the log your monitoring already collects.

Next steps

  • Add an --apply that does something useful and safe: rotate a log, restart the dead service with systemctl restart.
  • Put the script in the team repository (see the Git guide) so it changes through review, not by hand over SSH.
  • In Level 3 you'll see Ansible: when the same script has to run on 40 servers, that is its job.

Is something missing, or different in your environment? Get in touch and I'll fix it.

Tech Career Lab · newsletter

¿Te ha servido? Cada dos semanas envío una idea práctica para perfiles IT: LinkedIn, proyectos, networking y procesos de selección.Found this useful? Every two weeks I send one practical idea for IT professionals: LinkedIn, projects, networking and hiring processes.

Apúntate y llévate las 33 ideasSign up and get the 33 ideas →