Innovate | Connect | Transform
ContactService statusUSD $

GICT knowledge base

Building a System Health Monitoring Dashboard with Shell Scripts

Published

System Administration

System monitoring is essential for maintaining healthy servers and applications. While enterprise tools exist, a custom shell script dashboard offers simplicity, zero cost, and complete control. This guide walks you through building a comprehensive system health monitoring dashboard using basic shell scripting.

Why Shell Scripts for Monitoring?

Shell scripts are perfect for system monitoring because they’re:

  • Native to Linux/Unix – No installation required
  • Lightweight – Minimal resource consumption
  • Customizable – Tailor to your exact needs
  • Educational – Learn system internals while building
  • Portable – Works across different distributions

Understanding System Health Fundamentals

Before building, let’s understand what to monitor:

  • CPU Usage
    CPU utilization shows how hard your processor is working. High sustained usage (over 80%) indicates performance bottlenecks or runaway processes.
  • Memory Usage
    RAM consumption affects system responsiveness. When physical memory fills up, the system uses slower swap space, degrading performance significantly.
  • Disk Space
    Running out of disk space crashes applications and prevents logging. Monitoring free space prevents catastrophic failures.
  • System Load
    Load average represents the number of processes waiting for CPU time. A load higher than your CPU core count indicates the system is overworked.
  • Network Activity
    Monitoring network traffic helps identify bandwidth issues, DDoS attacks, or misconfigured applications.
  • Running Processes
    Tracking active processes helps identify resource hogs and unauthorized services.

The Complete Monitoring Solution

Basic Monitoring Script

Let’s start with a foundational monitoring script:

#!/bin/bash

# system_monitor.sh - Basic system health monitoring

# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Function to print section headers
print_header() {
    echo -e "\n${BLUE}========================================${NC}"
    echo -e "${BLUE}$1${NC}"
    echo -e "${BLUE}========================================${NC}"
}

# Function to check CPU usage
check_cpu() {
    print_header "CPU USAGE"
    
    cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
    echo -e "CPU Usage: ${cpu_usage}%"
    
    if (( $(echo "$cpu_usage > 80" | bc -l) )); then
        echo -e "${RED}WARNING: High CPU usage detected!${NC}"
    elif (( $(echo "$cpu_usage > 60" | bc -l) )); then
        echo -e "${YELLOW}CAUTION: Moderate CPU usage${NC}"
    else
        echo -e "${GREEN}CPU usage is normal${NC}"
    fi
    
    # Show top 5 CPU consuming processes
    echo -e "\nTop 5 CPU consuming processes:"
    ps aux --sort=-%cpu | head -6 | tail -5
}

# Function to check memory usage
check_memory() {
    print_header "MEMORY USAGE"
    
    mem_info=$(free -h | grep Mem)
    total=$(echo $mem_info | awk '{print $2}')
    used=$(echo $mem_info | awk '{print $3}')
    free=$(echo $mem_info | awk '{print $4}')
    
    mem_percentage=$(free | grep Mem | awk '{printf("%.0f", $3/$2 * 100)}')
    
    echo -e "Total Memory: $total"
    echo -e "Used Memory: $used"
    echo -e "Free Memory: $free"
    echo -e "Memory Usage: ${mem_percentage}%"
    
    if [ "$mem_percentage" -gt 90 ]; then
        echo -e "${RED}WARNING: Critical memory usage!${NC}"
    elif [ "$mem_percentage" -gt 75 ]; then
        echo -e "${YELLOW}CAUTION: High memory usage${NC}"
    else
        echo -e "${GREEN}Memory usage is normal${NC}"
    fi
    
    # Show top 5 memory consuming processes
    echo -e "\nTop 5 memory consuming processes:"
    ps aux --sort=-%mem | head -6 | tail -5
}

