Back to tutorials
Tutorial

VPS log monitoring tutorial (2026): Set up Fail2Ban, Logwatch, and actionable alerts for a hosting server

VPS log monitoring tutorial: configure Fail2Ban, Logwatch, and alerting to catch SSH, mail, and web attacks fast in 2026.

By Anurag Singh
Updated on Aug 28, 2026
Category: Tutorial
Share article
VPS log monitoring tutorial (2026): Set up Fail2Ban, Logwatch, and actionable alerts for a hosting server

Your VPS can show “up” in monitoring and still be quietly falling apart. Brute-force logins, mail auth failures, and PHP crashes often appear in logs long before a client opens a ticket. This VPS log monitoring tutorial walks through a practical, low-noise setup for Ubuntu or Debian. It focuses on the issues hosting servers actually run into.

You’ll build three layers:

  • Fail2Ban to automatically block repeated authentication attempts and obvious abuse patterns.
  • Logwatch (or Logcheck) to summarize what changed overnight, without reading raw logs.
  • Actionable alerts via email, with thresholds you can live with day after day.

If you want the same outcome with fewer moving parts, start with a managed VPS hosting plan from HostMyCode. You keep root access. We help keep the baseline secure and stable.

What you’ll build (and what it will catch)

This stack targets common hosting incidents in 2026:

  • SSH brute-force and password spraying
  • Web auth abuse (WordPress login floods, XML-RPC hits, admin panels)
  • Mail abuse signals (auth failures, repeated SASL failures, sudden spikes in queue errors)
  • Service instability (OOM kills, disk-full conditions, cron failures)

It’s not a SIEM. It’s not trying to be.

It’s a hosting-friendly baseline that runs on a small VPS. It also avoids constant alert tuning.

Prerequisites and server assumptions

  • Ubuntu 24.04 LTS / 26.04 LTS, or Debian 12/13 (root or sudo access)
  • A working MTA for outbound alerts (Postfix is fine). If you’d rather relay mail through a provider, follow the HostMyCode guide: SMTP relay setup guide tutorial (2026).
  • Systemd and journald (default on modern Ubuntu/Debian)

If you’re hosting client sites, start with predictable CPU and I/O.

A HostMyCode VPS gives you full control for this tutorial while keeping costs sane.

Step 1: Make sure logs are actually being kept

Alerting is pointless if logs vanish under disk pressure or rotate too aggressively.

On Ubuntu/Debian you’ll usually have classic files in /var/log/. You’ll also have the systemd journal.

Check disk space and log rotation

df -h
sudo logrotate -d /etc/logrotate.conf | less
sudo ls -lh /var/log | head

If your disk is tight, “monitoring” becomes a series of gaps.

On hosting servers, keep at least 15–20% free space on the root filesystem.

Persist the systemd journal (optional but recommended)

Some builds keep journald in memory by default.

Make it persistent so you can review reboots and short outages later:

sudo mkdir -p /var/log/journal
sudo sed -i 's/^#Storage=.*/Storage=persistent/' /etc/systemd/journald.conf
sudo systemctl restart systemd-journald

Quick check:

journalctl --disk-usage

Step 2: Install Fail2Ban and turn on sane defaults

Fail2Ban is the most “active” piece in this stack because it blocks traffic.

That’s why it pays off quickly on any public-facing hosting VPS.

sudo apt update
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
sudo fail2ban-client status

Don’t edit /etc/fail2ban/jail.conf directly.

Use a local override instead:

sudo nano /etc/fail2ban/jail.local

Paste a baseline that fits most hosting setups:

[DEFAULT]
# Ban for 2 hours; long enough to reduce noise, short enough to avoid permanent lockouts.
bantime  = 2h
findtime = 10m
maxretry = 5
backend  = systemd

# Email alerts only for repeat offenders (noise control)
destemail = root
sender = fail2ban@YOUR_HOSTNAME
mta = sendmail
action = %(action_mwl)s

# Your admin IP(s) to avoid accidental lockouts
ignoreip = 127.0.0.1/8 ::1 YOUR_PUBLIC_ADMIN_IP

