Guides

Prometheus, Grafana and Alertmanager on a single VPS: a Kubernetes-free weekend guide

Self-hosted monitoring on a single Ubuntu 24.04 VPS: Prometheus, node_exporter, Grafana and an alert when disk usage exceeds 80%.

UptimeMag editorial team · 31 October 2026 · 4 min read

Prometheus, Grafana e Alertmanager su un solo VPS: la guida weekend senza Kubernetes

If you manage two or three VPSs, you don't need a Kubernetes cluster with Prometheus Operator to know whether a disk is about to fill up. Four binaries on a single Ubuntu 24.04 machine, one configuration file per service and an afternoon's work will do. This guide covers installing Prometheus, node_exporter, Grafana and Alertmanager on a single server, with a rule that sends email or Telegram alerts when disk space exceeds 80%.

Versions used in this guide

Versions change over time: before running the commands, always check the latest version number on the official page, because download URLs contain the version in the filename.

Component Version Release date Source
Prometheus 3.15.1 (stable) 21 October 2026 prometheus.io/download
node_exporter 1.12.1 14 July 2026 prometheus.io/download
Alertmanager 0.34.2 29 October 2026 prometheus.io/download
Grafana OSS 13.3.0 27 October 2026 grafana.com/grafana/download

Beyond 3.15.1, Prometheus also publishes an LTS release, 3.13.5, and a pre-release 3.16.0-rc.2: for a production server it's best to stick to the stable or LTS release, not the rc.

Step 1: node_exporter, the system probe

Create the system user and download the binary:

sudo useradd --no-create-home --shell /bin/false node_exporter
cd /tmp
wget https://github.com/prometheus/node_exporter/releases/download/v1.12.1/node_exporter-1.12.1.linux-amd64.tar.gz
tar xvfz node_exporter-1.12.1.linux-amd64.tar.gz
sudo mv node_exporter-1.12.1.linux-amd64/node_exporter /usr/local/bin/
sudo chown root:root /usr/local/bin/node_exporter

Systemd unit in /etc/systemd/system/node_exporter.service:

[Unit]
Description=Prometheus Node Exporter
After=network.target

[Service]
Type=simple
User=node_exporter
ExecStart=/usr/local/bin/node_exporter
Restart=always

[Install]
WantedBy=multi-user.target

Activation:

sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter
curl -s http://localhost:9100/metrics | head

If lines starting with node_ appear, the exporter is working. Default port: 9100.

Step 2: Prometheus

cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v3.15.1/prometheus-3.15.1.linux-amd64.tar.gz
tar xvf prometheus-3.15.1.linux-amd64.tar.gz
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo mv prometheus-3.15.1.linux-amd64/prometheus /usr/local/bin/
sudo mv prometheus-3.15.1.linux-amd64/promtool /usr/local/bin/
sudo mv prometheus-3.15.1.linux-amd64/consoles /etc/prometheus/
sudo mv prometheus-3.15.1.linux-amd64/console_libraries /etc/prometheus/
sudo useradd --no-create-home --shell /bin/false prometheus
sudo chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus

File /etc/prometheus/prometheus.yml:

global:
  scrape_interval: 15s

rule_files:
  - "/etc/prometheus/alert.rules.yml"

alerting:
  alertmanagers:
    - static_configs:
      - targets: ["localhost:9093"]

scrape_configs:
  - job_name: "node"
    static_configs:
      - targets: ["localhost:9100"]

Systemd unit in /etc/systemd/system/prometheus.service:

[Unit]
Description=Prometheus
After=network.target

[Service]
Type=simple
User=prometheus
ExecStart=/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus \
  --web.listen-address=0.0.0.0:9090
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now prometheus

Verify at http://VPS-IP:9090/targets: the node target should show as UP.

Step 3: the disk-over-80% rule

File /etc/prometheus/alert.rules.yml:

groups:
  - name: disk
    rules:
      - alert: DiskAlmostFull
        expr: (1 - (node_filesystem_avail_bytes{mountpoint="/",fstype!="tmpfs"} / node_filesystem_size_bytes{mountpoint="/",fstype!="tmpfs"})) * 100 > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Disk over 80% on {{ $labels.instance }}"
          description: "Disk usage at {{ $value | printf \"%.1f\" }}% on mountpoint /."