# Function to check disk usage
check_disk() {
    print_header "DISK USAGE"
    
    df -h | grep -E '^/dev/' | while read line; do
        usage=$(echo $line | awk '{print $5}' | cut -d'%' -f1)
        partition=$(echo $line | awk '{print $1}')
        mount=$(echo $line | awk '{print $6}')
        
        echo -e "\nPartition: $partition (mounted on $mount)"
        echo $line | awk '{print "Total: "$2" | Used: "$3" | Available: "$4" | Usage: "$5}'
        
        if [ "$usage" -gt 90 ]; then
            echo -e "${RED}WARNING: Critical disk space!${NC}"
        elif [ "$usage" -gt 75 ]; then
            echo -e "${YELLOW}CAUTION: Low disk space${NC}"
        else
            echo -e "${GREEN}Disk space is adequate${NC}"
        fi
    done
}

# Function to check system load
check_load() {
    print_header "SYSTEM LOAD"
    
    load=$(uptime | awk -F'load average:' '{print $2}')
    cores=$(nproc)
    
    echo -e "CPU Cores: $cores"
    echo -e "Load Average:$load"
    
    load_1min=$(uptime | awk -F'load average:' '{print $2}' | cut -d',' -f1 | xargs)
    
    if (( $(echo "$load_1min > $cores" | bc -l) )); then
        echo -e "${RED}WARNING: System load exceeds CPU cores!${NC}"
    else
        echo -e "${GREEN}System load is normal${NC}"
    fi
}

# Function to check uptime
check_uptime() {
    print_header "SYSTEM UPTIME"
    
    uptime_info=$(uptime -p)
    boot_time=$(who -b | awk '{print $3, $4}')
    
    echo -e "System Uptime: $uptime_info"
    echo -e "Last Boot: $boot_time"
}

# Function to check network
check_network() {
    print_header "NETWORK INFORMATION"
    
    # Get primary network interface
    interface=$(ip route | grep default | awk '{print $5}' | head -1)
    
    if [ -n "$interface" ]; then
        echo -e "Primary Interface: $interface"
        
        # Get IP address
        ip_addr=$(ip addr show $interface | grep "inet " | awk '{print $2}' | cut -d'/' -f1)
        echo -e "IP Address: $ip_addr"
        
        # Check connectivity
        if ping -c 1 8.8.8.8 &> /dev/null; then
            echo -e "${GREEN}Internet connection: Active${NC}"
        else
            echo -e "${RED}Internet connection: Failed${NC}"
        fi
    else
        echo -e "${RED}No network interface found${NC}"
    fi
}

# Function to check running services
check_services() {
    print_header "CRITICAL SERVICES"
    
    # List of services to check (customize as needed)
    services=("ssh" "cron")
    
    for service in "${services[@]}"; do
        if systemctl is-active --quiet $service 2>/dev/null; then
            echo -e "$service: ${GREEN}Running${NC}"
        else
            echo -e "$service: ${RED}Not Running${NC}"
        fi
    done
}

# Main execution
clear
echo -e "${BLUE}╔════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║   SYSTEM HEALTH MONITORING DASHBOARD   ║${NC}"
echo -e "${BLUE}╚════════════════════════════════════════╝${NC}"
echo -e "Generated: $(date '+%Y-%m-%d %H:%M:%S')"

check_uptime
check_cpu
check_memory
check_disk
check_load
check_network
check_services

echo -e "\n${BLUE}========================================${NC}"
echo -e "${BLUE}Monitoring Complete${NC}"
echo -e "${BLUE}========================================${NC}\n"

Step-by-Step Setup Instructions

Step 1: Create the script file

Open a terminal and create a new file:

nano system_monitor.sh

Or use your preferred text editor (vim, gedit, etc.).

Step 2: Copy the script

Copy the entire script code above and paste it into the file.

Step 3: Save the file

In nano: Press Ctrl + X, then Y, then Enter
In vim: Press Esc, type :wq, then Enter

Step 4: Make the script executable

Give the script permission to run:

chmod +x system_monitor.sh

Step 5: Run the script

Execute the monitoring dashboard:

./system_monitor.sh

If the script is not in your current directory, use the full path:

/path/to/system_monitor.sh

Step 6: Move to a system-wide location (optional)

