Estimated reading time: 7 minutes
Table of contents
My Watchful LEMP Server
My Ultimate LEMP Server is humming along nicely, and my resilient backup strategy is running like clockwork. The system is fast, secure, and protected. However, my 22+ years of experience in the web hosting industry have taught me a crucial, and sometimes painful, lesson: Servers have a nasty habit of biting without any warning. Indeed, a system can seem perfectly healthy one moment, and then be completely offline the next. Without proactive server monitoring, you only discover this problem after it has already happened, usually when a visitor stumbles upon an error.
Ultimately, my goal was to build a system that would always bark long before it had a chance to bite. Even my own experiments can occasionally lead to a hiccup, and I’d rather know about it instantly. For this reason, I knew I needed a propper watchdog. I needed a guardian that could keep an eye on things 24/7 and report back. This is the story of my search for a truly lightweight server monitoring & alerting solution.
My Checklist for a Pragmatic Server Monitoring Solution
As I began my research, I knew I wanted to avoid the complexity of many enterprise-grade tools. While solutions like Prometheus and Grafana are powerful, they felt like overkill for my personal LEMP server. In fact, they would consume more resources than some of the services I was trying to monitor. With this in mind, I created a simple checklist. My ideal solution had to be:
- Ultra-Lightweight: Above all, it must have a negligible impact on system resources. It should sip, not guzzle, CPU and RAM.
- Proactive & Self-Healing: It should not only alert me to problems but also attempt to fix them automatically, for instance, by restarting a failed service.
- Simple, Instant Alerts: I didn’t need a tool with complex dashboards or historical graphing capabilities. In fact, my virtualization platform, Proxmox, already provides excellent real-time and historical graphs for all my container resources. For this reason, my focus was purely on active monitoring. I just wanted a straightforward email notification the moment something went wrong.
- Easy to Configure: Finally, the setup had to be manageable through simple, human-readable configuration files.
The Solution: Monit, The Veteran Watchdog
After evaluating a few options, I landed on Monit. It’s a classic, open-source utility that perfectly embodies my philosophy of simplicity and efficiency. Monit is a single, self-contained binary with no dependencies, and it has been battle-tested for years. It’s the epitome of a tool that does one job and does it exceptionally well. Consequently, it was the perfect choice because it delivered on every point in my checklist without any unnecessary overhead. This was the lightweight server monitoring tool I was looking for.

Step 1: Installing and Configuring Monit
Getting started with Monit on Alpine Linux is incredibly simple. Firstly, I installed it with a single command.
# Install Monit
sudo apk add monit
Secondly, I began editing the main configuration file, which is located at /etc/monitrc. I started by setting the check interval and enabling the simple, built-in web interface. This interface is great for getting a quick, at-a-glance overview of your server’s health.
Inside the file, I uncommented and modified the lines to check my services every 60 seconds and to allow access to the web interface from my local network.
sudo sed -i.bak '
s/^# *set daemon .*/set daemon 60/;
/set httpd port 2812/,/allow admin:monit/ {
s/^\( *\)use address localhost.*/\1# use address localhost # allow only localhost to connect/;
s/^\( *\)allow localhost.*/\1allow 192.168.0.0\/24 # allow anyone from my local network/;
}' /etc/monitrc
The command applies the changes, resulting in this configuration:
# Set the check cycle to 60 seconds
set daemon 60
# ...
# Enable the web interface
set httpd port 2812 and
# use address localhost # allow only localhost to connect
allow 192.168.0.0/24 # allow anyone from my local network
allow admin:monit # require user 'admin' with password 'monit'
I need to change the default admin password monit to something more secure (only letters and numbers can be used). I can do this by manually editing the file or using another sed command:
sudo sed -i '/allow admin:monit/ s/monit/secure_password/' /etc/monitrc
Another thing I could do, is changing the admin username to something else, maybe I did 😉
Opening the Firewall Port
Before starting Monit, I needed to update my nftables firewall configuration to allow incoming connections on port 2812. Otherwise, the web interface would be blocked. I used a simple sed command to add the new port to my existing rule.
# Add port 2812 to the existing list of accepted TCP ports
sudo sed -i 's/tcp dport { 22, 80, 443 }/tcp dport { 22, 80, 443, 2812 }/' /etc/nftables.nft
# It's always a good idea to check the syntax before applying
sudo nft -c -f /etc/nftables.nft
# Finally, reload the firewall rules
sudo rc-service nftables reload
With the basic configuration done and the firewall port open, I started the service and enabled it to run on boot.
# Enable and start the Monit service
sudo rc-update add monit default
sudo rc-service monit start
Monit Service Manager
I could now access the status page by navigating to http://your-server-ip:2812 in my browser. Here is an example screenshot when all services to monitor have been configured:

