Linux System Health Monitor
Build a Python system monitor with CPU, memory, and disk alerts.
Introduction
30 Second Summary
Everyone has experienced a sluggish computer, but few know how to diagnose what is actually happening under the hood. Engineers solve this by writing monitoring scripts that report exactly where system resources are being consumed.
In this project, you will build a command-line system health monitor in Python that reports CPU, memory, and disk usage on your Linux machine. You will develop each feature on its own Git branch, following the same workflow professional engineering teams use every day.
What You'll Build
A Python script that uses psutil to scan your Ubuntu system and print a formatted health report with threshold-based alerts.
By the end of this project, you'll have:
- A working Python system health monitor that reports CPU, memory, and disk usage with warning alerts.
- A GitHub repository with a clean commit history showing proper feature-branch workflow.
- Muscle memory for the Git branch-commit-merge-push cycle used in professional engineering teams.
- Secret Mission: Add network I/O monitoring and top-5 process reporting to your monitor.
Are there any prerequisites?
This project assumes you have WSL2 with Ubuntu installed, VS Code set up for remote WSL development, and a GitHub account. Step 1 covers installing the remaining tools you need.
Before We Start
Before writing any monitoring code, you need the right tools in place. This project uses psutil (a Python library for reading system metrics) and Git (for tracking your code changes with feature branches).
In this step, you will verify your Python installation, add the psutil library, and create a GitHub repository to hold your project.
In this step, get ready to:
- Verify Python 3.10 and install the psutil library.
- Create a GitHub repository and clone it locally.
- Configure your Git identity and confirm you are on the main branch.
Verify Python and install psutil
Your Ubuntu WSL2 environment comes with Python pre-installed. You need to confirm the version and then add psutil, the library your monitor will use to read CPU, memory, and disk stats.
- In VS Code, open a terminal by clicking Terminal in the top menu bar, then selecting New Terminal.
- Make sure your terminal says WSL: Ubuntu in the bottom-left corner of VS Code or in the terminal dropdown.
- Verify Python is available by running this command:
python3 --version
You should see output like Python 3.10.x (the last number may vary).
- Install psutil by running this command:
pip install psutil==7.2.2
What is psutil?
psutil (Python system and process utilities) is a library that lets Python read live system metrics. It works across Linux, macOS, and Windows, so monitoring scripts built with it are portable.
- Confirm psutil installed correctly by running:
python3 -c "import psutil; print(psutil.__version__)"
You should see 7.2.2 printed in your terminal.
Seeing an error when importing psutil?
If you see ModuleNotFoundError, the install may have targeted a different Python version. Try running pip3 install psutil==7.2.2 instead.
If you see a permissions error, try adding --user to the end of the install command.
help me troubleshoot my psutil installation on Ubuntu WSL2
Create a GitHub repository and clone it
Every feature you build will live on its own Git branch and get merged into a central main branch. You need a GitHub repository to host this history.
- Open your browser and go to github.com.
- Click the + icon in the top-right corner, then select New repository.
- Enter linux-health-monitor as the Repository name.
- Set visibility to Public.
- Check the box labeled Add a README file.
- Click the .gitignore template dropdown and select Python.
- Click Create repository.
Why add a .gitignore?
The Python .gitignore template tells Git to ignore files like __pycache__/ and virtual environment folders. These files clutter a repository and should never be committed.
- Back in your VS Code terminal, move to your home directory and clone the repository by running these commands:
cd ~
git clone https://github.com/[[GITHUB_USERNAME="YOUR_USERNAME"]]/linux-health-monitor.git
cd linux-health-monitor
Where do I find my clone URL?
On your new repository page, click the green Code button. Copy the HTTPS URL shown there and use it in place of the URL above.
You should see Git download the repository files into a new linux-health-monitor folder.
Clone failing with authentication error?
GitHub requires authentication for HTTPS clones. If you are prompted for credentials, you may need to set up a personal access token or use SSH instead.
help me authenticate with GitHub from my WSL2 terminal
Configure Git and verify
Git needs to know who you are so each commit is tagged with your name and email. This identity appears in your project's commit history on GitHub.
- Set your Git identity by running these commands:
git config user.name "[[GIT_NAME="Your Name"]]"
git config user.email "[[GIT_EMAIL="your-email@example.com"]]"
What does git config do?
These commands set your name and email for commits made in this repository. If you have already configured Git globally, you can skip this step.
- Verify you are on the main branch by running:
git branch
You should see * main in the output, indicating you are on the main branch.
Seeing master instead of main?
Some Git configurations default to master as the primary branch name. Since you initialized the repository on GitHub with a README, the default branch should already be main. If it shows master, rename it by running git branch -M main.
help me fix my default Git branch name in WSL2
Your environment is locked and loaded. Next up, you will write your first monitoring function and learn the Git feature-branch workflow by building a CPU report.
Build the CPU Monitor
Your environment is set up and ready to go. Now it's time to write your first real feature: a CPU monitor that reads live data from your system.
Instead of coding directly on the main branch, you'll use a Git feature branch. This isolates your work so that main always stays stable. Once the feature is complete, you merge it back in.
In this step, get ready to:
- Create a feature branch for isolated development.
- Write a Python function that reads CPU stats using psutil.
- Complete the branch-commit-merge cycle to land your feature on main.
Create a feature branch
A feature branch is a short-lived copy of your codebase where you build one thing. If something breaks, main is unaffected.
- In your terminal, create and switch to a new branch by running this command:
git checkout -b feature/cpu-monitor
What does this command do?
The git checkout -b command does two things in one: it creates a new branch called feature/cpu-monitor and immediately switches you onto it. Everything you commit from here stays on this branch until you merge.
Why use feature branches instead of committing to main?
Engineering teams keep main stable at all times. Each developer works on their own branch and only merges when the feature is tested and ready. This is the workflow you'll use in internships and jobs.
It also gives you a clean Git history. Each merge commit tells a story: "this is when CPU monitoring was added."
Write the CPU monitoring function
Your monitor will use psutil to pull real-time CPU data from your Linux system. The function will capture usage percentage, core counts, and clock frequency.
- In VS Code, create a new file called health_monitor.py inside your linux-health-monitor folder.
- Add the following code to health_monitor.py:
import psutil
def get_cpu_report():
# Gather CPU stats from the system
cpu_percent = psutil.cpu_percent(interval=1)
cpu_count_logical = psutil.cpu_count(logical=True)
cpu_count_physical = psutil.cpu_count(logical=False)
cpu_freq = psutil.cpu_freq()
# Build a formatted report as a list of strings
report = []
report.append("=" * 50)
report.append("CPU REPORT")
report.append("=" * 50)
report.append(f" Usage: {cpu_percent}%")
report.append(f" Logical cores: {cpu_count_logical}")
report.append(f" Physical cores: {cpu_count_physical}")
if cpu_freq:
report.append(f" Frequency: {cpu_freq.current:.0f} MHz")
report.append("")
return cpu_percent, "\n".join(report)
if __name__ == "__main__":
usage, report = get_cpu_report()
print(report)
What does this code do?
The get_cpu_report() function uses four psutil calls to gather CPU data from your operating system:
- psutil.cpu_percent(interval=1) measures CPU usage over a 1-second window. Without the interval, the first call always returns 0.0.
- psutil.cpu_count() returns either logical or physical core counts depending on the flag you pass.
- psutil.cpu_freq() returns the current clock speed. It may return None on some WSL2 setups, which is why the code checks with if cpu_freq: before printing.
The if __name__ == "__main__": block means this code only runs when you execute the file directly. It won't run if another script imports it later.
- Save health_monitor.py.
- Run the script to verify it works by running this command:
python3 health_monitor.py
You should see a formatted CPU report with a row of equals signs, your actual CPU usage percentage, core counts, and possibly a frequency value.
Seeing an error instead of a report?
- If you see ModuleNotFoundError: No module named 'psutil', run pip install psutil==7.2.2 again and make sure it completes without errors.
- If you see SyntaxError, check that your f-strings use curly braces {} around variables and that every opening parenthesis has a matching close.
- Make sure you are running the command from inside the ~/linux-health-monitor folder where the file lives.
Why isn't my health_monitor.py script running?
✔️ Awesome, I've got everything!
Great. Make sure you've saved health_monitor.py before continuing.
ⓧ I'd like to double check the full code
import psutil
def get_cpu_report():
cpu_percent = psutil.cpu_percent(interval=1)
cpu_count_logical = psutil.cpu_count(logical=True)
cpu_count_physical = psutil.cpu_count(logical=False)
cpu_freq = psutil.cpu_freq()
report = []
report.append("=" * 50)
report.append("CPU REPORT")
report.append("=" * 50)
report.append(f" Usage: {cpu_percent}%")
report.append(f" Logical cores: {cpu_count_logical}")
report.append(f" Physical cores: {cpu_count_physical}")
if cpu_freq:
report.append(f" Frequency: {cpu_freq.current:.0f} MHz")
report.append("")
return cpu_percent, "\n".join(report)
if __name__ == "__main__":
usage, report = get_cpu_report()
print(report)
Commit and merge to main
Your CPU monitor works. Time to save this progress with Git and merge it back into main.
- Complete the full branch-commit-merge cycle by running these commands:
git add health_monitor.py
git commit -m "feat: add CPU monitoring function"
git checkout main
git merge feature/cpu-monitor
git branch -d feature/cpu-monitor
What does each command do?
- git add health_monitor.py stages the file so Git knows to include it in the next commit.
- git commit -m "..." saves a snapshot of your staged changes with a descriptive message.
- git checkout main switches you back to the main branch.
- git merge feature/cpu-monitor brings all commits from the feature branch into main.
- git branch -d feature/cpu-monitor deletes the feature branch now that its work is safely merged.
This is the complete feature-branch cycle you'll repeat for every feature: branch, code, commit, merge, delete.
- Verify your commit landed on main by running:
git log --oneline
You should see your commit message feat: add CPU monitoring function in the output.
Don't see your commit on main?
- Run git branch to confirm you're on main. If you're still on feature/cpu-monitor, run git checkout main first.
- If the merge says "Already up to date", the commit may not have been created. Check git status on the feature branch to see if files were staged.
Why doesn't git log show my commit after merging?
You've completed your first full feature-branch cycle. Next up, you'll add memory and disk monitoring on a brand new branch.
Build the Memory and Disk Monitor
Your CPU monitor is working and merged into main. Now you need the other half of the picture: how much memory and disk space is your system actually using?
In this step, you will create a second feature branch and build two new functions that report memory consumption and disk utilization. You will follow the same branch-code-commit-merge cycle you used for the CPU monitor.
In this step, get ready to:
- Create a new feature branch for memory and disk monitoring.
- Write functions that report RAM, swap, and disk usage.
- Commit your work and merge it back into main.
Create a new feature branch
Just like the CPU monitor, memory and disk monitoring gets its own isolated branch. This keeps main stable while you develop.
- Create and switch to a new feature branch by running this command:
git checkout -b feature/mem-disk
- Verify you are on the new branch by running:
git branch
You should see * feature/mem-disk highlighted in the output, confirming you are on the new branch.
Still on main?
If git branch shows * main, make sure you typed checkout -b (with the -b flag). Without -b, Git tries to switch to an existing branch instead of creating a new one.
Help me switch to my feature branch
Add the memory monitoring function
The memory function uses psutil to read your system's RAM and swap usage. It converts raw bytes into human-readable gigabytes.
- In health_monitor.py, find the if __name__ == "__main__": line at the bottom of the file.
- Add the following function directly above that line:
def get_memory_report():
# Pull RAM and swap stats from the operating system
mem = psutil.virtual_memory()
swap = psutil.swap_memory()
# Convert bytes to gigabytes for readability
total_gb = mem.total / (1024 ** 3)
used_gb = mem.used / (1024 ** 3)
available_gb = mem.available / (1024 ** 3)
# Build the formatted report lines
report = []
report.append("=" * 50)
report.append("MEMORY REPORT")
report.append("=" * 50)
report.append(f" Total: {total_gb:.2f} GB")
report.append(f" Used: {used_gb:.2f} GB ({mem.percent}%)")
report.append(f" Available: {available_gb:.2f} GB")
report.append(f" Swap used: {swap.percent}%")
report.append("")
return mem.percent, "\n".join(report)
What does this code do?
psutil.virtual_memory() returns a named tuple with your total RAM, how much is used, and how much is available. psutil.swap_memory() does the same for swap space.
The raw values come in bytes, so dividing by (1024 ** 3) converts them to gigabytes. The function returns both the usage percentage (for alerts later) and the formatted report string.
- Save health_monitor.py.
You will not see a change yet because the if __name__ block does not call this function yet. The next chunk adds that.
Add the disk monitoring function
Disk monitoring checks how full your root filesystem is. This is critical information because a full disk can crash services silently.
- In health_monitor.py, add this function directly below get_memory_report() and above the if __name__ block:
def get_disk_report():
# Check the root filesystem usage
disk = psutil.disk_usage("/")
# Convert bytes to gigabytes
total_gb = disk.total / (1024 ** 3)
used_gb = disk.used / (1024 ** 3)
free_gb = disk.free / (1024 ** 3)
# Build the formatted report lines
report = []
report.append("=" * 50)
report.append("DISK REPORT")
report.append("=" * 50)
report.append(f" Total: {total_gb:.2f} GB")
report.append(f" Used: {used_gb:.2f} GB ({disk.percent}%)")
report.append(f" Free: {free_gb:.2f} GB")
report.append("")
return disk.percent, "\n".join(report)
What does this code do?
psutil.disk_usage("/") checks the root filesystem (/) and returns total, used, and free space. On your WSL2 Ubuntu system, this reflects the virtual disk allocated to Linux.
Like the memory function, it returns both the percentage (for threshold alerts later) and a formatted string.
- Save health_monitor.py.
Still no visible output change yet. The next chunk wires everything together.
Update the main block to print all three reports
The existing if __name__ block only calls get_cpu_report(). You need to update it to call all three functions.
- In health_monitor.py, find this block at the bottom of the file:
if __name__ == "__main__":
usage, report = get_cpu_report()
print(report)
- Replace the entire if __name__ block with this updated version:
if __name__ == "__main__":
# Run all three report functions
cpu_usage, cpu_report = get_cpu_report()
mem_usage, mem_report = get_memory_report()
disk_usage, disk_report = get_disk_report()
# Print each report
print(cpu_report)
print(mem_report)
print(disk_report)
What changed here?
The variable names now follow a consistent pattern: cpu_usage, mem_usage, disk_usage store the percentage values. cpu_report, mem_report, disk_report store the formatted strings.
You will use those usage percentages in the next step when you build the alert system.
- Save health_monitor.py.
- Run the script to verify all three reports print by running:
python3 health_monitor.py
You should see three formatted sections: CPU REPORT, MEMORY REPORT, and DISK REPORT, each showing real data from your system.
Seeing a NameError or IndentationError?
A NameError on get_memory_report or get_disk_report means the functions are not defined above the if __name__ block. Make sure they sit between get_cpu_report() and the if __name__ block.
An IndentationError means a line is indented incorrectly. Check that all lines inside each function use exactly four spaces of indentation.
Help me fix my health_monitor.py errors
✔️ Awesome, I've got everything!
Great. Double check you have saved your file before moving on.
ⓧ I'd like to double check the full code
Here is the complete health_monitor.py at this point:
import psutil
def get_cpu_report():
cpu_percent = psutil.cpu_percent(interval=1)
cpu_count_logical = psutil.cpu_count(logical=True)
cpu_count_physical = psutil.cpu_count(logical=False)
cpu_freq = psutil.cpu_freq()
report = []
report.append("=" * 50)
report.append("CPU REPORT")
report.append("=" * 50)
report.append(f" Usage: {cpu_percent}%")
report.append(f" Logical cores: {cpu_count_logical}")
report.append(f" Physical cores: {cpu_count_physical}")
if cpu_freq:
report.append(f" Frequency: {cpu_freq.current:.0f} MHz")
report.append("")
return cpu_percent, "\n".join(report)
def get_memory_report():
mem = psutil.virtual_memory()
swap = psutil.swap_memory()
total_gb = mem.total / (1024 ** 3)
used_gb = mem.used / (1024 ** 3)
available_gb = mem.available / (1024 ** 3)
report = []
report.append("=" * 50)
report.append("MEMORY REPORT")
report.append("=" * 50)
report.append(f" Total: {total_gb:.2f} GB")
report.append(f" Used: {used_gb:.2f} GB ({mem.percent}%)")
report.append(f" Available: {available_gb:.2f} GB")
report.append(f" Swap used: {swap.percent}%")
report.append("")
return mem.percent, "\n".join(report)
def get_disk_report():
disk = psutil.disk_usage("/")
total_gb = disk.total / (1024 ** 3)
used_gb = disk.used / (1024 ** 3)
free_gb = disk.free / (1024 ** 3)
report = []
report.append("=" * 50)
report.append("DISK REPORT")
report.append("=" * 50)
report.append(f" Total: {total_gb:.2f} GB")
report.append(f" Used: {used_gb:.2f} GB ({disk.percent}%)")
report.append(f" Free: {free_gb:.2f} GB")
report.append("")
return disk.percent, "\n".join(report)
if __name__ == "__main__":
cpu_usage, cpu_report = get_cpu_report()
mem_usage, mem_report = get_memory_report()
disk_usage, disk_report = get_disk_report()
print(cpu_report)
print(mem_report)
print(disk_report)
Commit and merge to main
Your memory and disk functions are working. Time to complete the Git feature-branch cycle: commit, switch to main, merge, and delete the branch.
- Stage, commit, merge, and clean up the feature branch by running these commands:
git add health_monitor.py
git commit -m "feat: add memory and disk monitoring"
git checkout main
git merge feature/mem-disk
git branch -d feature/mem-disk
What do these commands do?
- git add stages your changes for the next commit.
- git commit -m saves a snapshot with a descriptive message.
- git checkout main switches back to the main branch.
- git merge feature/mem-disk brings your feature branch changes into main.
- git branch -d deletes the feature branch now that its work is merged.
- Verify your commit history by running:
git log --oneline
You should see both your commits on main: the CPU monitoring commit from earlier and the new memory/disk monitoring commit.
Merge failed or branch not deleted?
If you see a merge conflict, make sure you did not edit health_monitor.py on main between steps. Run git status to check for uncommitted changes.
If git branch -d says the branch is not fully merged, try running the merge command again. The -d flag only deletes branches whose commits are already in main.
Help me fix my Git merge issue
Two feature branches down, one to go. Your monitor now covers CPU, memory, and disk. Next up, you will build an alert system that flags when any metric crosses a dangerous threshold, and push your finished project to GitHub.
Build the Alert System and Push to GitHub
Your monitor now reports CPU, memory, and disk usage. That is useful information, but a wall of numbers forces you to mentally check whether each value is "too high." Real monitoring tools solve this by setting thresholds and surfacing warnings automatically.
In this step you will add an alert system that compares each metric against a threshold and prints a warning when something is out of range. You will also push your finished project to GitHub so your commit history is visible to anyone reviewing your work.
In this step, get ready to:
- Create a feature branch for the alert system.
- Write a threshold-based alert function that flags high resource usage.
- Commit, merge, and push the final project to GitHub.
Create the alerts feature branch
Every new feature starts on its own branch. This keeps your work isolated until you are confident it is ready to merge.
- Create and switch to a new branch by running this command:
git checkout -b feature/alerts
What does this command do?
The git checkout -b flag creates a new branch and switches to it in a single step. You are now working on feature/alerts, completely separate from main.
Add the alert function and update the main block
The alert function will compare each usage percentage against a hard-coded threshold. If usage exceeds the limit, it appends a warning message to the report.
- In health_monitor.py, find the if __name__ block at the bottom of the file.
- Add the following check_alerts function directly above that if __name__ block:
def check_alerts(cpu_usage, mem_usage, disk_usage):
# Collect any warnings that exceed thresholds
alerts = []
thresholds = {"CPU": 80, "Memory": 80, "Disk": 75}
if cpu_usage > thresholds["CPU"]:
alerts.append(f" [WARNING] CPU usage at {cpu_usage}% (threshold: {thresholds['CPU']}%)")
if mem_usage > thresholds["Memory"]:
alerts.append(f" [WARNING] Memory usage at {mem_usage}% (threshold: {thresholds['Memory']}%)")
if disk_usage > thresholds["Disk"]:
alerts.append(f" [WARNING] Disk usage at {disk_usage}% (threshold: {thresholds['Disk']}%)")
# Build the alerts section of the report
report = []
report.append("=" * 50)
report.append("ALERTS")
report.append("=" * 50)
if alerts:
report.extend(alerts)
else:
report.append(" All systems healthy. No alerts.")
report.append("")
return "\n".join(report)
What does this code do?
The thresholds dictionary defines the maximum acceptable percentage for CPU (80%), Memory (80%), and Disk (75%).
Each if statement compares the live usage value against its threshold. When usage is too high, a [WARNING] line is appended. If nothing is over the limit, the report prints "All systems healthy."
Now you need to replace the existing if __name__ block so it prints a header, a timestamp, and includes the new alert report.
- Find the current if __name__ block at the bottom of the file:
if __name__ == "__main__":
cpu_usage, cpu_report = get_cpu_report()
mem_usage, mem_report = get_memory_report()
disk_usage, disk_report = get_disk_report()
print(cpu_report)
print(mem_report)
print(disk_report)
- Delete that entire block and replace it with the following:
if __name__ == "__main__":
# Print a header with the current timestamp
print()
print(" LINUX SYSTEM HEALTH MONITOR")
print(f" Generated at: {__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
# Gather all reports
cpu_usage, cpu_report = get_cpu_report()
mem_usage, mem_report = get_memory_report()
disk_usage, disk_report = get_disk_report()
alert_report = check_alerts(cpu_usage, mem_usage, disk_usage)
# Print the full health report
print(cpu_report)
print(mem_report)
print(disk_report)
print(alert_report)
What changed in the main block?
The new block prints a title banner and a timestamp so each run is identifiable. It also calls check_alerts() with all three usage values and prints the resulting alert section at the end of the report.
The __import__('datetime') trick imports the datetime module inline so you do not need a separate import line at the top of the file.
- Save health_monitor.py.
- Run the script to verify the full report by running this command:
python3 health_monitor.py
You should see a title reading "LINUX SYSTEM HEALTH MONITOR", a timestamp, then CPU, Memory, Disk, and Alerts sections. If all your metrics are below the thresholds you will see "All systems healthy. No alerts."
Seeing a syntax error or missing output?
- Make sure the check_alerts function is placed above the if __name__ block. Python reads top to bottom, so calling a function before defining it causes a NameError.
- Check that you deleted the entire old if __name__ block before pasting the new one. Two competing blocks cause indentation errors.
- Verify the f-string quotes match. The threshold dictionary uses double quotes on the outside and single quotes for the key lookups inside the f-string curly braces.
Help me debug my health_monitor.py alert function
✔️ Awesome, I've got everything!
Great. Double check you have saved health_monitor.py before moving on.
I'd like to double check the full code
Here is the complete health_monitor.py at this point:
import psutil
def get_cpu_report():
cpu_percent = psutil.cpu_percent(interval=1)
cpu_count_logical = psutil.cpu_count(logical=True)
cpu_count_physical = psutil.cpu_count(logical=False)
cpu_freq = psutil.cpu_freq()
report = []
report.append("=" * 50)
report.append("CPU REPORT")
report.append("=" * 50)
report.append(f" Usage: {cpu_percent}%")
report.append(f" Logical cores: {cpu_count_logical}")
report.append(f" Physical cores: {cpu_count_physical}")
if cpu_freq:
report.append(f" Frequency: {cpu_freq.current:.0f} MHz")
report.append("")
return cpu_percent, "\n".join(report)
def get_memory_report():
mem = psutil.virtual_memory()
swap = psutil.swap_memory()
total_gb = mem.total / (1024 ** 3)
used_gb = mem.used / (1024 ** 3)
available_gb = mem.available / (1024 ** 3)
report = []
report.append("=" * 50)
report.append("MEMORY REPORT")
report.append("=" * 50)
report.append(f" Total: {total_gb:.2f} GB")
report.append(f" Used: {used_gb:.2f} GB ({mem.percent}%)")
report.append(f" Available: {available_gb:.2f} GB")
report.append(f" Swap used: {swap.percent}%")
report.append("")
return mem.percent, "\n".join(report)
def get_disk_report():
disk = psutil.disk_usage("/")
total_gb = disk.total / (1024 ** 3)
used_gb = disk.used / (1024 ** 3)
free_gb = disk.free / (1024 ** 3)
report = []
report.append("=" * 50)
report.append("DISK REPORT")
report.append("=" * 50)
report.append(f" Total: {total_gb:.2f} GB")
report.append(f" Used: {used_gb:.2f} GB ({disk.percent}%)")
report.append(f" Free: {free_gb:.2f} GB")
report.append("")
return disk.percent, "\n".join(report)
def check_alerts(cpu_usage, mem_usage, disk_usage):
alerts = []
thresholds = {"CPU": 80, "Memory": 80, "Disk": 75}
if cpu_usage > thresholds["CPU"]:
alerts.append(f" [WARNING] CPU usage at {cpu_usage}% (threshold: {thresholds['CPU']}%)")
if mem_usage > thresholds["Memory"]:
alerts.append(f" [WARNING] Memory usage at {mem_usage}% (threshold: {thresholds['Memory']}%)")
if disk_usage > thresholds["Disk"]:
alerts.append(f" [WARNING] Disk usage at {disk_usage}% (threshold: {thresholds['Disk']}%)")
report = []
report.append("=" * 50)
report.append("ALERTS")
report.append("=" * 50)
if alerts:
report.extend(alerts)
else:
report.append(" All systems healthy. No alerts.")
report.append("")
return "\n".join(report)
if __name__ == "__main__":
print()
print(" LINUX SYSTEM HEALTH MONITOR")
print(f" Generated at: {__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
cpu_usage, cpu_report = get_cpu_report()
mem_usage, mem_report = get_memory_report()
disk_usage, disk_report = get_disk_report()
alert_report = check_alerts(cpu_usage, mem_usage, disk_usage)
print(cpu_report)
print(mem_report)
print(disk_report)
print(alert_report)
Commit, merge, and push to GitHub
Your alert feature is working. Time to lock it into Git history, merge it back into main, and push the complete project to your remote repository.
- Stage, commit, merge, delete the branch, and push by running these commands:
git add health_monitor.py
git commit -m "feat: add threshold alerts and formatted output"
git checkout main
git merge feature/alerts
git branch -d feature/alerts
git push origin main
What do these commands do?
- git add stages your changes for commit.
- git commit -m saves the staged snapshot with a descriptive message.
- git checkout main switches back to the main branch.
- git merge feature/alerts brings your alert work into main.
- git branch -d feature/alerts deletes the now-merged feature branch to keep things tidy.
- git push origin main uploads your local main branch to GitHub so the code is publicly visible.
- Verify your commit history by running:
git log --oneline
You should see three commits on main: one for the CPU monitor, one for memory and disk, and one for alerts.
- Open your browser and visit your GitHub repository page.
- You should see health_monitor.py listed with the commit history showing all three feature-branch merge commits.
Push rejected or authentication failed?
- If you see a "permission denied" error, your Git credentials may not be configured for this repository. Make sure you cloned using HTTPS and that your GitHub credentials (or personal access token) are cached.
- If the push is rejected because the remote contains work you do not have locally, run git pull --rebase origin main first, then try the push again.
Help me push my code to GitHub
Secret mission
Add Network and Process Monitoring
Your health monitor covers CPU, memory, and disk. But real monitoring scripts also track network activity and which processes are hogging resources. Can you extend your monitor with two new functions using psutil APIs you haven't seen yet?
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally with a free GitHub repository, so there are no ongoing costs.
Resources you used:
- linux-health-monitor local directory (~/linux-health-monitor).
- linux-health-monitor GitHub repository.
- psutil Python package.
Keep everything running
No action needed. Choose this if you are still actively building or plan to extend your health monitor with new features.
- Your ~/linux-health-monitor folder stays on disk and you can run python3 health_monitor.py any time.
- Your GitHub repository remains public and accessible at your profile.
- The psutil package remains installed in your Python environment.
Pause - I'll come back to this later
Nothing is actively running, so there is nothing to pause. Your files and repository will be waiting for you when you come back.
- Your local files stay in ~/linux-health-monitor.
- Your GitHub repository stays online.
- No processes are running in the background, so there is nothing to stop.
Delete - I don't want to use this again
Remove all project resources and start fresh. Follow these steps to fully clean up.
- Delete the local project folder by running this command in your Ubuntu terminal:
rm -rf ~/linux-health-monitor
- Uninstall the psutil package by running:
pip uninstall psutil -y
- Delete the GitHub repository:
- Open your linux-health-monitor repository on GitHub.
- Click Settings (under the repository name).
- Scroll down to the Danger Zone section.
- Click Delete this repository and follow the confirmation prompts.
- Confirm the local folder is gone by running:
ls ~/linux-health-monitor
You should see an error like "No such file or directory", confirming the folder has been removed.
Nice Work!
Nice Work!
Nice work! You just built a production-style Linux system health monitor in Python and pushed it to GitHub with a clean, professional commit history.
You've learned how to:
- Build a Python system health monitor that reports CPU, memory, and disk usage with real data from your Linux system using psutil.
- Use the Git feature-branch workflow: create a branch, write code, commit, merge back into main, delete the branch, and push.
- Implement threshold-based alerting logic that triggers warnings when system metrics exceed configurable limits.
- Secret Mission: Extended the monitor with network I/O stats and top-process reporting, using more advanced psutil APIs and data sorting.
Ready to quiz yourself?