Back to tutorials
Tutorial

Server Monitoring Tutorial (2026): Set Up Uptime, Resource Alerts, and Log Signals on a Hosting VPS

Server monitoring tutorial for 2026: set uptime checks, CPU/RAM/disk alerts, and log-based warnings on a hosting VPS.

By Anurag Singh
Updated on Aug 04, 2026
Category: Tutorial
Share article
Server Monitoring Tutorial (2026): Set Up Uptime, Resource Alerts, and Log Signals on a Hosting VPS

Most outages don’t begin with a clean “down.” They start with a disk creeping to 92%, a mail queue growing for two hours, or a PHP-FPM pool sliding into timeouts. This server monitoring tutorial walks you through a practical baseline for a hosting VPS or dedicated server: uptime checks, resource thresholds, and log signals you can act on before customers notice.

The plan is intentionally boring. You’ll use a few dependable tools and set thresholds that match real failure modes.

You’ll also route alerts to fixes you can actually do at 2 a.m.

The result is a small monitoring stack for Ubuntu 24.04 LTS and Debian 12/13. It also scales cleanly when you move to a larger node.

What you’ll build (and what you won’t)

  • Uptime checks (HTTP/HTTPS and optional TCP) from outside your server.
  • Resource alerts on CPU load, RAM pressure, disk usage, inode usage, and swap.
  • Service health checks for Nginx/Apache, PHP-FPM, and SSH.
  • Log-based alerts for recurring errors (502/504 spikes, SSH brute force, mail failures).
  • Simple notifications via email (works well for hosting operations).

You won’t be building a full observability platform. For most hosting admins, a handful of clean signals beats dashboards you never open.

Prerequisites and sizing notes for a hosting VPS

This tutorial assumes:

  • A VPS or dedicated server with root access (Ubuntu 24.04 LTS or Debian 12+).
  • A working outbound mail path (SMTP relay or local MTA) for alerts.
  • Basic services: Nginx/Apache, optional PHP-FPM, and SSH.

If you’re doing this for client sites, start with headroom. Monitoring helps you react earlier. It won’t save a VPS that sits at 95% CPU all afternoon.

If you want predictable performance across multiple sites, a managed VPS hosting plan can cost less than repeated incident cleanup.

Step 1 — Confirm your baseline: ports, DNS, and SSL behavior

Before you install anything, verify the basics from your laptop. When an uptime check fails later, you want a fast answer. Is it DNS, firewall rules, or the service itself?

1) Confirm DNS points where you think it points

dig +short A yourdomain.com
dig +short AAAA yourdomain.com

If the IP is wrong or inconsistent, fix DNS first. For domain moves, follow the steps in DNS migration without downtime.

2) Confirm HTTPS and certificate chain

curl -I https://yourdomain.com

TLS errors here will turn into noisy alerts later. If you need a clean Let’s Encrypt setup or renewal troubleshooting, use this SSL deployment tutorial.

3) Confirm open ports from the outside

On a hosting box, you typically need 80/443 (web) and 22 (admin). Mail ports only if you run mail.

If you’re unsure what’s exposed, do a quick firewall/ports review using this firewall audit tutorial.

Step 2 — Install a lightweight on-server monitor (Netdata)

You need something on the server that answers one question fast: “What changed?” Netdata is still a good single-node pick in 2026.

It installs quickly and gives you immediate visibility into CPU, memory, disk, network, and processes.

Install on Ubuntu/Debian:

sudo apt update
sudo apt install -y curl
curl -sSL https://get.netdata.cloud/kickstart.sh | sudo sh

Verify the service:

sudo systemctl status netdata --no-pager
sudo ss -lntp | grep netdata

By default, Netdata listens on port 19999. Don’t leave that open to the internet.

Lock Netdata to localhost (recommended)

Edit:

sudo nano /etc/netdata/netdata.conf

Set:

[web]
  bind to = 127.0.0.1

Restart:

sudo systemctl restart netdata

Access it securely via SSH port forwarding:

ssh -L 19999:127.0.0.1:19999 root@YOUR_SERVER_IP

Then open http://127.0.0.1:19999 in your browser.

Step 3 — Set up external uptime checks (the checks your customers “feel”)