To run the script from anywhere:

sudo mv system_monitor.sh /usr/local/bin/system_monitor

Now you can simply run:

system_monitor

Step 7: Verify it works

You should see a colorful dashboard showing:

  • System uptime
  • CPU usage with color-coded warnings
  • Memory usage statistics
  • Disk space for all partitions
  • System load averages
  • Network information
  • Service status

Advanced Features

Continuous Monitoring with Auto-Refresh

Create a dashboard that updates automatically:

Step 1: Create the continuous monitoring script

nano continuous_monitor.sh

Step 2: Add the following code

#!/bin/bash

# continuous_monitor.sh - Auto-refreshing dashboard

while true; do
    clear
    ./system_monitor.sh
    echo -e "\nRefreshing in 5 seconds... (Press Ctrl+C to exit)"
    sleep 5
done

Step 3: Make it executable and run

chmod +x continuous_monitor.sh
./continuous_monitor.sh

The dashboard will now refresh every 5 seconds automatically. Press Ctrl+C to stop it.

Logging and Historical Data

Add logging capabilities to track trends:

Step 1: Create the logging script

nano monitor_with_logging.sh

Step 2: Add this code

#!/bin/bash

# monitor_with_logging.sh

LOG_DIR="/var/log/system_monitor"
LOG_FILE="$LOG_DIR/system_health_$(date +%Y%m%d).log"

# Create log directory if it doesn't exist
mkdir -p $LOG_DIR

# Function to log data
log_metrics() {
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
    mem_usage=$(free | grep Mem | awk '{printf("%.0f", $3/$2 * 100)}')
    disk_usage=$(df -h / | tail -1 | awk '{print $5}' | cut -d'%' -f1)
    load_avg=$(uptime | awk -F'load average:' '{print $2}' | cut -d',' -f1 | xargs)
    
    echo "$timestamp | CPU: ${cpu_usage}% | MEM: ${mem_usage}% | DISK: ${disk_usage}% | LOAD: $load_avg" >> $LOG_FILE
}

# Run the main monitoring script
./system_monitor.sh

# Log the metrics
log_metrics

echo -e "\nMetrics logged to: $LOG_FILE"

Step 3: Make it executable

chmod +x monitor_with_logging.sh

Step 4: Run with sudo (required for writing to /var/log)

sudo ./monitor_with_logging.sh

Step 5: View logged data

cat /var/log/system_monitor/system_health_$(date +%Y%m%d).log

Email Alerts for Critical Events

Send email notifications when thresholds are exceeded:

Step 1: Install mail utilities

# For Ubuntu/Debian
sudo apt-get update
sudo apt-get install mailutils

# For CentOS/RHEL
sudo yum install mailx

If you want a complete step-by-step guide for ubuntu, check out our detailed article – How to Install Mailutils on Ubuntu: A Complete Guide

Step 2: Configure postfix (if prompted)

During installation, you might be asked to configure Postfix.
Choose the following options:
Select “Internet Site”
Enter your server’s hostname (e.g., server.example.com)

That’s all you need to complete the Mailutils configuration.

Step 3: Create the alert script

nano monitor_with_alerts.sh

Step 4: Add this code

#!/bin/bash

# monitor_with_alerts.sh

EMAIL="[email protected]"  # Change this to your email
ALERT_THRESHOLD_CPU=80
ALERT_THRESHOLD_MEM=90
ALERT_THRESHOLD_DISK=85

send_alert() {
    subject=$1
    message=$2
    echo "$message" | mail -s "$subject" $EMAIL
}

# Check CPU and alert
cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1 | cut -d'.' -f1)
if [ "$cpu_usage" -gt "$ALERT_THRESHOLD_CPU" ]; then
    send_alert "ALERT: High CPU Usage" "CPU usage is at ${cpu_usage}% on $(hostname)"
fi

# Check memory and alert
mem_usage=$(free | grep Mem | awk '{printf("%.0f", $3/$2 * 100)}')
if [ "$mem_usage" -gt "$ALERT_THRESHOLD_MEM" ]; then
    send_alert "ALERT: High Memory Usage" "Memory usage is at ${mem_usage}% on $(hostname)"