Reload without restarting the service:

sudo promtool check rules /etc/prometheus/alert.rules.yml
curl -X POST http://localhost:9090/-/reload

Step 4: Alertmanager and notifications

cd /tmp
wget https://github.com/prometheus/alertmanager/releases/download/v0.34.2/alertmanager-0.34.2.linux-amd64.tar.gz
tar xvf alertmanager-0.34.2.linux-amd64.tar.gz
sudo mkdir -p /etc/alertmanager /var/lib/alertmanager
sudo mv alertmanager-0.34.2.linux-amd64/alertmanager /usr/local/bin/
sudo mv alertmanager-0.34.2.linux-amd64/amtool /usr/local/bin/
sudo useradd --no-create-home --shell /bin/false alertmanager
sudo chown -R alertmanager:alertmanager /etc/alertmanager /var/lib/alertmanager

Email notifications

File /etc/alertmanager/alertmanager.yml:

global:
  smtp_smarthost: 'smtp.yourmailprovider.com:587'
  smtp_from: 'alert@yourdomain.com'
  smtp_auth_username: 'alert@yourdomain.com'
  smtp_auth_password: 'YOURPASSWORD'

route:
  receiver: email-sysadmin

receivers:
  - name: email-sysadmin
    email_configs:
      - to: 'you@yourdomain.com'
        send_resolved: true

Telegram notifications

As an alternative to email, replace the receiver with:

receivers:
  - name: telegram-sysadmin
    telegram_configs:
      - bot_token: 'YOUR_BOT_TOKEN'
        chat_id: -1001234567890
        send_resolved: true
        parse_mode: 'HTML'

The bot is created on Telegram by talking to @BotFather; the chat_id is obtained by sending a message to the bot and reading https://api.telegram.org/bot<TOKEN>/getUpdates.

Systemd unit in /etc/systemd/system/alertmanager.service:

[Unit]
Description=Alertmanager
After=network.target

[Service]
Type=simple
User=alertmanager
ExecStart=/usr/local/bin/alertmanager \
  --config.file=/etc/alertmanager/alertmanager.yml \
  --storage.path=/var/lib/alertmanager
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now alertmanager

Default Alertmanager port: 9093.

Step 5: Grafana and the CPU/RAM/disk/network dashboard

Grafana publishes an official APT repository; the commands below are from the official grafana.com documentation for Debian and Ubuntu:

sudo apt-get install -y apt-transport-https wget gnupg
sudo mkdir -p /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
sudo chmod 644 /etc/apt/keyrings/grafana.asc
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" | sudo tee -a /etc/apt/sources.list.d/grafana.list
sudo apt-get update
sudo apt-get install grafana
sudo systemctl enable --now grafana-server

With this method, Grafana automatically updates when you run apt-get update, unlike the .deb package downloaded manually, which needs to be updated manually with every new version.

Log in at http://VPS-IP:3000 with username admin and password admin (prompted on first login). Add Prometheus as a data source pointing to http://localhost:9090. For the CPU/RAM/disk/network dashboard you don't need to build it from scratch: go to "Dashboards → Import" and import a public dashboard compatible with node_exporter metrics by entering its numeric ID from the grafana.com/dashboards catalogue and selecting the data source you just created.

Final verification: generate a test alert

To verify that the Prometheus → Alertmanager → notification chain works without waiting for the disk to actually fill up, you can send a dummy alert directly to Alertmanager using amtool:

amtool alert add alertname=DiskAlmostFull instance=test-vps severity=warning --alertmanager.url=http://localhost:9093

Within a few seconds you should receive the email or Telegram message. To see active alerts in the queue:

amtool alert query --alertmanager.url=http://localhost:9093

If the notification doesn't arrive, the first place to check is the service log: sudo journalctl -u alertmanager -f.

Security note

Ports 9090, 9093 and 9100 have no authentication by default. On an internet-facing VPS they should be filtered with ufw or iptables, allowing access only from localhost or a VPN, and exposing only Grafana's port 3000 publicly behind a reverse proxy with TLS.

Written with the help of artificial intelligence and checked by the editors (EU AI Act, art. 50).