Monitor a Web Hosting Server with Prometheus and Grafana (Ubuntu 24.04)
A hosting server almost always fails in predictable ways: the disk fills up, a service stops, a certificate expires, a backup quietly stops running. To find out before your customers do, you need four pieces: Prometheus to collect the data, node_exporter for CPU, memory and disks, blackbox_exporter to check the sites from the outside, and Grafana to see it all. On Ubuntu 24.04 they install in fifteen minutes. This guide has the commands, a ready-made configuration and the alert rules worth turning on first.
How the stack fits together
- Prometheus polls HTTP endpoints (the exporters) at regular intervals, stores the numbers they return and evaluates alert rules.
- node_exporter exposes system metrics: CPU, memory, disks, network, load.
- blackbox_exporter requests your sites like a visitor would: is it up, which HTTP status, when does the certificate expire.
- Alertmanager receives alerts from Prometheus and sends them by email, Telegram, Slack and more.
- Grafana draws the graphs.
The most important advice comes before any command: run Prometheus on a different server from the one it watches. If the web server hangs, a Prometheus running on it hangs too and never tells you. A VPS costing a few euros a month is enough to watch dozens of servers.
1. Install Prometheus and node_exporter
Ubuntu 24.04 ships the packages in its own repositories:
sudo apt update
sudo apt install prometheus prometheus-node-exporter prometheus-alertmanager prometheus-blackbox-exporter
Prometheus listens on port 9090, node_exporter on 9100, Alertmanager on 9093 and blackbox_exporter on 9115. The main configuration file is /etc/prometheus/prometheus.yml.
On every web server, install only node_exporter and open port 9100 to the monitoring server alone:
sudo apt install prometheus-node-exporter
sudo ufw allow from 203.0.113.50 to any port 9100 proto tcp
Never leave 9100 open to the world: the metrics reveal disk names, versions and load.
2. Configure Prometheus
Replace /etc/prometheus/prometheus.yml on the monitoring server with something like this (the addresses are examples):
global:
scrape_interval: 30s
evaluation_interval: 30s
rule_files:
- /etc/prometheus/rules/*.yml
alerting:
alertmanagers:
- static_configs:
- targets: ["localhost:9093"]
scrape_configs:
- job_name: node
static_configs:
- targets: ["web1.example.com:9100", "web2.example.com:9100"]
- job_name: sites
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://www.customer-one.com
- https://www.customer-two.com
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: localhost:9115
The relabel_configs block is the part that confuses everyone: it tells Prometheus to call blackbox_exporter (localhost:9115) and pass it the site to check as a parameter.
Check the syntax and restart:
sudo mkdir -p /etc/prometheus/rules
promtool check config /etc/prometheus/prometheus.yml
sudo systemctl restart prometheus
Open http://server-ip:9090/targets (better through an SSH tunnel: ssh -L 9090:localhost:9090 server) and check that every target is UP.
3. The alert rules that actually matter
Too many alerts are worse than none: after a week of noise you stop reading them. Start with these five, in /etc/prometheus/rules/hosting.yml:
groups:
- name: hosting
rules:
- alert: ServerDown
expr: up{job="node"} == 0
for: 3m
annotations:
summary: "{{ $labels.instance }} has not answered for 3 minutes"
- alert: DiskAlmostFull
expr: node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes < 0.10
for: 10m
annotations:
summary: "{{ $labels.instance }}: less than 10% free on {{ $labels.mountpoint }}"
- alert: SiteDown
expr: probe_success{job="sites"} == 0
for: 2m
annotations:
summary: "{{ $labels.instance }} is not answering"
- alert: CertificateExpiring
expr: probe_ssl_earliest_cert_expiry - time() < 14 * 86400
for: 1h
annotations:
summary: "The certificate of {{ $labels.instance }} expires in less than 14 days"
- alert: OutOfMemory
expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.05
for: 10m
annotations:
summary: "{{ $labels.instance }}: available memory below 5%"
The for field prevents false alarms: the condition has to stay true for that long before the alert fires. Check with promtool check rules /etc/prometheus/rules/hosting.yml and reload Prometheus.
Why 14 days for certificates? Certbot renews Let's Encrypt certificates about 30 days before they expire: if the old certificate is still there with 14 days left, automatic renewal is broken and you still have two weeks to fix it.
4. Send the alerts by email
Alertmanager reads /etc/prometheus/alertmanager.yml. A minimal email setup:
global:
smtp_smarthost: smtp.example.com:587
smtp_from: alerts@example.com
smtp_auth_username: alerts@example.com
smtp_auth_password: "the-password"
route:
receiver: email
group_wait: 1m
repeat_interval: 12h
receivers:
- name: email
email_configs:
- to: you@example.com
repeat_interval: 12h repeats the alert while the problem is open, but no more than twice a day. Restart with sudo systemctl restart prometheus-alertmanager.
5. Grafana for the graphs
Grafana is not in Ubuntu's repositories: install it from the official one.
sudo apt install -y apt-transport-https wget gnupg
sudo mkdir -p /etc/apt/keyrings
wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update && sudo apt install grafana
sudo systemctl enable --now grafana-server
Grafana listens on port 3000 (initial login admin / admin, it asks you to change it right away). In Connections › Data sources add Prometheus at http://localhost:9090, then in Dashboards › Import enter ID 1860 ("Node Exporter Full"): you instantly get CPU, memory, disk and network graphs for every server.
6. What node_exporter cannot see
node_exporter knows the system, not the sites. It doesn't know which sites you host, who owns them, how much space each one uses, when the last backup ran or which PHP version they use. For that you have to write scripts that produce metrics (with node_exporter's textfile collector) or use a panel that exposes them itself.
Monitoring with Koapanel
Koapanel exposes hosting metrics in the Prometheus format at /api/v1/metrics on every panel, since version 0.27. Nothing to install on the web server: an administrator API key, created in API keys and saved in a file on the monitoring server, is all it takes.
- job_name: koapanel
scheme: https
metrics_path: /api/v1/metrics
scrape_interval: 60s
authorization:
credentials_file: /etc/prometheus/koapanel.key
static_configs:
- targets: ["panel.example.com:8443"]
On top of CPU, memory, disks and services you get:
| Metric | What it tells you |
|---|---|
koapanel_sites |
online and suspended sites |
koapanel_site_info |
every site with owner, PHP version and whether it runs WordPress |
koapanel_site_disk_bytes |
disk space used by each site |
koapanel_site_ssl_expiry_timestamp_seconds |
certificate expiry of each site |
koapanel_site_last_backup_timestamp_seconds |
last successful backup of each site |
koapanel_service_up |
nginx, MariaDB, PHP-FPM, fail2ban and SSH running or stopped |
koapanel_update_available |
a new panel version is out |
The most useful rule node_exporter can't give you is the one on backups:
- alert: BackupMissing
expr: time() - koapanel_site_last_backup_timestamp_seconds > 2 * 86400
for: 1h
annotations:
summary: "{{ $labels.domain }}: no successful backup for two days"
If you'd rather not run Prometheus, the panel still emails you by itself about expiring certificates whose renewal failed, failed backups and disks over 90%: see System emails. The full Prometheus setup is in the manual, section API and webhooks.
FAQ
Does Prometheus use a lot of resources?
Not for a few dozen servers and sites: a VPS with 1-2 GB of RAM is enough. Disk space depends on how many days you keep (15 by default): change it with --storage.tsdb.retention.time in /etc/default/prometheus.
Prometheus or an external uptime service?
Both. The external service tells you a site is down even when your monitoring server is down too; Prometheus tells you why, and warns you before it happens (disk, memory, certificates).
Can I use Zabbix or Netdata instead of Prometheus?
Yes. Both read the Prometheus format, so they also read Koapanel's /api/v1/metrics.
Should Prometheus be reachable from the Internet?
No. Keep 9090, 9093 and 3000 closed and reach them through an SSH tunnel or behind a reverse proxy with authentication.
Try Koapanel
Look around the public demo or install it on a fresh Ubuntu 24.04, free for up to 3 sites:
curl -fsSL https://get.koapanel.app | sudo bash
Every integration, from WHMCS to the API, is on the Integrations page.