On-server metrics can look fine while your site is unreachable from the public internet. Cover that gap with at least two external checks:

  • HTTPS check for your primary domain (200/301 expected).
  • TCP check for SSH or your web port (optional, helpful for network triage).

If you already pay for an uptime service, keep it. What matters is how you configure the interval, locations, and what counts as “down.”

Recommended uptime settings for hosting in 2026

  • Interval: 60 seconds for business-critical sites; 3–5 minutes for low-risk sites.
  • Confirmation: require 2 failed checks before alerting to reduce false positives.
  • Timeout: 10 seconds for HTTPS checks; 3 seconds for TCP checks.
  • Expected status: accept 200–399 for homepages that redirect to /en or /login.
  • Alert routing: email + one secondary channel (SMS/push) for after-hours.

Practical tip: if you run aggressive rate limiting, whitelist your uptime provider’s IP ranges. Otherwise, your “security” rules will create fake downtime.

For bot protection that doesn’t punish real users, see Nginx rate limiting.

Step 4 — Create real alerts on the server with Monit

Netdata is great for visibility. Monit is better for crisp, actionable checks like “Is Nginx responding?” or “Did php-fpm die?”

Monit can alert you and, if you want, restart services.

Install Monit

sudo apt update
sudo apt install -y monit mailutils

Note on email alerts: if your server can’t send mail directly (common on VPS networks), route alerts through a relay. If you’re already untangling SMTP issues, keep this SMTP troubleshooting tutorial nearby.

Configure Monit email basics

Edit:

sudo nano /etc/monit/monitrc

Add or adjust these lines (keep them near the top):

set daemon 60
set logfile syslog

set mailserver localhost

set alert ops@yourdomain.com
set mail-format {
  from: monit@yourdomain.com
  subject: [Monit] $SERVICE $EVENT on $HOST
  message: $EVENT Service $SERVICE on $HOST

Date: $DATE
Action: $ACTION
Description: $DESCRIPTION

Your Monit
}

Lock permissions (Monit refuses to run if the file is too open):

sudo chmod 600 /etc/monit/monitrc

Enable and start:

sudo systemctl enable --now monit
sudo monit status

Step 5 — Add the checks that prevent common hosting incidents

Monit checks live in /etc/monit/conf-enabled/ on Debian/Ubuntu. Keep one file per area.

This makes changes easier to review and roll back.

5A) Disk usage + inode usage (this is the “site stopped updating” alert)

Create:

sudo nano /etc/monit/conf-enabled/disk-space

Add:

check filesystem rootfs with path /
  if space usage > 85% then alert
  if space usage > 92% then alert
  if inode usage > 80% then alert
  if inode usage > 90% then alert

Two thresholds keep the alert useful. 85% means “schedule cleanup.” 92% means “stop what you’re doing.”

Inode alerts catch the ugly case where you have free GB but no file entries. That often comes from millions of tiny cache files.

5B) Load and memory pressure (separate symptoms from causes)

Create:

sudo nano /etc/monit/conf-enabled/system-health

Add (tune to your CPU count; example assumes 2–4 vCPUs):

check system $HOST
  if loadavg (1min) > 4 then alert
  if loadavg (5min) > 3 then alert
  if memory usage > 85% then alert
  if swap usage > 25% then alert

Swap usage isn’t automatically bad. A steady climb is usually an early “OOM kills are next” signal.

If you’re unsure about sizing, a small swap file plus monitoring is often better than running with none.

5C) Nginx or Apache process + HTTP response

For Nginx:

sudo nano /etc/monit/conf-enabled/nginx
check process nginx with pidfile /run/nginx.pid
  start program = "/usr/sbin/service nginx start"
  stop program  = "/usr/sbin/service nginx stop"
  if failed port 80 protocol http then restart
  if 5 restarts within 5 cycles then alert

For Apache on Ubuntu/Debian (often apache2):

sudo nano /etc/monit/conf-enabled/apache
check process apache2 with pidfile /run/apache2/apache2.pid
  start program = "/usr/sbin/service apache2 start"
  stop program  = "/usr/sbin/service apache2 stop"
  if failed port 80 protocol http then restart
  if 5 restarts within 5 cycles then alert

