How to Set Up Prometheus & Grafana Monitoring on Ubuntu
1 Jul 2025 · 9 min read · Cloud, Devops & Infrastructure, Servers, Tutorials
Introduction
Keeping an eye on a production server is as important as shipping the code that runs on it. Without data you fly blind, guessing at CPU spikes, memory leaks, or a disk filling up in the middle of the night.
In this guide, you'll turn a fresh Ubuntu Virtual Machine [VM] on Katapult into a self-monitored machine using Prometheus for metrics collection and Grafana for dashboards. By the end you will:
- scrape system-level metrics with Node Exporter
- store and query them with Prometheus
- explore live graphs in Grafana
- know where to go next for alerting and extra exporters
We'll move step by step, starting from an up-to-date VM and ending with a dashboard that highlights the health of the host in real time.
Prerequisites
- An Ubuntu Virtual Machine
- A sudo-enabled user (not root)
- Open outbound internet access (for package downloads)
Provision a Virtual Machine with Katapult Compute
- Open the Katapult dashboard, in the sidebar choose Compute ▸ Virtual machines, then click Launch virtual machine.

- Select the data centre closest to your users or team.
- For this walkthrough, the ROCK-3 plan (1 vCPU, 3 GB RAM, 25 GB NVMe) is enough. You can scale up later without rebuilding.
New Katapult users start with £100 free credit, so spinning up the 1 vCPU / 3 GB RAM VM needed for this guide (just £15 per month) costs you nothing while you test the stack.
Under Distributions pick Ubuntu 24.04. Although, this guide can be replicated in any Linux distro.
Keep the public IPv4 & IPV6 addresses that Katapult assigns.
Click Launch virtual machine. Provisioning takes less than a minute.
Once the VM is ready, you can SSH in like this:
ssh root@<VM_IP>
Copy the root password shown in the VM details page and log in as root, then create a sudo user.
- After logging in as root, create a new user with sudo privileges. Replace
with your desired username:
# Create a new user
adduser <username>
# Add the user to the sudo group
usermod -aG sudo <username>
# Verify by switching to the new user and running a sudo command
su - <username>
whoami # should return the new username
That's it! Your fresh Ubuntu 24.04 VM is ready for the monitoring stack we'll set up next.
Architecture at a Glance
Before we get started, let's see how the monitoring pieces fit together on the VM. Metrics will flow through three small services:
[Grafana :3000] ───► [Prometheus :9090] ───► [Node Exporter :9100]
- Node Exporter runs on the VM and exposes CPU, memory, disk, and network data over HTTP.
- Prometheus pulls those metrics at regular intervals, stores them on disk, and offers a query API.
- Grafana connects to Prometheus, turning raw numbers into time‑series graphs and dashboards.
The setup is self‑contained on one host, but you can point Prometheus at many exporters or move Grafana to another server later.
Update and Prep the VM
To get started, run these commands as a sudo-enabled user after logging in:
# Bring the system up to date
sudo apt update && sudo apt upgrade -y
# Install helper tools for downloads and archives
sudo apt install wget tar -y
# Add system users (no shell, no home) for the monitoring daemons
sudo useradd --system --no-create-home --shell /usr/sbin/nologin prometheus
sudo useradd --system --no-create-home --shell /usr/sbin/nologin node_exporter
# Create directories Prometheus will need
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus
Open the ports
- Security group – open TCP 9090 (Prometheus) and TCP 3000 (Grafana) only to IPs you trust (your office VPN, a jump-box, or your own home IP). Avoid
0.0.0.0/0. - ufw on the VM – same idea, but host-level:
sudo ufw allow 9090/tcp
sudo ufw allow 3000/tcp
- HTTPS – both services ship with plain HTTP. You can put Nginx, Traefik, or Caddy in front, add a free Let's Encrypt cert, and protect Grafana with basic auth or OAuth.
That leaves us with a patched OS, reserved service accounts, and open ports—ready to drop in Prometheus next.
Install Prometheus
We'll install Prometheus through its precompiled binaries using the following steps:
- We pull the tarball straight from the project's GitHub page and copy the binaries to
/usr/local/bin:
cd /tmp
PROM_VERSION="3.4.1"
wget https://github.com/prometheus/prometheus/releases/download/v${PROM_VERSION}/prometheus-${PROM_VERSION}.linux-amd64.tar.gz
tar -xzf prometheus-${PROM_VERSION}.linux-amd64.tar.gz
sudo cp prometheus-${PROM_VERSION}.linux-amd64/{prometheus,promtool} /usr/local/bin/
Make sure to download a binary that is compatible with your VM's architecture
- In the downloaded folder, there is a
prometheus.ymlfile which tells Prometheus to poll Node Exporter. We'll create ours by running the following commands:
sudo tee /etc/prometheus/prometheus.yml > /dev/null <<'EOF'
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'node'
static_configs:
- targets: ['localhost:9100']
EOF
sudo chown prometheus:prometheus /etc/prometheus/prometheus.yml
- Create a systemd unit file to run Prometheus as the
prometheususer and restart it if the process exits:
sudo tee /etc/systemd/system/prometheus.service >/dev/null <<'EOF'
[Unit]
Description=Prometheus
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
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
- Reload systemd, start the service, and confirm it is active:
sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
sudo systemctl status prometheus
If the status shows "Active: running", Prometheus is now listening on http://<VM_IP>:9090.
Next, we'll install Node Exporter so Prometheus actually has metrics to scrape.
Install Prometheus Node Exporter
The Prometheus Node Exporter is a tiny Go binary that reads Linux metrics from /proc and makes them available on http://localhost:9100/metrics. Prometheus will scrape that endpoint every 15 seconds.
We'll install it for Prometheus through its precompiled binaries using the following steps:
- Install v1.9.1, the latest stable release at the time of writing:
cd /tmp
NODE_EXPORTER_VERSION="1.9.1"
wget https://github.com/prometheus/node_exporter/releases/download/v${NODE_EXPORTER_VERSION}/node_exporter-${NODE_EXPORTER_VERSION}.linux-${ARCH}.tar.gz
tar -xzf node_exporter-${NODE_EXPORTER_VERSION}.linux-${ARCH}.tar.gz
sudo cp node_exporter-${NODE_EXPORTER_VERSION}.linux-${ARCH}/node_exporter /usr/local/bin/
Why only one file? Everything Node Exporter needs is baked into that single static binary
- Create a systemd unit file to the node exporter as the
node_exporteruser we created earlier:
sudo tee /etc/systemd/system/node_exporter.service >/dev/null <<'EOF'
[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 \
--web.listen-address=:9100
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
- Reload systemd, start the service, and confirm it is active:
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter
sudo systemctl status node_exporter --no-pager
You should see "Active: running". Node Exporter is now listening on http://<VM_IP>:9090/metrics.
- You can also verify the metrics endpoint by running:
curl http://localhost:9100/metrics
You should see output like this:
# HELP go_gc_duration_seconds A summary of the wall-time pause (stop-the-world) duration in garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 2.117e-05
go_gc_duration_seconds{quantile="0.25"} 7.5001e-05
go_gc_duration_seconds{quantile="0.5"} 0.000383494
go_gc_duration_seconds{quantile="0.75"} 0.000550436
go_gc_duration_seconds{quantile="1"} 0.000787689
go_gc_duration_seconds_sum 0.002410226
go_gc_duration_seconds_count 7
....
- Open
http://<VM_IP>:9090/targetsin your browser; the node job should display UP with a "last scrape" time below 15 seconds, confirming that Prometheus is successfully collecting metrics from Node Exporter.

Prometheus is now gathering CPU, memory, disk, and network stats every scrape interval.
With the data pipeline in place, the final piece is a front-end: Grafana.
Install Grafana
Prometheus stores and serves raw time-series data, but its built-in web UI is not exactly interesting. Grafana fills that gap: it's an open-source visualisation layer that speaks PromQL, lets you build dashboards with click-and-drag panels, and ships with thousands of community templates. By wiring Grafana to Prometheus you turn numeric streams into real-time graphs, tables, and alerts—all through a polished browser interface running on port 3000.
We'll install Grafana from its official APT repository so future apt upgrade commands pull in the latest stable release automatically.
- Add the Grafana repo and key:
sudo apt install -y apt-transport-https software-properties-common 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 -a /etc/apt/sources.list.d/grafana.list
sudo apt update
- Install, start, and enable Grafana:
sudo apt install -y grafana
sudo systemctl daemon-reload
sudo systemctl enable --now grafana-server
sudo systemctl status grafana-server --no-pager
A green "Active: running" status means Grafana is up at http://<VM_IP>:3000.
With Grafana installed, open http://<VM_IP>:3000 in a browser to access the dashboard, sign in with admin / admin, and set a new password when prompted.

Next, let's connect the Grafana dashboard to Prometheus!
Connect Grafana to Prometheus
You can connect the Prometheus service running in our VM to Grafana by following these steps:\n
Add Prometheus as a data source
- In the sidebar choose Connections ▸ Data sources ▸ Add data source.
- Select Prometheus.
- Set the Prometheus Server URL to
http://localhost:9090 - Click Save & test. Grafana should reply with "Successfully Queried the Prometheus API"

- Import a starter dashboard
- In the Grafana sidebar click Dashboards ▸ New ▸ Import.
- Enter 1860 (Node Exporter Full) in the dashboard URL field and hit Load.
- Select your Prometheus data source and click Import.

- You'll land on a live dashboard plotting CPU, memory, disk, and network metrics—the same data Node Exporter has been feeding into Prometheus.

With Grafana in place, the monitoring stack is complete:
Grafana ───► Prometheus ───► Node Exporter
Add Remote Machines to Prometheus
So far Prometheus scrapes only the local VM. Let's widen the lens and pull in metrics from other hosts—databases, worker nodes, or anything else that can run Node Exporter.
Repeat the "Install Prometheus Node Exporter" steps on every machine you want to watch. Remember to Open port 9100 in the host's firewall or cloud security group so your Prometheus VM can reach it.
Back on the Prometheus VM, add the extra hosts to
/etc/prometheus/prometheus.yml:
scrape_configs:
- job_name: 'node'
# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
static_configs:
- targets:
- 'localhost:9100' # the Prometheus VM itself
- '10.0.0.12:9100' # database server
- 'worker-02.internal:9100' # background worker
- You can list IPs, DNS names, or mix both. While you're here, label them so dashboards stay readable:
- targets: ['10.0.0.12:9100']
labels:
role: db
env: prod
- Save the file, then reload Prometheus without a full restart:
sudo systemctl reload prometheus
- Open
http://<PROM_VM_IP>:9090/targets. All new nodes should show UP. If one is DOWN, double-check the firewall and hostname. - The Node Exporter Full dashboard you imported earlier already has an $instance drop-down. Pick any host in that list to see its own CPU, memory, and disk graphs—no extra work required.
That's it! You now have one Prometheus installation scraping many exporters.
Benefits and Best Practices
Prometheus and Grafana give you live data on CPU, memory, disk, and network usage. With this information, you can get the following:
- Instant visibility: CPU, memory, disk, and network graphs update every few seconds, so you spot spikes before users feel them.
- Single-pane view: Prometheus scrapes; Grafana shows. One login for all host metrics keeps troubleshooting fast.
- Low overhead: The three services together use under 200 MB RAM on a small VM and add almost no CPU load.
- Scales out: Point Prometheus at exporters on other VMs, or move Grafana to its own host without changing anything you've learned here.
Grafana Dashboard Best Practices
Use these tips to build clear and useful Grafana dashboards.
| Do | Why |
|---|---|
| Keep panels focused – one graph per resource (CPU, memory, disk, network). | Cuts visual noise so problems jump out. |
| Use short time ranges for live ops – 5 m to 1 h | Makes spikes obvious so you can act fast. |
| Add thresholds – red at 80 % CPU, amber at 70 %, for example. | Gives an instant "good/bad" read without reading numbers. |
| Use template variables (for example, $instance) | Add a dropdown that lists every target your Prometheus job scrapes; pick a VM from the list and the same dashboard instantly shows that host's metrics. No need to copy or rebuild the dashboard for each machine. |
| Save versions after edits. | Grafana's revision history lets you roll back a bad tweak. |
Prometheus & Node Exporter Tips
- Scrape every 15s for host metrics: Faster rarely adds value but costs disk.
- Store 15–30 days of data locally: Ship older data to long-term storage if needed.
- Label everything (instance, job, env) early: Re-labeling later is painful.
- Snapshot before upgrades: Take a snapshot with Katapult so you can mount it or spin up a clone if an update misbehaves.
Learn more about Prometheus best practices in the official documentation.
Next steps
- Add file-based service discovery or Consul for large fleets.
- Add alerting – Pair Prometheus with alertmanager, then set rules such as "CPU > 80 % for 5 m". See the Prometheus Alerting docs.
- Secure endpoints – Put nginx or Traefik in front with HTTPS and basic auth, or use Grafana's built-in OAuth.
- Expand exporters – Try
blackbox_exporterfor ping checks orpostgres_exporterfor database metrics.
Share this article
About the author
Shedrack A
Technical Copywriter