Introduction
Monitoring is not optional for production servers. Without visibility into CPU usage, memory consumption, disk I/O, and network traffic, you are flying blind — problems become visible only when users report them, and by then the damage is done. A proper monitoring stack catches resource exhaustion, performance degradation, and anomalies before they escalate into downtime.
Prometheus and Grafana are the industry-standard open-source monitoring combination. Prometheus collects and stores time-series metrics by scraping targets at regular intervals, supports a powerful query language called PromQL, and handles alerting rules. Grafana connects to Prometheus as a data source and provides a web-based dashboard builder for visualizing metrics as graphs, gauges, tables, and heatmaps. Together, they give you complete observability into your server's health.
In this tutorial, you will install Prometheus to collect metrics, deploy Node Exporter to expose system-level metrics (CPU, memory, disk, network), install Grafana for dashboard visualization, connect Grafana to Prometheus, and import a pre-built dashboard to start monitoring immediately. On our own Raff infrastructure, we run this exact stack across all compute nodes — the setup you build in this tutorial mirrors what we use in production.
Step 1 — Create System Users for Prometheus
Prometheus and Node Exporter should run under dedicated system accounts with no login shell and no home directory. This follows the principle of least privilege — if either service is compromised, the attacker cannot use the account to log in or escalate privileges.
Create a system user for Prometheus:
bashsudo useradd --no-create-home --shell /usr/sbin/nologin prometheus
Create the directories Prometheus needs for configuration and data storage:
bashsudo mkdir -p /etc/prometheus
sudo mkdir -p /var/lib/prometheus
Set ownership so the Prometheus user can write to its data directory:
bashsudo chown prometheus:prometheus /var/lib/prometheus
Step 2 — Install Prometheus
Download the latest Prometheus release. Check the Prometheus downloads page for the current version — at the time of writing, the latest LTS is 2.53:
bashcd /tmp
curl -LO https://github.com/prometheus/prometheus/releases/download/v2.53.3/prometheus-2.53.3.linux-amd64.tar.gz
Extract the archive:
bashtar xvf prometheus-2.53.3.linux-amd64.tar.gz
cd prometheus-2.53.3.linux-amd64
Copy the binaries to the system path:
bashsudo cp prometheus promtool /usr/local/bin/
Copy the console templates and libraries to the configuration directory:
bashsudo cp -r consoles console_libraries /etc/prometheus/
Set ownership:
bashsudo chown -R prometheus:prometheus /etc/prometheus
sudo chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool
Verify the installation:
bashprometheus --version
You should see the version number and build information.
Clean up the downloaded files:
bashcd ~
rm -rf /tmp/prometheus-*
Step 3 — Configure Prometheus
Create the Prometheus configuration file:
bashsudo nano /etc/prometheus/prometheus.yml
Add the following configuration:
yamlglobal:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
- job_name: "node_exporter"
static_configs:
- targets: ["localhost:9100"]
This configuration tells Prometheus to scrape metrics every 15 seconds from two targets: itself (port 9090) and Node Exporter (port 9100, which you will install in the next step).
Set the file ownership:
bashsudo chown prometheus:prometheus /etc/prometheus/prometheus.yml
Create a systemd service file so Prometheus starts automatically:
bashsudo nano /etc/systemd/system/prometheus.service
Add the following:
ini[Unit]
Description=Prometheus Monitoring System
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/var/lib/prometheus/ \
--web.console.templates=/etc/prometheus/consoles \
--web.console.libraries=/etc/prometheus/console_libraries \
--storage.tsdb.retention.time=30d
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
The --storage.tsdb.retention.time=30d flag keeps 30 days of metrics data. Adjust this based on your disk space — each day of metrics for a single server typically uses 10-50 MB depending on the number of metrics collected.
Start Prometheus and enable it at boot:
bashsudo systemctl daemon-reload
sudo systemctl start prometheus
sudo systemctl enable prometheus
Verify it is running:
bashsudo systemctl status prometheus
You should see active (running). Test that Prometheus is responding:
bashcurl -s http://localhost:9090/-/healthy
Expected output:
Prometheus Server is Healthy.
Step 4 — Install Node Exporter
Node Exporter is a Prometheus exporter that exposes hardware and OS-level metrics — CPU usage, memory, disk I/O, network traffic, filesystem usage, and more. It runs as a lightweight daemon on each server you want to monitor.
Create a system user for Node Exporter:
bashsudo useradd --no-create-home --shell /usr/sbin/nologin node_exporter
Download and install Node Exporter:
bashcd /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
sudo cp node_exporter-1.8.2.linux-amd64/node_exporter /usr/local/bin/
sudo chown node_exporter:node_exporter /usr/local/bin/node_exporter
Create the systemd service file:
bashsudo nano /etc/systemd/system/node_exporter.service
Add the following:
ini[Unit]
Description=Prometheus Node Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Start Node Exporter and enable it at boot:
bashsudo systemctl daemon-reload
sudo systemctl start node_exporter
sudo systemctl enable node_exporter
Verify it is running:
bashsudo systemctl status node_exporter
Test that metrics are being exposed:
bashcurl -s http://localhost:9100/metrics | head -20
You should see lines starting with # HELP and # TYPE followed by metric names and values. Each metric represents a measurement — CPU seconds, memory bytes, disk operations, network packets.
Clean up:
bashrm -rf /tmp/node_exporter-*
Tip
To monitor additional Raff VMs, install Node Exporter on each server and add its IP to the node_exporter job targets in /etc/prometheus/prometheus.yml. For example: targets: ["localhost:9100", "10.0.0.5:9100", "10.0.0.6:9100"]. Prometheus will scrape all targets at the configured interval.
Step 5 — Install Grafana
Grafana provides the visualization layer. Install it from the official Grafana APT repository to receive the latest updates.
Install the prerequisites and add the Grafana GPG key:
bashsudo apt install -y apt-transport-https software-properties-common
curl -fsSL https://apt.grafana.com/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/grafana.gpg
Add the Grafana repository:
bashecho "deb [signed-by=/usr/share/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
Install Grafana:
bashsudo apt update
sudo apt install -y grafana
Start Grafana and enable it at boot:
bashsudo systemctl start grafana-server
sudo systemctl enable grafana-server
Verify it is running:
bashsudo systemctl status grafana-server
You should see active (running).
Step 6 — Configure the Firewall and Access Grafana
Allow access to Grafana's web interface through UFW:
bashsudo ufw allow 3000/tcp
Tip
For production environments, restrict Grafana access to your own IP: sudo ufw allow from your_ip to any port 3000. You can also place Grafana behind Nginx with HTTPS for encrypted access, or use a WireGuard VPN to access it privately without exposing the port.
Open your browser and navigate to:
http://your_server_ip:3000
Log in with the default credentials:
- Username:
admin - Password:
admin
Grafana immediately prompts you to change the admin password. Set a strong password and click Save.
Warning
Change the default password immediately. Anyone who can reach port 3000 can log in with admin/admin if you do not change it.
Step 7 — Connect Grafana to Prometheus
After logging in, add Prometheus as a data source so Grafana can query metrics.
Click the hamburger menu (three horizontal lines) in the top left, then navigate to Connections → Data sources → Add data source.
Select Prometheus from the list.
In the configuration form, set the Prometheus server URL to:
http://localhost:9090
Leave all other settings at their defaults. Scroll to the bottom and click Save & Test. You should see a green banner confirming "Successfully queried the Prometheus API."
Step 8 — Import a Dashboard
Instead of building dashboards from scratch, import a pre-built community dashboard for Node Exporter metrics. The most popular one is "Node Exporter Full" (Dashboard ID: 1860).
Click the hamburger menu → Dashboards → New → Import.
In the Import via grafana.com field, enter:
1860
Click Load. Grafana fetches the dashboard configuration from grafana.com.
In the Prometheus dropdown at the bottom, select the Prometheus data source you configured in Step 7. Click Import.
You should see a comprehensive dashboard with panels showing CPU usage, memory usage, disk I/O, network traffic, filesystem usage, and system load. The data updates automatically at the scrape interval you configured (15 seconds).
Key panels to monitor:
- CPU Busy — Percentage of CPU time spent on work. Sustained values above 80% suggest you need a larger VM or need to optimize your application.
- RAM Used — Memory consumption. If this consistently exceeds 85% of total RAM, consider upgrading your Raff VM tier.
- Disk I/O — Read and write operations per second. NVMe SSD storage on Raff VMs handles high IOPS without bottlenecking.
- Network Traffic — Bytes sent and received. With unmetered bandwidth on Raff, you do not need to worry about overage charges regardless of traffic volume.
Step 9 — Verify Prometheus Targets
Confirm that Prometheus is successfully scraping both targets. Allow temporary access to the Prometheus UI:
bashsudo ufw allow 9090/tcp
Navigate to http://your_server_ip:9090/targets in your browser. You should see two targets — prometheus and node_exporter — both showing UP in green. If either shows DOWN, check that the respective service is running with systemctl status.
After verifying, remove public access to the Prometheus UI:
bashsudo ufw delete allow 9090/tcp
Warning
Prometheus does not have built-in authentication. Never expose port 9090 to the internet in production. Access it through Grafana (which has authentication) or through an SSH tunnel: ssh -L 9090:localhost:9090 user@your_server_ip.
Conclusion
You have installed Prometheus and Grafana on your Raff Ubuntu 24.04 VM, deployed Node Exporter for system metrics, connected Grafana to Prometheus, and imported a comprehensive monitoring dashboard. Your server now has full observability into CPU, memory, disk, and network performance.
From here, you can:
- Monitor additional Raff VMs by installing Node Exporter on each server and adding them as Prometheus scrape targets
- Set up Grafana alerting to send notifications via email, Slack, or PagerDuty when metrics cross thresholds
- Complement this stack with Uptime Kuma for HTTP endpoint monitoring alongside infrastructure metrics
- Secure Grafana behind Nginx with HTTPS for encrypted dashboard access
- Add application-specific exporters for MySQL, PostgreSQL, Redis, or Docker containers
- Protect access to your monitoring stack using fail2ban and UFW
Prometheus and Grafana together use approximately 300-700 MB of RAM, which fits comfortably on a Raff Tier 2 VM ($9.99/month) alongside lightweight services. For monitoring multiple servers or retaining longer metric history, the Tier 3 ($19.99/month) provides more headroom. NVMe SSD storage ensures fast metric writes and dashboard query responses even with 30 days of retention data.
This tutorial was tested by our security engineering team on a Raff CPU-Optimized Tier 3 VM with Prometheus 2.53 and Grafana 11.

