<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mide Sofek</title>
    <description>The latest articles on DEV Community by Mide Sofek (@midesofek).</description>
    <link>https://dev.to/midesofek</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4113350%2Ffb446aa2-7a04-45fb-9603-f60a02690a4b.png</url>
      <title>DEV Community: Mide Sofek</title>
      <link>https://dev.to/midesofek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/midesofek"/>
    <language>en</language>
    <item>
      <title>How I Built a Server Health-Check &amp; Slack Alerting Script Using Only Bash</title>
      <dc:creator>Mide Sofek</dc:creator>
      <pubDate>Mon, 07 Sep 2026 20:37:07 +0000</pubDate>
      <link>https://dev.to/midesofek/how-i-built-a-server-health-check-slack-alerting-script-using-only-bash-4ne4</link>
      <guid>https://dev.to/midesofek/how-i-built-a-server-health-check-slack-alerting-script-using-only-bash-4ne4</guid>
      <description>&lt;p&gt;In this tutorial, we will build a simple bash script that monitors server health and sends alerts to Slack via a webhook.&lt;/p&gt;

&lt;p&gt;The script checks four things: &lt;strong&gt;disk, memory, CPU, and a running service&lt;/strong&gt;. If any of them crosses a threshold you set, the script sends a Slack message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you start
&lt;/h2&gt;

&lt;p&gt;You will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Working Laptop (this tutorial uses macOS &amp;amp; macOS commands)&lt;/li&gt;
&lt;li&gt;A terminal with bash&lt;/li&gt;
&lt;li&gt;A Slack account&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;A note on macOS vs Linux.&lt;/strong&gt; Some of the commands here are macOS-specific. For instance, &lt;code&gt;vm_stat&lt;/code&gt; and &lt;code&gt;top -l 1&lt;/code&gt; do not exist on Linux. You would use &lt;code&gt;free -m&lt;/code&gt; and &lt;code&gt;top -bn1&lt;/code&gt; instead. &lt;code&gt;df -h&lt;/code&gt; and &lt;code&gt;pgrep&lt;/code&gt; work on both. I will flag each one as we go.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I will be using the &lt;code&gt;vim&lt;/code&gt; text editor in this tutorial. If you prefer another editor like VS Code, nano, or Cursor, copy the final code from the &lt;a href="https://github.com/midesofek/Server-Health-Status-Alert-Bash-Script" rel="noopener noreferrer"&gt;public repo&lt;/a&gt; and follow along.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Create a Slack Webhook URL
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;a href="https://api.slack.com/apps" rel="noopener noreferrer"&gt;api.slack.com/apps&lt;/a&gt; and click &lt;strong&gt;Create New App&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj1d46nmlonntwb0g9y5o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj1d46nmlonntwb0g9y5o.png" alt="How to Create a new Slack App by Mide Sofek" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose &lt;strong&gt;Blank App&lt;/strong&gt;, give the app a name (e.g. &lt;code&gt;Server Health Bot&lt;/code&gt;), and pick the workspace you want alerts to land in (create a workspace if you haven't)&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0g8kschxup1prel0ist7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0g8kschxup1prel0ist7.png" alt="How to Create a Blank Slack App by Mide Sofek" width="800" height="436"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsdxsobxatpd0i5eq15q5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsdxsobxatpd0i5eq15q5.png" alt="How to Create a Blank Slack App from Scratch by Mide Sofek" width="799" height="441"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the sidebar, open &lt;strong&gt;Incoming Webhooks&lt;/strong&gt; and toggle &lt;strong&gt;Activate Incoming Webhooks&lt;/strong&gt; on&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkkspotg96xnsov8caxfj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkkspotg96xnsov8caxfj.png" alt="How to Set Up Incoming Webhooks in Slack App by Mide Sofek" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll down and click &lt;strong&gt;Add New Webhook to Workspace&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fme4ppuqym0x9x3rkg618.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fme4ppuqym0x9x3rkg618.png" alt="How to Add New Webhook to a Slack App by Mide Sofek" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose the channel the alerts should post to, then click &lt;strong&gt;Allow&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flm7j0modikq6b7vhqtqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flm7j0modikq6b7vhqtqw.png" alt="How to Allow Permissions in Slack App by Mide Sofek" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the webhook URL that appears. It will look like this:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ Treat this URL like a password. Anyone who has it can post to your channel.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 2: Add the Webhook URL to &lt;code&gt;.env&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In your terminal, run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 The &lt;code&gt;&amp;gt;&lt;/code&gt; operator is called &lt;strong&gt;output redirection&lt;/strong&gt;. It takes whatever &lt;code&gt;echo&lt;/code&gt; prints and writes it to &lt;code&gt;.env&lt;/code&gt; instead of your screen. If &lt;code&gt;.env&lt;/code&gt; doesn't exist, it gets created. If it &lt;em&gt;does&lt;/em&gt; exist, &lt;code&gt;&amp;gt;&lt;/code&gt; overwrites it completely, use &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; when you want to append instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, protect &lt;code&gt;.env&lt;/code&gt; from being accidentally committed to git by listing it in &lt;code&gt;.gitignore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 Note the &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; here. Your project may already have a &lt;code&gt;.gitignore&lt;/code&gt;, and &lt;code&gt;&amp;gt;&lt;/code&gt; would wipe it. &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; appends a new line and still creates the file if it's missing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 3: Write the Bash Script
&lt;/h2&gt;

