Simple monitoring of servers using Ruby and Nexmo

For the Nexmo-2016 Hackathon, I decided to make a simple tool that monitors my servers and tells me whenever their disk, memory and processor usage reaches a critical point.

This idea came after I was running a server with a scraper that had a bad memory leak and used up all the ram in a day affecting other services. Being the noob I am, I was not monitoring my server and wished for a tool that somehow notified my before my other services were affected.

This blog post can serve as a good tutorial to learn basic ruby, executing native commands, string manipulation and sending sms via nexmo.

Initial thoughts on how to do it

  • Write a ruby script that keeps running in the background, monitoring the processes
  • When the threshold for a particular usage is crossed, message the user about it using a predefined template
  • Use Nexmo APIs to send a message to my number

Getting the data

Lets start with the step by step extraction of critical elements and getting their final percentage values to play with

Disk Usage

This one is the easiest and achieved by running a single command and parsing the output

df = \`df --total\`  
disk_perc = df.split(" ")[-2].to_f.round(2)  

Memory Usage

Getting the memory usage is a bit tough as you have compute the percentage usage from the free command. I will use the -/+ buffers/cache to the real memory usage (line 3 in free output)

free = `free`  
lines = free.split("\n")  
memline = lines[2].split()  
used = memline[2].to_f  
free = memline[3].to_f  
total = used + free  
mem_perc = (used/total * 100).round(2)  

Cpu Usage

Getting the cpu usage is a bit more complex as you have to read the /proc/cpuinfo file twice in a time interval and then compute the load difference to get the real value. Taken from usagewatch gems source code.

@proc0 = File.readlines('/proc/stat').grep(/^cpu /).first.split(" ")
sleep 1  
@proc1 = File.readlines('/proc/stat').grep(/^cpu /).first.split(" ")

@proc0usagesum = @proc0[1].to_i + @proc0[2].to_i + @proc0[3].to_i
@proc1usagesum = @proc1[1].to_i + @proc1[2].to_i + @proc1[3].to_i
@procusage = @proc1usagesum - @proc0usagesum

@proc0total = 0
for i in (1..4) do  
  @proc0total += @proc0[i].to_i
@proc1total = 0
for i in (1..4) do  
  @proc1total += @proc1[i].to_i
@proctotal = (@proc1total - @proc0total)

@cpuusage = (@procusage.to_f / @proctotal.to_f)
@cpuusagepercentage = (100 * @cpuusage).to_f.round(2)

Wrapping it up

I made three functions to get the percentage usage of each attribute

  • get_disk_usage: To get the disk usage
  • get_memory_usage: To get the memory usage
  • get_cpu_usage: To get the cpu usage

Now lets test to see what happens when all these function are called. My monitor.rb file:

# ... functions declared
puts get_disk_usage  
puts get_memory_usage  
puts get_cpu_usage  



Setting the infinite listener

Now that we can correctly read the usage values, we can just compare them to a threshold and trigger a function if they go above it. So I create a function check which will just run an infinite loop and keep checking the variables to see if they cross the 90% threshold (defined in THRESHOLD constant). If they do, it will call the send function to send the alert message.I also sleep the script for an hour after sending a message as I don't want to be bombarded with the same message over and over again!

def check  
    while true
        disk = get_disk_usage
        mem = get_memory_usage
        cpu = get_cpu_usage
        if disk > THRESHOLD
            send("You disk usage is now #{disk}. Please do something about it.")
        if mem > THRESHOLD
            send("You memory usage is now #{mem}. Please do something about it.")
        if cpu > THRESHOLD
            send("You cpu usage is now #{cpu}. Please do something about it.")

Setting up Nexmo

First goto and register to get a API_KEY and SECRET. I put them in environment variables to make sure they are secure :)
I use foreman to run my code and thus made a .env file to store the enviroment variables with all my settings that looks like this


Now in my code, I add the line

nexmo = NEXMO_KEY, secret: NEXMO_SECRET)  

to instantiate a nexmo client.

Sending the alert SMS

Now in my send function, I use the nexmo client to send a message based on the text from the parameter. I have used SENDER and MESSAGE_NUMBER as constants to store the sender same and receiver's mobile number.

def send(message)  
    nexmo.send_message(from: SENDER, to: MESSAGE_NUMBER, text: message)
    sleep 3600

EDIT: Since this was giving some error and I didn't have much time, I used an alternative way of sending the sms using a simple post request to nexmo.

uri = URI.parse("")  
params = {  
    "api_key" => NEXMO_KEY,
    "api_secret" => NEXMO_SECRET,
    "to" => MESSAGE_NUMBER,
    "from" => SENDER,
    "text" => message

call = Net::HTTP.post_form(uri, params)  

Putting it all together

In the start of my monitor.rb file, I add the imports and settings

require 'nexmo'


At the absolute end, I just add the line


And let ruby do that magic.
The app can be then run using ruby monitor.rb or via foreman using foreman start (See the repo for the Procfile).

The DEVPOST project can be found here:
The GitHub link to the repo:


Simple wasn't it. Now I can just run it via a simple command and get a sms whenever such thing happens again.

You can find detailed setup details in the GitHub repo's

Possible Expansion

  • Daemon
  • One click install/package as a gem
  • Web server to monitor?
  • Support more OS
  • Convert to bash script?

Links and References