GICT knowledge base
Building a System Health Monitoring Dashboard with Shell Scripts
Published
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
Database Sharding Strategies for Growing Apps
Database sharding strategies for growing apps in 2026: when you actually need sharding, how to choose a shard key, the 5 main strategies, cross-shard query costs...
System AdministrationVector Databases Explained: pgvector for AI-Powered Apps
Vector databases explained for AI apps in 2026: what they are, why pgvector is the right default for most teams, how embeddings work, chunking strategies, production...
System AdministrationUsing AI to Monitor and Auto-Heal Your Hosting Stack
AI monitoring and auto-healing for hosting: how to detect anomalies in your stack with machine learning, automate safe fixes, and stop paging yourself for known issues.
System AdministrationPHP-FPM Configuration Guide: How to Tune Pools for Maximum Server Performance
Learn how to tune PHP-FPM pools for maximum server performance, from picking the right process manager mode to calculating pm.max_children from real memory.