If your sites are HTTPS-only, keep the port 80 check anyway. It catches basic routing failures and is easy to validate.

Pair it with an external HTTPS uptime check for the full picture.

5D) PHP-FPM health check (stops the 502/504 spiral)

First, list PHP-FPM services:

systemctl list-units --type=service | grep php | grep fpm

Typical service names include php8.2-fpm or php8.3-fpm. Create a check (example uses PHP 8.3):

sudo nano /etc/monit/conf-enabled/php-fpm
check process php-fpm with pidfile /run/php/php8.3-fpm.pid
  start program = "/usr/sbin/service php8.3-fpm start"
  stop program  = "/usr/sbin/service php8.3-fpm stop"
  if 5 restarts within 5 cycles then alert

If you’re chasing intermittent gateway errors, don’t troubleshoot by hunch. Follow a structured flow in this 502/504 troubleshooting tutorial.

5E) SSH service (admin access is part of uptime)

Create:

sudo nano /etc/monit/conf-enabled/ssh
check process sshd with pidfile /run/sshd.pid
  start program = "/usr/sbin/service ssh start"
  stop program  = "/usr/sbin/service ssh stop"
  if failed port 22 protocol ssh then restart
  if 5 restarts within 5 cycles then alert

If you want safer admin access, combine monitoring with hardening. HostMyCode has a detailed guide on SSH hardening and a practical option using a jump host.

Reload Monit and validate configuration

sudo monit reload
sudo monit status

If Monit reports syntax errors, fix them immediately. Monitoring breaks quietly.

It often breaks right before a weekend.

Step 6 — Add log-based signals (failures that look “fine” in metrics)

Graphs don’t catch everything. A VPS can have plenty of CPU and still be broken due to application errors, upstream timeouts, or mail delivery failures.

Logs give you earlier, sharper clues.

We’ll keep this simple:

  • journalctl queries to confirm what “bad” looks like on your box.
  • logwatch daily summaries + a couple of targeted greps via cron for fast alerts.

6A) Identify your top 3 “incident” log patterns

Start with these commands, then adjust them to match your stack and log paths.

Nginx 502/504 and upstream issues:

sudo grep -R " 502 " /var/log/nginx/access.log | tail -n 20
sudo grep -R "upstream" /var/log/nginx/error.log | tail -n 50

SSH auth failures:

sudo journalctl -u ssh --since "1 hour ago" | grep -i "failed\|invalid" | tail -n 50

Disk full / filesystem warnings:

sudo dmesg --level=err,warn | tail -n 50

6B) Install Logwatch for daily summaries

sudo apt update
sudo apt install -y logwatch

Run a test report:

sudo logwatch --detail high --range today --service all --format text

To email it daily, configure /etc/cron.daily/00logwatch or create a custom cron job.

A daily digest is boring in the best way. It catches slow leaks before they become outages.

6C) Add two targeted cron alerts (simple and effective)

Create a small script:

sudo nano /usr/local/sbin/monitor-grep-alerts.sh
#!/bin/bash
set -euo pipefail

ALERT_TO="ops@yourdomain.com"
HOSTNAME=$(hostname -f)

# Alert on repeated 502/504 in the last 10 minutes (Nginx access log)
COUNT_50X=$(awk -v d="$(date -d '10 minutes ago' '+%d/%b/%Y:%H:%M')" 'BEGIN{c=0} $4 ~ d {if ($9 ~ /502|504/) c++} END{print c}' /var/log/nginx/access.log 2>/dev/null || echo 0)

if [ "${COUNT_50X}" -ge 20 ]; then
  echo "${HOSTNAME}: Detected ${COUNT_50X} (502/504) responses in ~10 minutes. Check Nginx error.log and PHP-FPM." | \
    mail -s "[ALERT] ${HOSTNAME} spike in 502/504" "${ALERT_TO}"
fi

# Alert on disk usage above 92% (root)
DISK=$(df -P / | awk 'NR==2{gsub(/%/,"",$5); print $5}')
if [ "${DISK}" -ge 92 ]; then
  echo "${HOSTNAME}: Root filesystem is ${DISK}% used. Clean up logs/caches or expand disk." | \
    mail -s "[ALERT] ${HOSTNAME} disk usage ${DISK}%" "${ALERT_TO}"
