The architecture
Prometheus collects and stores metrics (a time-series database), node_exporter exposes the VPS's system metrics (CPU, RAM, disk, network), Grafana visualizes them, Alertmanager sends the alerts. Four services, one VPS, zero external dependencies.
Prerequisites
An Ubuntu 24.04 VPS with at least 2 GB of RAM and root access. Ports 3000 (Grafana), 9090 (Prometheus) and 9093 (Alertmanager) stay on localhost or behind a reverse proxy — never expose them directly to the Internet.
Step 1 — Install Prometheus
Create the user and directories, then install the official binary (the 2.53 series is an LTS):
useradd --no-create-home --shell /usr/sbin/nologin prometheus
mkdir /etc/prometheus /var/lib/prometheus
cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v2.53.1/prometheus-2.53.1.linux-amd64.tar.gz
tar -xzf prometheus-2.53.1.linux-amd64.tar.gz
cp prometheus-2.53.1.linux-amd64/prometheus prometheus-2.53.1.linux-amd64/promtool /usr/local/bin/
chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheusCreate /etc/prometheus/prometheus.yml:
global:
scrape_interval: 15s
rule_files:
- /etc/prometheus/rules/*.yml
alerting:
alertmanagers:
- static_configs:
- targets: ['localhost:9093']
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ['localhost:9090']
- job_name: node
static_configs:
- targets: ['localhost:9100']Then the systemd unit /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=15d
Restart=always
[Install]
WantedBy=multi-user.targetValidate the configuration and start:
promtool check config /etc/prometheus/prometheus.yml
systemctl daemon-reload
systemctl enable --now prometheusStep 2 — Install node_exporter
The Ubuntu package is good enough and installs the systemd service automatically:
apt install -y prometheus-node-exporter
systemctl status prometheus-node-exporterThe exporter listens on port 9100. On a powerful VPS, self-monitoring is essentially free — the scrape load is negligible on a Ryzen 9.
Step 3 — Install Grafana
Add the official repository:
install -d -m 0755 /etc/apt/keyrings
wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor -o /etc/apt/keyrings/grafana.gpg
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" > /etc/apt/sources.list.d/grafana.list
apt update && apt install -y grafana
systemctl enable --now grafana-serverStep 4 — First dashboard
Open http://VPS-IP:3000 (through an SSH tunnel ssh -L 3000:localhost:3000 if the port is filtered). Log in with admin/admin, change the password, then:
1. Connections → Data sources → Add → Prometheus, URL http://localhost:9090, Save & test. 2. Dashboards → Import → enter ID 1860 ("Node Exporter Full") → pick your Prometheus source.
You immediately get real-time CPU, RAM, disk, network and I/O graphs.
Step 5 — Install and configure Alertmanager
apt install -y prometheus-alertmanagerEdit /etc/prometheus/alertmanager.yml to route alerts to email and a Telegram bot (create the bot via @BotFather, get your chat_id from the bot's getUpdates URL):
global:
smtp_smarthost: 'smtp.example.com:587'
smtp_from: '[email protected]'
smtp_auth_username: '[email protected]'
smtp_auth_password: 'SMTPPassword'
route:
receiver: email
group_by: [alertname]
routes:
- match:
severity: critical
receiver: telegram
receivers:
- name: email
email_configs:
- to: '[email protected]'
- name: telegram
telegram_configs:
- bot_token: '123456:ABC-DEF-your-token'
chat_id: 123456789
message: '{{ .CommonAnnotations.summary }}'systemctl restart prometheus-alertmanagerStep 6 — Alert rules
Create /etc/prometheus/rules/node.yml:
groups:
- name: node
rules:
- alert: InstanceDown
expr: up == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Instance {{ $labels.instance }} unreachable"
- alert: DiskAlmostFull
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 15
for: 10m
labels:
severity: warning
annotations:
summary: "Root filesystem over 85% full on {{ $labels.instance }}"
- alert: HighLoad
expr: node_load1 > 8
for: 15m
labels:
severity: warning
annotations:
summary: "High load on {{ $labels.instance }}"Tune the HighLoad threshold to your vCPU count. Validate and reload:
promtool check rules /etc/prometheus/rules/node.yml
systemctl reload prometheusVerification
curl localhost:9090/-/healthyandcurl localhost:9100/metrics | headboth respond.- In Prometheus, Status → Targets: both jobs are UP.
- Alerts: your three rules show up in green.
- Real test:
systemctl stop prometheus-node-exporter— the InstanceDown alert fires after 2 minutes, Telegram and email both receive it. Restart the service.
Troubleshooting
- Target DOWN: check the service is running (
ss -tlnp | grep 9100) and the target really islocalhost. - Alerts not received:
journalctl -u prometheus-alertmanager; for Telegram, the chat_id is numeric and the bot must have received a first message from you. - Grafana datasource error: use
http://localhost:9090, not the hostname, and make sure Prometheus is listening.
Going further
Any server running node_exporter can join the node job: add its IP to the target list in prometheus.yml, reload, and one Prometheus instance watches your whole fleet. For black-box checks — HTTP status, DNS answers, TLS certificate expiry — install the prometheus-blackbox-exporter package and wire it as an additional job; it pairs naturally with uptime alerts routed through the same Alertmanager. Dashboards for these exporters are importable from grafana.com the same way as ID 1860.