Tools: How to Set Up Linux Server Monitoring in 10 Minutes (Free) (2026)
How to Set Up Linux Server Monitoring in 10 Minutes (Free)
What We're Monitoring
Step 1: Install netdata (2 minutes)
Step 2: Basic Cron-Based Alerting (3 minutes)
Step 3: PM2 Monitoring (if using Node.js)
Step 4: Quick Health Endpoint
The 10-Minute Summary If you're running a production app on a VPS and you're relying on "it seems fine" as your monitoring strategy — this post is for you. Here's a minimal, free monitoring setup that covers the basics in under 10 minutes. Netdata is free, open source, and installs with one command: It auto-discovers services, requires no config, and gives you a real-time dashboard on port 19999. For dead-simple email alerts without a third-party service: Add this to your Node.js app: Then add to your cron monitor: I built ARIA to solve exactly this.
Try it free at step2dev.com — no credit card needed. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse
bash <(curl -Ss https://my-netdata.io/kickstart.sh)
bash <(curl -Ss https://my-netdata.io/kickstart.sh)
bash <(curl -Ss https://my-netdata.io/kickstart.sh)
# Access it (from your server)
http://your-server-ip:19999 # Or tunnel it securely
ssh -L 19999:localhost:19999 user@your-server
# Then visit http://localhost:19999 in your browser
# Access it (from your server)
http://your-server-ip:19999 # Or tunnel it securely
ssh -L 19999:localhost:19999 user@your-server
# Then visit http://localhost:19999 in your browser
# Access it (from your server)
http://your-server-ip:19999 # Or tunnel it securely
ssh -L 19999:localhost:19999 user@your-server
# Then visit http://localhost:19999 in your browser
# Install mailutils
sudo apt install mailutils -y # Create a monitoring script
sudo nano /usr/local/bin/server-check.sh
# Install mailutils
sudo apt install mailutils -y # Create a monitoring script
sudo nano /usr/local/bin/server-check.sh
# Install mailutils
sudo apt install mailutils -y # Create a monitoring script
sudo nano /usr/local/bin/server-check.sh
#!/bin/bash
ALERT_EMAIL="[email protected]"
THRESHOLD_DISK=85
THRESHOLD_RAM=90 # Disk check
DISK_USAGE=$(df -h / | awk 'NR==2{print $5}' | tr -d '%')
if [ "$DISK_USAGE" -gt "$THRESHOLD_DISK" ]; then echo "ALERT: Disk usage is ${DISK_USAGE}% on $(hostname)" | mail -s "Disk Alert - $(hostname)" $ALERT_EMAIL
fi # RAM check
RAM_AVAILABLE=$(free | awk '/^Mem/{print int($7/$2*100)}')
if [ "$RAM_AVAILABLE" -lt $((100 - THRESHOLD_RAM)) ]; then echo "ALERT: RAM available is ${RAM_AVAILABLE}% on $(hostname)" | mail -s "RAM Alert - $(hostname)" $ALERT_EMAIL
fi # Service check (change 'node' to your process name)
if ! pgrep -x "node" > /dev/null; then echo "ALERT: node process is DOWN on $(hostname)" | mail -s "Service DOWN - $(hostname)" $ALERT_EMAIL
fi
#!/bin/bash
ALERT_EMAIL="[email protected]"
THRESHOLD_DISK=85
THRESHOLD_RAM=90 # Disk check
DISK_USAGE=$(df -h / | awk 'NR==2{print $5}' | tr -d '%')
if [ "$DISK_USAGE" -gt "$THRESHOLD_DISK" ]; then echo "ALERT: Disk usage is ${DISK_USAGE}% on $(hostname)" | mail -s "Disk Alert - $(hostname)" $ALERT_EMAIL
fi # RAM check
RAM_AVAILABLE=$(free | awk '/^Mem/{print int($7/$2*100)}')
if [ "$RAM_AVAILABLE" -lt $((100 - THRESHOLD_RAM)) ]; then echo "ALERT: RAM available is ${RAM_AVAILABLE}% on $(hostname)" | mail -s "RAM Alert - $(hostname)" $ALERT_EMAIL
fi # Service check (change 'node' to your process name)
if ! pgrep -x "node" > /dev/null; then echo "ALERT: node process is DOWN on $(hostname)" | mail -s "Service DOWN - $(hostname)" $ALERT_EMAIL
fi
#!/bin/bash
ALERT_EMAIL="[email protected]"
THRESHOLD_DISK=85
THRESHOLD_RAM=90 # Disk check
DISK_USAGE=$(df -h / | awk 'NR==2{print $5}' | tr -d '%')
if [ "$DISK_USAGE" -gt "$THRESHOLD_DISK" ]; then echo "ALERT: Disk usage is ${DISK_USAGE}% on $(hostname)" | mail -s "Disk Alert - $(hostname)" $ALERT_EMAIL
fi # RAM check
RAM_AVAILABLE=$(free | awk '/^Mem/{print int($7/$2*100)}')
if [ "$RAM_AVAILABLE" -lt $((100 - THRESHOLD_RAM)) ]; then echo "ALERT: RAM available is ${RAM_AVAILABLE}% on $(hostname)" | mail -s "RAM Alert - $(hostname)" $ALERT_EMAIL
fi # Service check (change 'node' to your process name)
if ! pgrep -x "node" > /dev/null; then echo "ALERT: node process is DOWN on $(hostname)" | mail -s "Service DOWN - $(hostname)" $ALERT_EMAIL
fi
chmod +x /usr/local/bin/server-check.sh # Add to cron (run every 5 minutes)
crontab -e
# Add:
*/5 * * * * /usr/local/bin/server-check.sh
chmod +x /usr/local/bin/server-check.sh # Add to cron (run every 5 minutes)
crontab -e
# Add:
*/5 * * * * /usr/local/bin/server-check.sh
chmod +x /usr/local/bin/server-check.sh # Add to cron (run every 5 minutes)
crontab -e
# Add:
*/5 * * * * /usr/local/bin/server-check.sh
# Install PM2 globally
npm install -g pm2 # Start your app with PM2
pm2 start server.js --name your-app # Set up startup script (survive reboots)
pm2 startup
pm2 save # Monitor in terminal
pm2 monit
# Install PM2 globally
npm install -g pm2 # Start your app with PM2
pm2 start server.js --name your-app # Set up startup script (survive reboots)
pm2 startup
pm2 save # Monitor in terminal
pm2 monit
# Install PM2 globally
npm install -g pm2 # Start your app with PM2
pm2 start server.js --name your-app # Set up startup script (survive reboots)
pm2 startup
pm2 save # Monitor in terminal
pm2 monit
app.get('/health', (req, res) => { const healthCheck = { uptime: process.uptime(), message: 'OK', timestamp: Date.now(), memoryUsage: process.memoryUsage(), }; res.status(200).json(healthCheck);
});
app.get('/health', (req, res) => { const healthCheck = { uptime: process.uptime(), message: 'OK', timestamp: Date.now(), memoryUsage: process.memoryUsage(), }; res.status(200).json(healthCheck);
});
app.get('/health', (req, res) => { const healthCheck = { uptime: process.uptime(), message: 'OK', timestamp: Date.now(), memoryUsage: process.memoryUsage(), }; res.status(200).json(healthCheck);
});
# Health endpoint check
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/health)
if [ "$HTTP_STATUS" != "200" ]; then echo "ALERT: Health check failed with status $HTTP_STATUS" | mail -s "App Health Fail" $ALERT_EMAIL
fi
# Health endpoint check
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/health)
if [ "$HTTP_STATUS" != "200" ]; then echo "ALERT: Health check failed with status $HTTP_STATUS" | mail -s "App Health Fail" $ALERT_EMAIL
fi
# Health endpoint check
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/health)
if [ "$HTTP_STATUS" != "200" ]; then echo "ALERT: Health check failed with status $HTTP_STATUS" | mail -s "App Health Fail" $ALERT_EMAIL
fi
# 1. Install netdata for visual monitoring
bash <(curl -Ss https://my-netdata.io/kickstart.sh) # 2. Add cron monitoring script
# (copy from above) # 3. Set up PM2 if Node.js
pm2 startup && pm2 save # 4. Add /health endpoint to your app # 5. Test your alerts
# ./server-check.sh (run manually, check email)
# 1. Install netdata for visual monitoring
bash <(curl -Ss https://my-netdata.io/kickstart.sh) # 2. Add cron monitoring script
# (copy from above) # 3. Set up PM2 if Node.js
pm2 startup && pm2 save # 4. Add /health endpoint to your app # 5. Test your alerts
# ./server-check.sh (run manually, check email)
# 1. Install netdata for visual monitoring
bash <(curl -Ss https://my-netdata.io/kickstart.sh) # 2. Add cron monitoring script
# (copy from above) # 3. Set up PM2 if Node.js
pm2 startup && pm2 save # 4. Add /health endpoint to your app # 5. Test your alerts
# ./server-check.sh (run manually, check email) - Service health (is your app actually running?)
- Basic alerting when things go wrong