1Gbps Dedicated Servers
Unmetered Dedicated Servers
AMD Dedicated Servers
Gaming Dedicated Servers
10Gbps Dedicated Servers
GPU Servers
Intel Dedicated Servers
DDOS Protected Dedicated Servers
About Us
Contact Us
Blogs
Tutorials
A server can be "up" and still be quietly failing you. Disk filling up over weeks, memory creeping toward a swap death spiral, a backup job that's been silently erroring for days — none of that shows up in a basic uptime check. The only way to catch it is to actually watch the numbers over time, not just whether the server responds to a ping.
This guide sets up a self-hosted observability stack — Prometheus for metrics collection and storage, Node Exporter for exposing system-level statistics, and Grafana for visualizing all of it on dashboards — on a bare-metal Ubuntu 24.04 server. By the end, you'll have live graphs of CPU load, memory pressure, disk usage, and network throughput, with no dependency on a third-party SaaS monitoring platform.
Before installing anything, it helps to know what each component actually does and where data flows between them:
[Node Exporter :9100] ──scraped by──► [Prometheus :9090] ──queried by──► [Grafana :3000]
Node Exporter is a lightweight agent that reads hardware and OS-level statistics straight from the kernel (via /proc and /sys) and exposes them on a plain HTTP endpoint.
/proc
/sys
Prometheus is the collection and storage engine. Rather than waiting for data to be pushed to it, it pulls (scrapes) metrics from each exporter on a schedule and stores them as time-series data.
Grafana is the visualization layer. It queries Prometheus using PromQL and renders the results as dashboards, graphs, and alerts.
This pull-based model is one reason the stack scales well on bare metal — adding a new server to watch just means pointing Prometheus at one more exporter endpoint, with no agent needing to know where to send anything.
A bare-metal or dedicated server running Ubuntu 24.04 LTS.
A non-root user with sudo privileges.
sudo
Open inbound access (locally or via firewall rule) on ports 9090 (Prometheus), 3000 (Grafana), and 9100 (Node Exporter) — or, better, restrict these to your management network and reach Grafana through a reverse proxy, covered at the end.
Running monitoring services under their own unprivileged accounts limits what damage a compromised process could do — a sound default for anything reading system-level data with elevated visibility.
sudo useradd --no-create-home --shell /bin/false prometheus sudo useradd --no-create-home --shell /bin/false node_exporter sudo mkdir -p /etc/prometheus /var/lib/prometheus sudo chown prometheus:prometheus /etc/prometheus /var/lib/prometheus
Prometheus ships as a single static binary, so installation is just downloading, extracting, and placing it.
cd /tmp curl -s https://api.github.com/repos/prometheus/prometheus/releases/latest \ | grep browser_download_url | grep linux-amd64 | cut -d '"' -f 4 | wget -qi - tar xvf prometheus-*.linux-amd64.tar.gz cd prometheus-*.linux-amd64 sudo mv prometheus promtool /usr/local/bin/ sudo mv consoles console_libraries /etc/prometheus/ sudo mv prometheus.yml /etc/prometheus/prometheus.yml sudo chown -R prometheus:prometheus /etc/prometheus /usr/local/bin/prometheus /usr/local/bin/promtool
Create a systemd unit so Prometheus runs as a managed background service and restarts automatically if it crashes:
sudo tee /etc/systemd/system/prometheus.service > /dev/null <
A status showing Active: active (running) means it started cleanly. Prometheus's own web interface is now reachable at http://your-server-ip:9090 — it scrapes its own internal metrics by default, so you should already see one target listed under Status → Targets.
Active: active (running)
http://your-server-ip:9090
Node Exporter follows the same binary-and-systemd pattern, exposing the actual hardware and OS metrics — CPU time, memory pressure, disk I/O, filesystem usage, network counters — that Prometheus will go on to collect.
cd /tmp curl -s https://api.github.com/repos/prometheus/node_exporter/releases/latest \ | grep browser_download_url | grep linux-amd64 | cut -d '"' -f 4 | wget -qi - tar xvf node_exporter-*.linux-amd64.tar.gz sudo mv node_exporter-*.linux-amd64/node_exporter /usr/local/bin/ sudo chown node_exporter:node_exporter /usr/local/bin/node_exporter
sudo tee /etc/systemd/system/node_exporter.service > /dev/null <
Confirm it's exposing data by visiting http://your-server-ip:9100/metrics — you should see a long plaintext list of metric names and values. This page is Node Exporter's entire interface; there's no dashboard here, just raw numbers waiting to be scraped.
http://your-server-ip:9100/metrics
Right now Prometheus only knows about itself. Edit /etc/prometheus/prometheus.yml and add Node Exporter as a scrape target under scrape_configs:
/etc/prometheus/prometheus.yml
scrape_configs
scrape_configs: - job_name: "prometheus" static_configs: - targets: ["localhost:9090"] - job_name: "node" static_configs: - targets: ["localhost:9100"]
If you're monitoring multiple bare-metal servers from one central Prometheus instance, add each one's IP under the same node job:
node
- job_name: "node" static_configs: - targets: ["10.0.0.11:9100", "10.0.0.12:9100", "10.0.0.13:9100"]
Restart Prometheus to pick up the change:
sudo systemctl restart prometheus
Back in the Prometheus web UI under Status → Targets, you should now see two entries — prometheus and node — both reporting state UP. If node shows DOWN, double-check the target's firewall allows inbound traffic on port 9100 from the Prometheus host specifically.
prometheus
UP
DOWN
Grafana is the layer that turns raw time-series numbers into something a human actually wants to look at.
sudo apt-get install -y apt-transport-https software-properties-common wget gnupg2 wget -q -O - https://packages.grafana.com/gpg.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/grafana.gpg > /dev/null echo "deb [signed-by=/etc/apt/trusted.gpg.d/grafana.gpg] https://packages.grafana.com/oss/deb stable main" | sudo tee /etc/apt/sources.list.d/grafana.list sudo apt-get update sudo apt-get install -y grafana sudo systemctl daemon-reload sudo systemctl enable --now grafana-server
Visit http://your-server-ip:3000. The default login is username admin, password admin — you'll be prompted to set a new password immediately, and you should, since this dashboard will be looking at fairly sensitive operational data about your infrastructure.
http://your-server-ip:3000
admin
Inside Grafana:
http://localhost:9090
http://<prometheus-ip>:9090
Building dashboard panels from scratch works, but for general system monitoring, the community-maintained Node Exporter Full dashboard covers CPU, memory, disk, network, and load averages out of the box.
You should immediately see live graphs populate — CPU usage by core, memory breakdown (used, cached, available), disk space per mount point, network throughput per interface. This single dashboard is usually enough to catch the slow-burn problems a simple uptime check would miss entirely.
A dashboard nobody's watching at 3 AM doesn't help when disk usage crosses a dangerous threshold overnight. Grafana's built-in alerting can notify you instead of requiring someone to be looking.
In Grafana, go to Alerting → Alert rules → New alert rule. A practical starting alert — disk usage climbing past a safe margin — uses a query against the node_filesystem_avail_bytes and node_filesystem_size_bytes metrics that Node Exporter already exposes. Configure a contact point (email, Slack webhook, or similar) under Alerting → Contact points so the rule actually reaches you when it fires.
node_filesystem_avail_bytes
node_filesystem_size_bytes
The setup above is functional but wide open — Prometheus and Grafana both default to no authentication on Prometheus's own UI and a default admin password on Grafana's. Before exposing this beyond your own management network:
Restrict ports 9090 and 9100 to internal/management traffic only via your firewall (ufw or iptables) — these don't need to be reachable from the public internet at all; only Grafana needs a path to Prometheus, and that can happen entirely over a private network.
ufw
iptables
Put Grafana behind a reverse proxy with TLS (Nginx or Caddy) rather than exposing port 3000 directly, so login traffic and dashboard data aren't sent in plaintext.
Change Grafana's default admin credentials immediately — this was already prompted in Step 5, but it's worth confirming explicitly.
Prometheus collects and stores the metrics; Grafana visualizes them. Prometheus does have a basic built-in graphing interface, usable in a pinch, but it's not designed for building the kind of multi-panel, shareable dashboards that Grafana specializes in. Running Prometheus alone gets you data; pairing it with Grafana is what makes that data actually legible at a glance.
The pull model means Prometheus itself controls the scrape schedule and immediately knows if a target stops responding — a target going silent is itself a signal worth alerting on. It also keeps configuration centralized: adding a new server to monitor means updating one config file on the Prometheus server, not deploying new push configuration to every monitored machine.
It depends on how many metrics you're scraping and at what interval, but a single node's system-level metrics (CPU, memory, disk, network) at a default 15-second scrape interval typically uses a modest amount of disk per day. Usage scales with the number of exporters and targets being scraped. Prometheus's default retention period and `--storage.tsdb.retention.time` flag control how long old data is kept before being purged.
Yes — that's the standard pattern. Install Node Exporter on every server you want visibility into, then add each one's IP and port 9100 as a target under the same `job_name: "node"` block in `prometheus.yml`. One central Prometheus and Grafana pair can comfortably watch a small fleet of bare-metal servers this way.
It depends on what you're optimizing for. Self-hosting keeps your operational metrics entirely under your own control, with no recurring per-host or per-metric billing from a third party. The trade-off is that you're now responsible for keeping the monitoring stack itself running and scaled.
At eServers , we proudly partner with 15+ leading global tech providers to deliver secure, high-performance hosting solutions. These trusted alliances with top hardware, software, and network innovators ensure our clients benefit from modern technology and enterprise-grade reliability.