Set Up Prometheus and Grafana on a VPS
Netdata (or just htop in a spare terminal) is fine for watching a server right now. It falls apart the moment you want to know what CPU looked like three weeks ago, get an alert in Slack instead of a red graph nobody's looking at, or see two VPS boxes on one screen. That's the gap Prometheus and Grafana fill, and you can have both running on a single VPS in well under an hour.
Symptom: you've outgrown "just check top"
A few signs this is you:
- A site was slow at 3 AM last night and you have no way to prove whether it was CPU, memory, or disk I/O — the moment's gone.
- You're running two or three VPS instances and keep opening separate SSH sessions just to compare load.
- You want an alert before disk hits 100%, not a support ticket after it does.
- Netdata's free tier only keeps a few hours of local history, and the cloud tier's retention doesn't match what you need for capacity planning.
None of that is a Netdata problem specifically — it's what happens when a single-node, real-time tool gets asked to do long-term, multi-node work. Prometheus (a time-series database that scrapes metrics on a schedule) plus Grafana (the dashboard and alerting layer on top) is the standard combination for exactly this.
Cause: real monitoring needs three separate pieces
People often expect "install Prometheus" to be one step. It's actually three small services working together, and understanding the split makes the setup make sense instead of feeling like magic:
| Component | Job | Runs where |
|---|---|---|
node_exporter | Reads CPU, memory, disk, and network stats from the OS and exposes them on a local port | Every VPS you want to monitor |
| Prometheus | Scrapes those exporters on a timer (default 15s) and stores the history | One server — can be the same VPS or a dedicated monitoring box |
| Grafana | Queries Prometheus and draws the dashboards, plus handles alert rules | Same place as Prometheus, usually |
For a single VPS, all three can live on that one box. If you've got several, put Prometheus and Grafana on one central VPS and just run node_exporter on the others.
Fix: install node_exporter, Prometheus, and Grafana
This assumes Ubuntu 22.04/24.04 or AlmaLinux 8/9 with systemd and root or sudo access. Swap apt for dnf where noted.
Step 1 — Install node_exporter (do this on every server you're monitoring)
useradd --no-create-home --shell /usr/sbin/nologin node_exporter
cd /tmp
curl -LO https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz
tar xvf node_exporter-1.8.2.linux-amd64.tar.gz
cp node_exporter-1.8.2.linux-amd64/node_exporter /usr/local/bin/
chown node_exporter:node_exporter /usr/local/bin/node_exporter
Create /etc/systemd/system/node_exporter.service:
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=node_exporter
ExecStart=/usr/local/bin/node_exporter
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now node_exporter
curl http://localhost:9100/metrics | head -n 5
If that last command prints metric lines instead of "connection refused," it's working. Repeat this step on every VPS you want data from — that's the only step that needs to happen on each one.
Step 2 — Install Prometheus (on your monitoring VPS)
useradd --no-create-home --shell /usr/sbin/nologin prometheus
mkdir /etc/prometheus /var/lib/prometheus
cd /tmp
curl -LO https://github.com/prometheus/prometheus/releases/download/v2.54.1/prometheus-2.54.1.linux-amd64.tar.gz
tar xvf prometheus-2.54.1.linux-amd64.tar.gz
cd prometheus-2.54.1.linux-amd64
cp prometheus promtool /usr/local/bin/
cp -r consoles console_libraries /etc/prometheus/
chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus /usr/local/bin/prometheus /usr/local/bin/promtool
Edit /etc/prometheus/prometheus.yml and add a scrape target for each server's node_exporter:
scrape_configs:
- job_name: "node"
static_configs:
- targets: ["localhost:9100", "203.0.113.15:9100", "203.0.113.16:9100"]
If node_exporter is on a different VPS, you need port 9100 reachable between them — over a WireGuard tunnel or a firewall rule scoped to that server's IP only, never open to the internet (more on that below).
Create /etc/systemd/system/prometheus.service:
[Unit]
Description=Prometheus
After=network.target
[Service]
User=prometheus
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/var/lib/prometheus \
--storage.tsdb.retention.time=30d
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now prometheus
Check http://your-vps-ip:9090/targets (over SSH tunnel, not directly — see Step 4) — every target should show State: UP. If one shows DOWN, it's almost always a firewall blocking port 9100 between the two servers, not Prometheus itself.
Step 3 — Install Grafana
# Ubuntu/Debian
curl https://apt.grafana.com/gpg.key | gpg --dearmor -o /usr/share/keyrings/grafana.gpg
echo "deb [signed-by=/usr/share/keyrings/grafana.gpg] https://apt.grafana.com stable main" | tee /etc/apt/sources.list.d/grafana.list
apt update && apt install -y grafana
systemctl enable --now grafana-server
# AlmaLinux/RHEL
cat <<EOF | tee /etc/yum.repos.d/grafana.repo
[grafana]
name=grafana
baseurl=https://rpm.grafana.com
enabled=1
gpgcheck=1
gpgkey=https://rpm.grafana.com/gpg.key
EOF
dnf install -y grafana
systemctl enable --now grafana-server
Grafana listens on port 3000 by default with a first-login username and password of admin/admin — it'll force a password change immediately.
Step 4 — Reach it safely, then add the data source
Don't open 3000 or 9090 to the world. Tunnel in over SSH instead:
ssh -L 3000:localhost:3000 -L 9090:localhost:9090 user@your-vps-ip
Now open http://localhost:3000 in your own browser. Inside Grafana: Connections → Data sources → Add data source → Prometheus, set the URL to http://localhost:9090, and save.
Then Dashboards → New → Import, and enter dashboard ID 1860 ("Node Exporter Full" — the standard community dashboard). Pick your Prometheus data source and you'll have CPU, memory, disk, network, and load average graphs for every server you added, with real history, in about two clicks.
Setting up an alert instead of watching a graph
In Grafana, open any panel (disk usage is a good first one), click Edit → Alert, and set a condition like "disk free < 10% for 5 minutes." Under Alerting → Contact points, add a Slack webhook or an SMTP email so the alert actually reaches you instead of just changing a panel's color. This is the entire point of the exercise — Netdata can show you the graph, but Prometheus + Grafana can wake you up before the disk fills and takes the site down with it.
Prevention: keep the monitoring stack from becoming its own problem
- Cap retention. The
--storage.tsdb.retention.time=30dflag above stops Prometheus from quietly filling the disk with years of 15-second samples. Adjust to what you actually need. - Firewall it properly. On Ubuntu,
ufw deny 9090 && ufw deny 3000and access only via SSH tunnel or a reverse proxy with auth in front. On AlmaLinux, do the same withfirewalldzones. - Don't monitor the monitor from itself. If Prometheus and your main site share one small VPS, a runaway process on either can starve the other. For anything beyond one or two low-traffic sites, put monitoring on its own small VPS.
- Watch the watcher.
systemctl status prometheus grafana-server node_exporterafter any VPS reboot — none of these auto-heal from a crashed dependency, they just don't start.
Monitoring more than one VPS from one dashboard
This is the actual payoff over Netdata's per-server view: once node_exporter is running on each box and each one has a line in Prometheus's scrape_configs, the same Grafana dashboard shows all of them side by side, filterable by hostname. No extra Grafana install needed — one Prometheus, one Grafana, as many exporters as you have servers.
How this compares to Netdata
| Netdata | Prometheus + Grafana | |
|---|---|---|
| Setup time | ~5 minutes, one command | ~30-45 minutes, several services |
| History | Hours on free local tier | As long as your disk allows (weeks to months) |
| Multi-server view | Needs Netdata Cloud (paid tiers for teams) | Native, free, one dashboard |
| Alerting | Built in, simple | More flexible, more setup |
| Best for | "What's wrong right now" on one box | Trends, fleets, and alerts that page you |
They're not really competitors — plenty of admins run Netdata for the instant local view and Prometheus/Grafana for history and alerting. Pick based on how many servers you're managing and whether "check it now" or "get warned before it breaks" is the bigger gap.
Frequently asked questions
Do I need Prometheus and Grafana if I already use Netdata?
Not necessarily. Netdata is faster to set up and great for a live, one-server view. Prometheus and Grafana are worth the extra setup once you need weeks of history, alerts that reach Slack or email, or one dashboard covering several VPS instances at once.
How much disk space does Prometheus use?
With default 15-second scrape intervals and a couple of servers, expect roughly 1-2GB per month of retained data. The --storage.tsdb.retention.time flag in the systemd unit caps this automatically, so set it to match your disk size and needs.
Is it safe to expose ports 9090 and 3000 to the internet?
No. Neither has authentication on by default for the Prometheus UI, and both are common scan targets. Keep them firewalled and access Grafana either through an SSH tunnel or behind an Nginx reverse proxy with its own login.
Can I monitor a cPanel shared hosting account this way?
Not directly — node_exporter needs root-level systemd access to read OS metrics, which shared hosting doesn't give you. This setup is for VPS or dedicated servers where you control the operating system.
What's the fastest way to get a useful dashboard once Grafana is installed?
Import community dashboard ID 1860 ("Node Exporter Full") under Dashboards -> New -> Import. It's pre-built for exactly the metrics node_exporter reports, so you get CPU, memory, disk, and network graphs immediately without building panels by hand.