fi

# Check disk and alert
df -h | grep -E '^/dev/' | while read line; do
    usage=$(echo $line | awk '{print $5}' | cut -d'%' -f1)
    partition=$(echo $line | awk '{print $1}')
    
    if [ "$usage" -gt "$ALERT_THRESHOLD_DISK" ]; then
        send_alert "ALERT: Low Disk Space" "Disk usage on $partition is at ${usage}% on $(hostname)"
    fi
done

Step 5: Edit the email address

Replace [email protected] with your actual email address.

Step 6: Make it executable and test

chmod +x monitor_with_alerts.sh
./monitor_with_alerts.sh

Step 7: Test email functionality

echo "Test message" | mail -s "Test Subject" [email protected]

Check your email inbox (and spam folder) for the test message.

Automation with Cron

Schedule automatic monitoring:

Step 1: Open crontab editor

crontab -e

If this is your first time, select your preferred editor (nano is easiest for beginners).

Step 2: Add automation rules

Add these lines to the bottom of the file:

# Run monitoring with logging every 5 minutes
*/5 * * * * /path/to/monitor_with_logging.sh

# Check for alerts every 10 minutes
*/10 * * * * /path/to/monitor_with_alerts.sh

# Daily summary report at 8 AM
0 8 * * * /path/to/system_monitor.sh | mail -s "Daily System Report" [email protected]

Step 3: Replace paths

Change /path/to/ to the actual location of your scripts. For example:

*/5 * * * * /home/username/system_monitor.sh

Or if you moved it to a system location:

*/5 * * * * /usr/local/bin/system_monitor

Step 4: Save and exit

In nano: Press Ctrl + X, then Y, then Enter

Step 5: Verify cron jobs are scheduled

crontab -l

This displays all your scheduled cron jobs.

Step 6: Check cron execution logs

# View cron logs
grep CRON /var/log/syslog | tail -20

# Or on some systems
tail -f /var/log/cron

Understanding Cron Syntax:

* * * * * command
│ │ │ │ │
│ │ │ │ └─── Day of week (0-7, Sunday = 0 or 7)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of month (1-31)
│ └───────── Hour (0-23)
└─────────── Minute (0-59)

Common Cron Examples:

# Every minute
* * * * * /path/to/script.sh

# Every 15 minutes
*/15 * * * * /path/to/script.sh

# Every hour at minute 30
30 * * * * /path/to/script.sh

# Every day at midnight
0 0 * * * /path/to/script.sh

# Every Monday at 9 AM
0 9 * * 1 /path/to/script.sh

# First day of every month at 3 AM
0 3 1 * * /path/to/script.sh

Troubleshooting Common Issues

  • Script Shows Incorrect CPU Usage:
    Some systems display CPU differently. Try alternative commands:

    # Alternative CPU check
    mpstat 1 1 | awk '/Average/ {print 100-$NF}'
  • Memory Calculations Are Wrong:
    Ensure you’re using the right fields from free:

    # Modern systems
    free | grep Mem | awk '{printf("%.0f", $3/$2 * 100)}'
    
  • Disk Usage Not Showing All Partitions:
    Use df -hT to see filesystem types and filter accordingly:

    df -hT | grep -E 'ext4|xfs|btrfs'
    
  • Email Alerts Not Working:
    Install and configure mail utilities:

    sudo apt-get install mailutils
    sudo dpkg-reconfigure postfix
    

Conclusion

Building a system health monitoring dashboard with shell scripts gives you complete control over what you monitor and how you respond to issues. Start with the basic script provided, then gradually add features that match your specific needs. The modular nature of shell scripts makes it easy to expand functionality over time.

Remember that monitoring is not just about collecting data but about taking action on that data. Set up alerts, review logs regularly, and continuously refine your thresholds based on your system’s behavior.

Related guides

Account services are unavailable until the production backend is configured.