Step 2: Implementing Server Monitoring for Core Resources
With Monit running, it was time to tell it what to watch. I started with the server’s vital signs: CPU, RAM, and disk space. I created a new configuration file for these system checks. Monit automatically includes any file in the /etc/monit.d/ directory.
# Create the /etc/monit.d directory
sudo mkdir -p /etc/monit.d
# Uncomment the include line in the main configuration file
sudo sed -i 's/^# *include \/etc\/monit.d\/\*/include \/etc\/monit.d\/\*/' /etc/monitrc
sudo tee /etc/monit.d/system.conf > /dev/null <<'EOF'
# Check general system resources
check system $HOST
if loadavg (1min) > 4 then alert
if loadavg (5min) > 2 then alert
if memory usage > 85% then alert
if cpu usage (user) > 80% for 3 cycles then alert
if cpu usage (system) > 40% for 3 cycles then alert
# Check the root filesystem
check device rootfs with path /
if space usage > 85% then alert
EOF
To apply the new rules, I just needed to tell Monit to reload its configuration.
sudo monit reload
Now, Monit is actively watching my server’s core health. If any of these thresholds were breached, it would generate an alert.
Step 3: Keeping an Eye on the LEMP Stack
Next, I moved on to the most important part: monitoring the Nginx, MariaDB and PHP-FPM services that power my websites, and while I’m at it, I added other services too. This is where Monit‘s self-healing capabilities truly shine. Not only can it detect a failed service, but it can also automatically restart it.
I created another configuration file for my web stack services (Note: /var/run/php-fpm83-site1.sock and /var/run/php-fpm84-site2.sock and site1.com needs to be changed):
sudo tee /etc/monit.d/lemp.conf > /dev/null <<'EOF'
# Monitor the Nginx process
check process nginx with pidfile /var/run/nginx/nginx.pid
start program = "/etc/init.d/nginx start"
stop program = "/etc/init.d/nginx stop"
if failed host site1.com port 443 protocol https request "/"
for 2 cycles
then restart
if 5 restarts within 5 cycles then timeout
# Monitor the PHP-FPM 8.3 process
check process php-fpm83 with pidfile /var/run/php-fpm83/php-fpm.pid
start program = "/etc/init.d/php-fpm83 start"
stop program = "/etc/init.d/php-fpm83 stop"
if failed unixsocket /var/run/php-fpm83-site1.sock then restart
if 5 restarts within 5 cycles then timeout
# Monitor the PHP-FPM 8.4 process
check process php-fpm84 with pidfile /var/run/php-fpm84/php-fpm.pid
start program = "/etc/init.d/php-fpm84 start"
stop program = "/etc/init.d/php-fpm84 stop"
if failed unixsocket /var/run/php-fpm84-site2.sock then restart
if 5 restarts within 5 cycles then timeout
# Monitor the MariaDB process
check process mariadb with pidfile /run/mysqld/mariadb.pid
start program = "/etc/init.d/mariadb start"
stop program = "/etc/init.d/mariadb stop"
if failed unixsocket /run/mysqld/mysqld.sock then restart
if 5 restarts within 5 cycles then timeout
# Monitor the SSH daemon
check process sshd with pidfile /run/sshd.pid
start program = "/etc/init.d/sshd start"
stop program = "/etc/init.d/sshd stop"
if failed port 22 protocol ssh then restart
if 5 restarts within 5 cycles then timeout
# Monitor sshguard
check process sshguard with pidfile /var/run/sshguard.pid
start program = "/etc/init.d/sshguard start"
stop program = "/etc/init.d/sshguard stop"
if 5 restarts within 5 cycles then timeout
# Monitor nftables by executing the check within a shell
check program nftables-status with path "/bin/sh -c '/usr/sbin/nft list ruleset'"
if status != 0 for 2 cycles then exec "/bin/sh -c '/etc/init.d/nftables start'"
EOF
These rules are incredibly powerful. For example, the Nginx rule checks if the process is running. In addition, it goes a step further by actively checking if port 443 is responding to HTTPS requests. If it’s not, Monit will automatically execute the restart command. Similarly, it checks the PHP-FPM and the other processes by ensuring their sockets are available.
After reloading Monit again sudo monit reload, my lightweight server monitoring setup was now acting as a self-healing guardian for my websites.
Step 4: Enabling Simple, Reliable Email Alerts
The final step for our internal monitor was to ensure Monit could actually tell me when something was wrong. For Gmail, the settings in the /etc/monitrc would look like this:
set mailserver smtp.gmail.com port 587
username "[email protected]" password "YOUR_GMAIL_APP_PASSWORD"
using tlsv12
set mail-format {
from: LEMP Server <[email protected]>
subject: Monit Alert -- $EVENT on $HOST
message: $EVENT Service $SERVICE
Date: $DATE
Action: $ACTION
Host: $HOST
Description: $DESCRIPTION
}
set alert [email protected]
After one last sudo monit reload, the setup was complete. Now, if Monit detects an issue – from high CPU usage to a failed Nginx process – it will immediately send a detailed alert to my inbox.
Step 5: Adding an External Safety Net
Our Monit setup is great for internal health checks, but it has one inherent limitation: it runs on the server it’s monitoring. Consequently, what happens if the server is truly down? For instance, if it hangs completely, the load is too high for it to even send an email, or my home internet connection simply goes offline? In those scenarios, Monit is rendered silent because it’s part of the problem.
For this reason, a complete monitoring strategy needs a reliable external check. I use the free tier of UptimeRobot as my ultimate safety net. In short, UptimeRobot‘s servers, located all around the world, will try to connect to my server every few minutes. If they can’t reach it, they know it’s completely unresponsive. When that happens, their system instantly sends me both an email and a push notification to my phone via their app. This external check perfectly complements the internal lightweight server monitoring with Monit. If you’re looking for a similar service, you can sign up through my affiliate links, which supports me with no extra cost to you.
This means I now have a two-layered approach:
- Monit is my internal doctor, checking the health of individual services and trying to fix them automatically.
- UptimeRobot is my external paramedic, notifying me if the entire server is unresponsive to the outside world.
Final Thoughts: From Resilient to Self-Aware
This project was another missing piece of my infrastructure puzzle. My LEMP server (and my jump box) is now more than just secure and backed up; it’s self-aware and watched from both the inside and the out. The internal monitor, Monit, actively checks its own health, automatically recovers from common service failures, and immediately notifies me of any issues. At the same time, an external service stands guard, ready to send a push notification in case of a failure. This simple yet powerful layered approach to lightweight server monitoring has finally given me true peace of mind. I can now confidently walk away from the server, knowing that my digital watchdogs are always on duty, ensuring the long-term stability I set out to achieve from the very beginning. Oh… that reminds me… before I walk away… I need to add /etc/monitrc and /etc/monit.d to the backup strategy…
Buy me a coffee 🙂
If you found this post helpful, informative, or if it saved or made you some money, consider buying me a coffee. Your support means a lot and motivates me to keep writing.
You can do so via bunq.me (bunq, iDeal, Bankcontact and Credit- or Debit cards) or PayPal (PayPal and Credit- or Debit cards). Thank you!
Disclaimer
The blog posts, guides, and scripts provided on this website are for informational and educational purposes only. They are provided “as is” and without any warranty of any kind, either express or implied, including, but not limited to, the implied warranties of merchantability, fitness for a particular purpose, or non-infringement.
By using the information or scripts from this blog, you agree that I am not liable for any direct, indirect, incidental, consequential, or any other damages or losses arising from the use of or inability to use the information, scripts, or instructions contained herein. You assume full responsibility for any and all risks associated with the use of this content.
The blog posts, guides, and pages may contain referral/affiliate links. If you make a purchase through these links, I may receive a commission at no additional cost to you.