[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s

# If you run a mail stack on the VPS (Postfix/Dovecot), enable these.
# Disable them if this server does not handle mail.
[postfix]
enabled = true

[dovecot]
enabled = true

# Basic web auth jail (useful if you have HTTP auth-protected endpoints)
[apache-auth]
enabled = false

[nginx-http-auth]
enabled = false

Replace YOUR_PUBLIC_ADMIN_IP and YOUR_HOSTNAME.

If your IP changes often, don’t whitelist huge ranges.

Use a jump box or VPN instead.

Restart and confirm:

sudo systemctl restart fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshd

Quick diagnostic: confirm bans are working (without locking yourself out)

From a different machine (or using a temporary test user), trigger a few failed SSH logins.

Then check what Fail2Ban did:

sudo fail2ban-client get sshd banip
sudo iptables -S | grep -i f2b | head
# If you use nftables on your distro:
sudo nft list ruleset | grep -i f2b | head

If you’re on a cPanel server, don’t stack random firewall systems and hope it works.

For cPanel-specific hardening patterns, use the existing HostMyCode walkthrough: cPanel Fail2Ban setup guide tutorial (2026).

Step 3: Add a WordPress login jail (optional, high value)

On WordPress-heavy VPSs, login floods can burn CPU even when they never succeed.

The key is clean logging. Then you ban repeated hits to wp-login.php and xmlrpc.php.

Nginx: add a dedicated access log for WordPress login endpoints. In your server block (commonly under /etc/nginx/sites-available/):

location = /wp-login.php {
  access_log /var/log/nginx/wp-login-access.log;
  try_files $uri $uri/ /index.php?$args;
}

location = /xmlrpc.php {
  access_log /var/log/nginx/xmlrpc-access.log;
  try_files $uri $uri/ /index.php?$args;
}

Reload Nginx:

sudo nginx -t && sudo systemctl reload nginx

Create Fail2Ban filters:

sudo nano /etc/fail2ban/filter.d/nginx-wp-login.conf
[Definition]
failregex = ^<HOST> - .* "POST /wp-login\.php .*" (200|301|302|403|404)
ignoreregex =

Then a jail:

sudo nano /etc/fail2ban/jail.d/nginx-wp-login.local
[nginx-wp-login]
enabled = true
port = http,https
filter = nginx-wp-login
logpath = /var/log/nginx/wp-login-access.log
findtime = 10m
maxretry = 20
bantime = 2h

Restart Fail2Ban:

sudo systemctl restart fail2ban
sudo fail2ban-client status nginx-wp-login

Keep thresholds forgiving. Real users mistype passwords.

Your goal is to stop floods, not block customers.

If you’re still building the WordPress stack itself, HostMyCode has a full deployment flow you can pair with this monitoring: WordPress VPS setup guide tutorial (2026).

Step 4: Install Logwatch for daily summaries (low effort, high signal)

Raw logs are loud.

Logwatch turns them into a digest you can scan in a couple minutes.

sudo apt install -y logwatch

Create a local config so package updates don’t overwrite your settings:

sudo nano /etc/logwatch/conf/logwatch.conf

Adjust these common options:

MailTo = you@yourdomain.com
MailFrom = logwatch@YOUR_HOSTNAME
Detail = Med
Range = yesterday
Service = All
Format = html

Test-run it before you trust cron:

sudo logwatch --range today --detail Med --format text | less

On Ubuntu/Debian, Logwatch usually runs from /etc/cron.daily/00logwatch.

Confirm it’s there:

sudo ls -l /etc/cron.daily | grep -i logwatch

Pitfall: Logwatch emails not arriving

  • If mail is stuck locally, troubleshoot the queue (Postfix): mailq, postqueue -p
  • If your server IP has reputation issues, fix deliverability before relying on alerts. HostMyCode’s guide helps: email bounce troubleshooting tutorial (2026).

Step 5: Add “fast alerts” for the few things that can’t wait until morning

Daily summaries cover most situations. They don’t help when the root filesystem fills up or SSH gets hammered.

Keep fast alerts narrow. If everything is urgent, nothing is.

Disk space alert with a simple systemd timer

Create a script that emails you when free space drops below a threshold.

sudo nano /usr/local/sbin/disk-alert.sh
#!/bin/sh
THRESHOLD=85
TO="you@yourdomain.com"
HOST="$(hostname -f 2>/dev/null || hostname)"

df -P / | awk 'NR==2 {gsub("%","",$5); print $5}' | while read -r USED; do
  if [ "$USED" -ge "$THRESHOLD" ]; then
    echo "Root filesystem is ${USED}% full on ${HOST}.\n\n$(df -h /)" \
      | mail -s "[ALERT] Disk usage ${USED}% on ${HOST}" "$TO"
  fi
done
sudo chmod 750 /usr/local/sbin/disk-alert.sh

Create a systemd unit and timer:

sudo nano /etc/systemd/system/disk-alert.service
[Unit]
Description=Disk usage email alert

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/disk-alert.sh
sudo nano /etc/systemd/system/disk-alert.timer
[Unit]
Description=Run disk usage alert every 10 minutes

[Timer]
OnBootSec=5m
OnUnitActiveSec=10m

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now disk-alert.timer
systemctl list-timers | grep -i disk-alert

SSH “spike” alert via journald query (optional)

This helps on a new server before you have much history.

Start by running it manually:

sudo journalctl -u ssh --since "10 min ago" | grep -ci "Failed password"

If the count stays high, fix the cause.

Tighten SSH access (keys only, disable password auth, reduce exposure). HostMyCode walks through a safer baseline here: Server hardening tutorial (2026).

Step 6: Make Fail2Ban and alerts work with your firewall (without self-sabotage)

On a hosting VPS you’ll typically use UFW or nftables directly.

Mixing three firewall stacks is how you end up with mystery outages.

  • If you use UFW, keep rules boring: allow SSH (preferably from trusted sources), allow 80/443, and open mail ports only if you actually run mail.
  • If you use a hosting panel firewall (CSF on cPanel, for example), let it own the rules and integrate Fail2Ban carefully.

If you’ve already locked yourself out or Let’s Encrypt renewals started failing after firewall changes, use the troubleshooting runbook: UFW firewall troubleshooting tutorial (2026).

Step 7: Tune noise down (so you keep paying attention)

Alert fatigue is predictable, not a personal failure.

Hosting servers attract background junk: bots, broken clients, and misconfigured plugins.

Tune for signals that predict downtime, compromise, or customer pain.

Practical noise controls

  • Whitelist carefully: only your admin IPs and monitoring IPs, not entire countries or ISPs.
  • Use longer findtime for “slow” attacks: password spraying often spreads attempts over 15–60 minutes.
  • Separate jails per service: SSH can be strict; WordPress login can be more forgiving.
  • Review bans weekly: if you routinely ban real customers, your thresholds are too aggressive.

Fail2Ban reporting commands you’ll actually use

sudo fail2ban-client status
sudo fail2ban-client status sshd
sudo fail2ban-client get sshd banned
sudo fail2ban-client get sshd banip

Step 8: Add a simple incident checklist (copy/paste ready)

When an alert fires, you want a short path to “is this real?” and “what’s the next move?”

  • SSH brute-force: fail2ban-client status sshd → check banned IPs → confirm password auth is disabled if possible.
  • Disk alert: df -hsudo du -xhd1 /var | sort -h → check /var/log, backups, and tmp files.
  • Mail failures: check queue: mailq / postqueue -p → review auth failures → confirm SPF/DKIM/DMARC and PTR.
  • Web login flood: check Nginx access log count: awk '{print $1}' /var/log/nginx/wp-login-access.log | sort | uniq -c | sort -nr | head

If your plan includes restore or failover, don’t wing it at 2 a.m.

Write the steps down and test them. HostMyCode’s restore-oriented playbook pairs well with this: VPS disaster recovery tutorial (2026).

Step 9: Optional hardening that improves log quality

Cleaner server config often means cleaner logs.

Two upgrades that make monitoring easier:

  • Turn on security headers and clearer access logs on Nginx/Apache so you can tie incidents to specific endpoints.
  • Use SFTP with per-user access instead of shared FTP passwords; it reduces credential leaks and leaves a better audit trail.

HostMyCode has an SFTP lockdown guide if you need it: SFTP setup tutorial (2026).

Summary: a monitoring stack that fits real hosting ops

You now have Fail2Ban actively blocking repeat abuse, Logwatch delivering a daily digest, and a couple of fast alerts for failures that can turn into downtime.

It’s intentionally simple. That’s why you’ll keep it running.

If you want a VPS where these basics stay easy to maintain—clean OS images, stable networking, and support that understands hosting workflows—start with HostMyCode VPS or step up to managed VPS hosting for help with ongoing server care.

If you run client sites or business-critical WordPress, monitoring is how you catch trouble before it becomes downtime. HostMyCode can provision a VPS sized for your traffic and help you keep the baseline secure.

Start with a HostMyCode VPS, or choose managed VPS hosting if you want hands-on help with hardening, updates, and routine operational checks.

FAQ

Should I use Logwatch or Logcheck?

Logwatch is a solid fit for daily summaries and spotting trends.

Logcheck is stricter and often noisier.

For most hosting VPSs, start with Logwatch and add targeted “fast alerts” for disk and SSH spikes.

Can Fail2Ban break Let’s Encrypt renewals?

Yes, if you ban your own IP while testing or write web filters that match too broadly.

Keep web jails tightly scoped to login endpoints, and whitelist your admin IPs.

If renewals fail, check your firewall rules and HTTP logs.

How do I monitor a cPanel server differently?

Use cPanel-aware patterns and avoid stacking conflicting firewall systems.

Start with HostMyCode’s cPanel-specific security and Fail2Ban guides, then add Logwatch for a daily digest.

Where should I send alerts if my VPS can’t reliably deliver mail?

Use an SMTP relay through a trusted provider so alerts don’t depend on your server IP reputation.

Configure SPF/DKIM/DMARC for the sending domain and keep relay credentials locked down.

What’s the minimum monitoring I should run on a small VPS?

Fail2Ban for SSH, a disk usage alert, and Logwatch daily summaries. That combination catches most “silent failures” before they become outages.

VPS log monitoring tutorial (2026): Set up Fail2Ban, Logwatch, and actionable alerts for a hosting server | HostMyCode