Prometheus for One Server
06 Oct 2026Prometheus for a single server doesn’t need service discovery, remote write, or a week of tuning. It needs one scrape config and three alerts. Everything else is for when you have a fleet, not a box.
Install Prometheus and node_exporter
useradd --no-create-home --shell /usr/sbin/nologin node_exporter
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 mv node_exporter-1.8.2.linux-amd64/node_exporter /usr/local/bin/
Run it as a systemd service so it survives reboots:
# /etc/systemd/system/node_exporter.service
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=node_exporter
ExecStart=/usr/local/bin/node_exporter
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now node_exporter
It listens on :9100 and exposes CPU, memory, disk, and network metrics — no config needed for the basics.
Install Prometheus itself the same way (download the binary, run as a systemd service, listens on :9090).
The minimum scrape config
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: node
static_configs:
- targets: ['localhost:9100']
- job_name: prometheus
static_configs:
- targets: ['localhost:9090']
That’s a working setup for one server. No file_sd, no Consul, no Kubernetes service discovery — static_configs with a hardcoded target list is correct for a single box, not a shortcut you’ll regret.
Reload after editing:
curl -X POST http://localhost:9090/-/reload
The three alerts worth having
Most alert rule examples online are copy-pasted from a 50-service fleet and don’t fit a single-server setup. For one box, three cover the failure modes that actually happen:
# alert.rules.yml
groups:
- name: node
rules:
- alert: DiskSpaceLow
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) < 0.1
for: 10m
labels:
severity: critical
annotations:
summary: "Disk under 10% free"
- alert: HighLoad
expr: node_load5 / count(node_cpu_seconds_total{mode="idle"}) without (cpu, mode) > 1.5
for: 10m
labels:
severity: warning
annotations:
summary: "5-minute load average is 1.5x core count"
- alert: InstanceDown
expr: up == 0
for: 2m
labels:
severity: critical
annotations:
summary: "{{ $labels.job }} is not reachable"
DiskSpaceLow catches the single most common self-hosted-server failure: logs or a database filling the disk until everything stops writing. HighLoad catches sustained overload before it becomes an outage. InstanceDown catches the exporter (or the box) going away entirely.
Reference the rule file from prometheus.yml:
rule_files:
- "alert.rules.yml"
Where alerts actually go
Prometheus evaluates rules but doesn’t notify anyone by itself — that’s Alertmanager’s job. For one server, running a second service just to route a Slack message is arguably more than you need; a lightweight alternative is a small script polling Prometheus’s /api/v1/alerts endpoint and posting to a webhook. If you already run Alertmanager for other reasons, use it. If this is your only box, don’t add a process just to send three alerts.
What to skip until you have more than one server
Don’t add remote_write to a long-term storage backend, don’t set up federation, and don’t reach for Thanos or Mimir. Local disk retention (Prometheus defaults to 15 days) is plenty until you’re running enough servers that cross-host queries matter more than local ones.