&lt;p&gt;We will build the script in stages, testing as we go:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the file with a shebang&lt;/li&gt;
&lt;li&gt;Write the alert function, then test it&lt;/li&gt;
&lt;li&gt;Add the disk check&lt;/li&gt;
&lt;li&gt;Add the memory check&lt;/li&gt;
&lt;li&gt;Add the CPU check&lt;/li&gt;
&lt;li&gt;Add the service check&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Building it this way means that when something breaks, you know exactly which piece broke.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Create the script file
&lt;/h3&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim health_check.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuaoh84ej96ocstfju3lr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuaoh84ej96ocstfju3lr.png" alt="How to Create a bash Script by Mide Sofek" width="798" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This opens the vim text editor with the filename &lt;code&gt;health_check.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Press &lt;code&gt;i&lt;/code&gt; to enter INSERT mode, then type the shebang at the top of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa3dafuo78rl5k4kkbjvq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa3dafuo78rl5k4kkbjvq.png" alt="How to set Insert Mode in vim by Mide Sofek" width="799" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 The shebang tells your system which interpreter should run the file. You have to place this at the very &lt;strong&gt;first line&lt;/strong&gt;. If you place a blank line or comment above it, it stops working.&lt;/p&gt;

&lt;p&gt;Also note: bash scripts &lt;strong&gt;conventionally&lt;/strong&gt; end in &lt;code&gt;.sh&lt;/code&gt;, but the extension is optional, and the script runs fine without it. The shebang, on the other hand, is &lt;strong&gt;required&lt;/strong&gt; if you want to execute the script directly with &lt;code&gt;./health_check.sh&lt;/code&gt;. You can skip it when you always run &lt;code&gt;bash health_check.sh&lt;/code&gt;, but leaving it out is a habit that will bite you later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once done, press &lt;code&gt;esc&lt;/code&gt;, then type &lt;code&gt;:wq&lt;/code&gt; to write and quit the editor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flw54ae9fhyntl6mlg7bt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flw54ae9fhyntl6mlg7bt.png" alt="Saving vim scripts with :wq by Mide Sofek" width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Write the alert function
&lt;/h3&gt;

&lt;p&gt;Before we touch any monitoring logic, we will set up the alerting side first: a reusable function that sends a message to Slack from the terminal.&lt;/p&gt;

&lt;p&gt;Doing this first means every check we write afterwards has somewhere to report to, and we can confirm Slack works before adding anything that might obscure the problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;THE FULL PATH TO YOUR .env file&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="c"&gt;# e.g: source /Users/mac/MyProjects/linux_lessons/ServerHealthStatusAlert/.env&lt;/span&gt;