fi

Make it executable:

sudo chmod 750 /usr/local/sbin/monitor-grep-alerts.sh

Add a cron entry to run every 5 minutes:

sudo crontab -e
*/5 * * * * /usr/local/sbin/monitor-grep-alerts.sh

This isn’t fancy, and that’s the point. It’s easy to read, easy to tune, and easy to remove when you move to a centralized log pipeline.

Until then, it catches the failures hosting teams trip over most often.

Step 7 — Test alerts on purpose (or you’ll test them during an outage)

Don’t assume alerts work because everything is “green.” Trigger a few controlled failures and confirm the full path from event to inbox.

Test 1: Stop and start a web service

sudo systemctl stop nginx
sleep 70
sudo systemctl start nginx

You should receive an alert and see Monit restart attempts (depending on your configuration).

Test 2: Force a disk threshold (safe method)

Create a temporary file to raise usage (adjust size for your disk):

sudo fallocate -l 2G /root/.diskfill-test
df -h /

Remove it immediately after testing:

sudo rm -f /root/.diskfill-test

Test 3: Confirm external uptime catches real reachability issues

Temporarily block port 443 (only if you can recover safely):

sudo ufw deny 443/tcp
sleep 120
sudo ufw delete deny 443/tcp

If you’re using UFW and want a clean baseline, follow this UFW setup tutorial.

Step 8 — Operational checklist: what to watch weekly

  • Disk and inode trends: if disk grows steadily, identify the directory (often logs, cache, backups, or mail spools).
  • Memory pressure: repeated swap alerts usually mean PHP-FPM tuning or a plan upgrade is due.
  • 502/504 spikes: correlate Nginx errors with PHP-FPM status and slow requests.
  • SSH auth failures: if brute force is heavy, tighten access controls and confirm Fail2Ban policy.
  • SSL renewal horizon: fix renewals at least 15 days before expiry, not the night it expires.

Common pitfalls (and how to avoid noisy alerts)

  • Alerting on CPU instead of load: short CPU spikes are normal. Sustained load is the real “the server feels slow” signal.
  • One threshold for disk: you need an “early” alert and an “urgent” alert, or you’ll train yourself to ignore the only one.
  • Monitoring internal IPs only: your server can see itself even when the internet can’t.
  • Exposing Netdata publicly: keep it on localhost and access via SSH tunnel or VPN.
  • Email alerts with broken deliverability: test your alert inbox end-to-end. If outbound email is part of your hosting, also set rDNS and SPF/DKIM/DMARC.

Want this monitoring baseline without burning a weekend on setup and tuning? Start with a HostMyCode managed VPS hosting plan and refine from there. If you prefer hands-on control (and straightforward scaling once you outgrow shared hosting), a standard HostMyCode VPS gives you root access and predictable resources.

FAQ

Do I need Netdata if I already have Monit?

Yes, if you want quicker diagnosis. Monit answers “is it broken?” Netdata helps you see what changed and roughly when it started.

What alert thresholds are reasonable for a hosting VPS?

Start with disk at 85%/92%, memory at 85%, swap at 25%, and load tuned to CPU count. Then adjust based on your traffic pattern.

How do I avoid false uptime alerts from rate limiting or WAF rules?

Whitelist your uptime checker IP ranges and make sure rate limiting allows a small, steady request pattern. If you’re using Nginx, validate with a second location-based monitor.

Should I monitor email delivery too?

If your server handles mail, yes. Monitor queue growth, outbound errors, and TLS failures. Also make sure DNS and rDNS are correct to prevent “silent” deliverability issues.

Summary: a monitoring setup you can trust

You now have external uptime checks, on-server metrics, practical Monit alerts, and a couple of log signals that catch common hosting failures early.

Keep the setup small, test it regularly, and treat every alert as feedback you can use to reduce noise.

If you’re building this on new infrastructure, pick a plan that fits the workload. For multi-site hosting and predictable performance, HostMyCode VPS is a solid base, and managed VPS hosting is there when you’d rather focus on your sites than babysit servers.