send_alert&lt;span class="o"&gt;(){&lt;/span&gt;
 &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-type: application/json'&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$msg&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; &lt;span class="nv"&gt;$SLACK_WEBHOOK_URL&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 Watch your quotes in the &lt;code&gt;curl&lt;/code&gt; payload. Single quotes in bash prevent variable expansion, so &lt;code&gt;'{"text": "$MESSAGE"}'&lt;/code&gt; sends the literal string &lt;code&gt;$MESSAGE&lt;/code&gt; to Slack rather than your message. You need double quotes around the parts that contain variables.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Test it before moving on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;send_alert &lt;span class="s2"&gt;"Bash Scripting is Cool!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a message shows up in your Slack channel, the hard part is done.&lt;/p&gt;

&lt;p&gt;Your script should now look like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;code so far&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;source&lt;/span&gt; /Users/mac/MyProjects/Devops_Mastery/linux_lessons/ServerHealthStatusAlert/.env

send_alert&lt;span class="o"&gt;(){&lt;/span&gt;
 &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-type: application/json'&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$msg&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; &lt;span class="nv"&gt;$SLACK_WEBHOOK_URL&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
send_alert &lt;span class="s2"&gt;"Bash Scripting is Cool!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;delete this test call before moving on&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.3 Disk check
&lt;/h3&gt;

&lt;p&gt;To read disk usage, we will use &lt;code&gt;df -h&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvbis1xz9knabu490y7rb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvbis1xz9knabu490y7rb.png" alt="How to read disk usage in Linux by Mide Sofek" width="799" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The disk check Logic&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;disk_usage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'/$'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $5 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/%//"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;disk_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$disk_usage&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; &lt;span class="nv"&gt;$disk_threshold&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;send_alert &lt;span class="s2"&gt;"🚨 [ALERT] Disk usage high on &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="nv"&gt;$disk_usage&lt;/span&gt;&lt;span class="s2"&gt;% (threshold: &lt;/span&gt;&lt;span class="nv"&gt;$disk_threshold&lt;/span&gt;&lt;span class="s2"&gt;%) - &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.4 Memory check
&lt;/h3&gt;

&lt;p&gt;To read memory usage on macOS, we will use &lt;code&gt;vm_stat&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;macOS only.&lt;/strong&gt; Linux exposes memory through &lt;code&gt;/proc/meminfo&lt;/code&gt; (usually read via &lt;code&gt;free -m&lt;/code&gt;). &lt;code&gt;vm_stat&lt;/code&gt; reports memory in &lt;strong&gt;pages&lt;/strong&gt; rather than megabytes, so we have to do a little arithmetic to get a usable percentage.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;mem_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80

&lt;span class="nv"&gt;pages_free&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Pages free"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $3 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/.&lt;/span&gt;&lt;span class="nv"&gt;$/&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;pages_active&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Pages active"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $3 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/.&lt;/span&gt;&lt;span class="nv"&gt;$/&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;pages_inactive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Pages inactive"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $3 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/.&lt;/span&gt;&lt;span class="nv"&gt;$/&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;pages_speculative&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Pages speculative"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $3 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/.&lt;/span&gt;&lt;span class="nv"&gt;$/&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;pages_wired&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Pages wired down"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $4 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/.&lt;/span&gt;&lt;span class="nv"&gt;$/&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;pages_compressed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Pages occupied by compressor:"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $5 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/.&lt;/span&gt;&lt;span class="nv"&gt;$/&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;free_group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pages_free&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="nv"&gt;$pages_speculative&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;used&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pages_active&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="nv"&gt;$pages_inactive&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="nv"&gt;$pages_wired&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="nv"&gt;$pages_compressed&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$free_group&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="nv"&gt;$used&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;used_percent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"scale=2; &lt;/span&gt;&lt;span class="nv"&gt;$used&lt;/span&gt;&lt;span class="s2"&gt; / &lt;/span&gt;&lt;span class="nv"&gt;$total&lt;/span&gt;&lt;span class="s2"&gt; * 100"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;mem_alert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$used_percent&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$mem_threshold&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$mem_alert&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 1 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;send_alert &lt;span class="s2"&gt;"🚨 [ALERT] Memory usage high on &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="nv"&gt;$used_percent&lt;/span&gt;&lt;span class="s2"&gt;% (threshold: &lt;/span&gt;&lt;span class="nv"&gt;$mem_threshold&lt;/span&gt;&lt;span class="s2"&gt;%) - &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 Bash can only compare integers. &lt;code&gt;[ 78.4 -gt 80 ]&lt;/code&gt; will throw an error, not a wrong answer. That's why we pipe (using &lt;code&gt;|&lt;/code&gt;) the calculation through &lt;code&gt;bc&lt;/code&gt;, which handles decimals and returns &lt;code&gt;1&lt;/code&gt; for true and &lt;code&gt;0&lt;/code&gt; for false.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.5 CPU check
&lt;/h3&gt;

&lt;p&gt;To read CPU usage, we will use &lt;code&gt;top -l 1&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;macOS only.&lt;/strong&gt; The &lt;code&gt;-l 1&lt;/code&gt; flag means "take one sample and exit". On Linux, the equivalent is &lt;code&gt;top -bn1&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;top&lt;/code&gt; reports CPU &lt;strong&gt;idle&lt;/strong&gt; percentage, not usage, so we get usage by subtracting from 100:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;usage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;idle&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;cpu_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80

&lt;span class="nv"&gt;cpu_idle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;top &lt;span class="nt"&gt;-l&lt;/span&gt; 1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"CPU usage:"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $7 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/%//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;cpu_usage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"100 - &lt;/span&gt;&lt;span class="nv"&gt;$cpu_idle&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;cpu_alert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cpu_usage&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$cpu_threshold&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;| bc&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$cpu_alert&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 1 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;send_alert &lt;span class="s2"&gt;"🚨 [ALERT] CPU usage high on &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="nv"&gt;$cpu_usage&lt;/span&gt;&lt;span class="s2"&gt;% (threshold: &lt;/span&gt;&lt;span class="nv"&gt;$cpu_threshold&lt;/span&gt;&lt;span class="s2"&gt;%) - &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.6 Service check
&lt;/h3&gt;

&lt;p&gt;To check whether a service is running, we will use &lt;code&gt;pgrep&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sshd"&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;pgrep &lt;span class="nv"&gt;$service_name&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"running"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"not running"&lt;/span&gt;
    send_alert &lt;span class="s2"&gt;"🚨 [ALERT] Service &lt;/span&gt;&lt;span class="nv"&gt;$service_name&lt;/span&gt;&lt;span class="s2"&gt; is not running on &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;A real limitation worth knowing.&lt;/strong&gt; &lt;code&gt;pgrep sshd&lt;/code&gt; will come back empty on macOS even when Remote Login is enabled. macOS uses launchd &lt;strong&gt;socket activation&lt;/strong&gt; — &lt;code&gt;sshd&lt;/code&gt; isn't kept running in the background, it's started on demand when a connection actually arrives. So there's no persistent process for &lt;code&gt;pgrep&lt;/code&gt; to find.&lt;/p&gt;

&lt;p&gt;On a Linux server, &lt;code&gt;pgrep sshd&lt;/code&gt; works exactly as you'd expect. To test the logic locally on a Mac, point it at a process that is always running, like &lt;code&gt;Finder&lt;/code&gt; by setting &lt;code&gt;service_name="Finder"&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Escape (with &lt;code&gt;esc&lt;/code&gt;) then save the bash file with &lt;code&gt;:wq&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj3v4cse8u0neqz2zvua5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj3v4cse8u0neqz2zvua5.png" alt="Saving vim files with :wq by Mide Sofek" width="788" height="25"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Run the script
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash health_check.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your checks run, and any that breach a threshold should show up in Slack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnc0pc7jfdxj1te51oqvl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnc0pc7jfdxj1te51oqvl.png" alt="How I Built a Server Health-Check &amp;amp; Slack Alerting Script Using Only Bash by Mide Sofek" width="797" height="70"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft7bkekbfnsi9x3yru2ft.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft7bkekbfnsi9x3yru2ft.png" alt="How I Built a Server Health-Check &amp;amp; Slack Alerting Script Using Only Bash by Mide Sofek" width="798" height="110"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 5: Schedule it with cron
&lt;/h2&gt;

&lt;p&gt;Running the script by hand defeats the point. Let's have cron run it on a schedule.&lt;/p&gt;

&lt;p&gt;Open your crontab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This opens a text editor where you can write your cron schedules. Press &lt;code&gt;i&lt;/code&gt; to enter INSERT mode if the editor doesn't do it for you.&lt;/p&gt;

&lt;p&gt;Then add your schedule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /bin/bash /absolute/path/to/health_check.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs the script at the top of every hour.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Always use absolute paths in cron.&lt;/strong&gt; Cron runs with a minimal environment and doesn't start in your project directory, so a relative path like &lt;code&gt;./health_check.sh&lt;/code&gt; fails silently. This applies to every path in your script, including your log file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're unsure what a schedule does, paste it into &lt;a href="https://crontab.guru" rel="noopener noreferrer"&gt;crontab.guru&lt;/a&gt; — it translates cron expressions into plain English.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;MacOS Note:&lt;/strong&gt; macOS sandboxing may block cron from reading files in protected directories. If your script runs fine by hand but does nothing on a schedule, grant Full Disk Access to &lt;code&gt;cron&lt;/code&gt; under &lt;strong&gt;System Settings → Privacy &amp;amp; Security → Full Disk Access&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The final script
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;source&lt;/span&gt; /Users/mac/MyProjects/Devops_Mastery/linux_lessons/ServerHealthStatusAlert/.env &lt;span class="c"&gt;# replace with your own path&lt;/span&gt;

send_alert&lt;span class="o"&gt;(){&lt;/span&gt;
 &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-type: application/json'&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$msg&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; &lt;span class="nv"&gt;$SLACK_WEBHOOK_URL&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;## DISK CHECK LOGIC&lt;/span&gt;
&lt;span class="nv"&gt;disk_usage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'/$'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $5 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/%//"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;disk_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$disk_usage&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; &lt;span class="nv"&gt;$disk_threshold&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;send_alert &lt;span class="s2"&gt;"🚨 [ALERT] Disk usage high on &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="nv"&gt;$disk_usage&lt;/span&gt;&lt;span class="s2"&gt;% (threshold: &lt;/span&gt;&lt;span class="nv"&gt;$disk_threshold&lt;/span&gt;&lt;span class="s2"&gt;%) - &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;## MEMORY CHECK LOGIC&lt;/span&gt;
&lt;span class="nv"&gt;mem_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80
&lt;span class="nv"&gt;pages_free&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Pages free"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $3 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/.&lt;/span&gt;&lt;span class="nv"&gt;$/&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;pages_active&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Pages active"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $3 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/.&lt;/span&gt;&lt;span class="nv"&gt;$/&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;pages_inactive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Pages inactive"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $3 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/.&lt;/span&gt;&lt;span class="nv"&gt;$/&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;pages_speculative&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Pages speculative"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $3 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/.&lt;/span&gt;&lt;span class="nv"&gt;$/&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;pages_wired&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Pages wired down"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $4 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/.&lt;/span&gt;&lt;span class="nv"&gt;$/&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;pages_compressed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Pages occupied by compressor:"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $5 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/.&lt;/span&gt;&lt;span class="nv"&gt;$/&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;free_group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pages_free&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="nv"&gt;$pages_speculative&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;used&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pages_active&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="nv"&gt;$pages_inactive&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="nv"&gt;$pages_wired&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="nv"&gt;$pages_compressed&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$free_group&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="nv"&gt;$used&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;used_percent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"scale=2; &lt;/span&gt;&lt;span class="nv"&gt;$used&lt;/span&gt;&lt;span class="s2"&gt; / &lt;/span&gt;&lt;span class="nv"&gt;$total&lt;/span&gt;&lt;span class="s2"&gt; * 100"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;mem_alert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$used_percent&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$mem_threshold&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$mem_alert&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 1 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;send_alert &lt;span class="s2"&gt;"🚨 [ALERT] Memory usage high on &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="nv"&gt;$used_percent&lt;/span&gt;&lt;span class="s2"&gt;% (threshold: &lt;/span&gt;&lt;span class="nv"&gt;$mem_threshold&lt;/span&gt;&lt;span class="s2"&gt;%) - &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;## CPU CHECK LOGIC&lt;/span&gt;
&lt;span class="nv"&gt;cpu_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80
&lt;span class="nv"&gt;cpu_idle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;top &lt;span class="nt"&gt;-l&lt;/span&gt; 1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"CPU usage:"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $7 }'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/%//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;cpu_usage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"100 - &lt;/span&gt;&lt;span class="nv"&gt;$cpu_idle&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;cpu_alert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cpu_usage&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$cpu_threshold&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;| bc&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$cpu_alert&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 1 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;send_alert &lt;span class="s2"&gt;"🚨 [ALERT] CPU usage high on &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="nv"&gt;$cpu_usage&lt;/span&gt;&lt;span class="s2"&gt;% (threshold: &lt;/span&gt;&lt;span class="nv"&gt;$cpu_threshold&lt;/span&gt;&lt;span class="s2"&gt;%) - &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;## SERVICE CHECK LOGIC&lt;/span&gt;
&lt;span class="nv"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sshd"&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;pgrep &lt;span class="nv"&gt;$service_name&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"running"&lt;/span&gt;
&lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"not running"&lt;/span&gt;
        send_alert &lt;span class="s2"&gt;"🚨 [ALERT] Service &lt;/span&gt;&lt;span class="nv"&gt;$service_name&lt;/span&gt;&lt;span class="s2"&gt; is not running on &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also find the full code on &lt;a href="https://github.com/midesofek/Server-Health-Status-Alert-Bash-Script/blob/main/health_check.sh" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>bash</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
