After installing exporters on the machines you want to monitor, the next step is to collect their measurements in one place. Prometheus requests those measurements, stores their history and lets you query them. This guide connects the exporter stage of our monitoring series to the Grafana dashboard stage, with checks that show whether each part is actually working.
What Is Prometheus?
Prometheus is an open-source monitoring server and time-series database. A time series combines a metric name, labels identifying what was measured, and samples recorded over time. For example, CPU measurements can carry labels for a machine, processor and operating mode. That makes it possible to compare machines without mixing their results together.
In the usual setup, the server pulls metrics from HTTP endpoints; exporters do not automatically report to a central gateway. Prometheus also exposes its own metrics. Start with that self-monitoring target so you can distinguish a working server from a problem reaching a remote exporter. The official getting-started guide describes this collection flow.
Exporters, Prometheus and Grafana Have Different Jobs
Linux node_exporter normally exposes host metrics on port 9100; Windows windows_exporter commonly uses 9182. Prometheus normally provides its query interface on 9090. Grafana connects to that query interface, rather than treating an exporter's raw metrics page as a database.
Use the exporter appropriate to each operating system. A working Windows exporter will not suddenly provide the Linux node_* metric names used in the CPU example below. See node_exporter's supported collectors and the Windows exporter project when choosing collectors and dashboards.
Prerequisites and Network Plan
Choose one installation method: a Windows foreground trial, a fresh Debian/Ubuntu systemd installation, or Docker. Do not start two Prometheus instances on the same port or let two processes write to the same database directory. The Linux service instructions assume sudo access, systemd and an x86-64 machine. An ARM Raspberry Pi needs the corresponding ARM archive instead.
The example Linux host is 192.168.1.10:9100 and the Windows host is 192.168.1.50:9182. Replace them with addresses from your network, preferably DHCP reservations or stable DNS names. Confirm the exporters are running before adding them. Allow exporter access from the monitoring server through your host firewall; avoid opening these ports to the internet.
Create a Small, Understandable Configuration
Save this as prometheus.yml in the location specified for your chosen method. Remove either remote job if you do not have that type of host. YAML indentation matters: use spaces and keep each job beneath the single scrape_configs key.
global:
scrape_interval: 30s
evaluation_interval: 30s
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ['localhost:9090']
- job_name: linux
static_configs:
- targets: ['192.168.1.10:9100']
- job_name: windows
static_configs:
- targets: ['192.168.1.50:9182']
Thirty seconds is a deliberate starting interval, not a universal default or a guarantee that every incident will be captured. Shorter intervals create more samples and more exporter requests. Keep a copy of the last working file before changing jobs, and validate the new file before restarting. The configuration reference explains job settings and reload behavior.
Installation on Windows
Choose a Windows archive from the official download page, compare its checksum, and extract it. Put prometheus.exe, promtool.exe and your configuration together in C:\Prometheus. From PowerShell, run:
Set-Location C:\Prometheus
.\promtool.exe check config .\prometheus.yml
.\prometheus.exe --config.file=prometheus.yml --storage.tsdb.path=data --web.listen-address=127.0.0.1:9090
Continue only if validation succeeds. Open http://127.0.0.1:9090 on that Windows machine. This foreground trial ends when you stop the process or close its terminal; it does not register a Windows service. Keep the data directory if you want to preserve collected history. A permanent installation needs a separately configured service or another supervised deployment method.
Installation on Debian or Ubuntu with systemd
This sequence is for a fresh installation. For an existing service, inspect its account, paths and unit first, back up its configuration, and follow the release upgrade notes. Download the linux-amd64 archive into a dedicated working folder. The example pins 3.13.4, a release listed upstream when this guide was checked; compare the checksum and supported releases before a future installation.
mkdir -p prometheus-install
cd prometheus-install
curl -fLO https://github.com/prometheus/prometheus/releases/download/v3.13.4/prometheus-3.13.4.linux-amd64.tar.gz
printf '%s %s\n' '87f21a66f96c597a189cef8d640e8921b621fc17a9feff707c332c4f3b3ddd56' 'prometheus-3.13.4.linux-amd64.tar.gz' | sha256sum -c -
tar -xzf prometheus-3.13.4.linux-amd64.tar.gz
sudo useradd --system --user-group --no-create-home --shell /usr/sbin/nologin prometheus
sudo install -m 0755 prometheus-3.13.4.linux-amd64/prometheus /usr/local/bin/prometheus
sudo install -m 0755 prometheus-3.13.4.linux-amd64/promtool /usr/local/bin/promtool
sudo install -d -m 0755 /etc/prometheus
sudo install -d -o prometheus -g prometheus -m 0750 /var/lib/prometheus
sudo nano /etc/prometheus/prometheus.yml
The checksum command must report OK; stop if the download or verification fails. Paste the configuration above into the editor. The dedicated account needs read access to configuration and write access to its data directory, without owning the executable. If curl or nano is unavailable, install it through your distribution's package manager first.
Save the following unit as /etc/systemd/system/prometheus.service. The explicit paths remove any dependence on the directory from which you last ran a command.
[Unit]
Description=Prometheus monitoring server
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
WorkingDirectory=/etc/prometheus
ExecStart=/usr/local/bin/prometheus --config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/var/lib/prometheus --storage.tsdb.retention.time=15d --web.listen-address=127.0.0.1:9090
Restart=on-failure
[Install]
WantedBy=multi-user.target
Validate as the service account, then enable the unit:
sudo -u prometheus /usr/local/bin/promtool check config /etc/prometheus/prometheus.yml
sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
sudo systemctl status prometheus --no-pager
sudo journalctl -u prometheus -n 50 --no-pager
curl -fsS http://127.0.0.1:9090/-/ready
A successful readiness response checks the server, while target health checks the exporters. For a remote browser, an SSH tunnel keeps the query interface private: run ssh -L 9090:127.0.0.1:9090 monitoring-host from your workstation, replacing monitoring-host with its actual SSH host name. Then browse your workstation's localhost port. Grafana on the same host can use localhost too; a separate host requires an intentionally configured private listener and firewall policy.
Docker as an Alternative
With Docker installed, create the configuration in your current directory before starting the container. This Linux-shell example binds the browser port to localhost and stores history in a named volume. The file bind uses --mount, which fails if the source file is missing instead of silently creating a directory there.
docker run -d --name prometheus --restart unless-stopped \
-p 127.0.0.1:9090:9090 \
--mount type=bind,source="$(pwd)/prometheus.yml",target=/etc/prometheus/prometheus.yml,readonly \
--mount type=volume,source=prometheus_data,target=/prometheus \
prom/prometheus:v3.13.4
Inside this container, localhost means the container itself. The self-monitoring job is correct, but a host exporter needs its reachable host address. Do not bind Prometheus itself to container loopback when other containers must connect to it. For both services on one Docker network, use the complete stack in our Grafana with Prometheus guide.
Verify Collection Before Building Dashboards
Open /targets and inspect each configured endpoint's last scrape and error message. Query up: a value of one means the latest scrape succeeded; zero means it failed. Neither proves that an entire application is healthy. A job removed from configuration can disappear from results rather than remain as a permanent zero.
| Symptom | Next check |
|---|---|
| Self target fails | Compare the configured port with the listener and inspect server logs. |
| Connection refused | Check the exporter process, its listening address and its configured port. |
| Timeout | Check the route and firewall from the Prometheus machine, then exporter response time. |
| Authentication or certificate error | Match the scrape job's scheme, credentials and trusted certificate to the endpoint. |
| Target is up but a metric is absent | Inspect its raw metrics and enabled collectors; verify the metric belongs to that operating system. |
For Linux hosts, this query calculates the average non-idle CPU percentage across cores over five minutes:
100 * (1 - avg by (instance) (rate(node_cpu_seconds_total{job="linux",mode="idle"}[5m])))
Wait for several scrapes before expecting a rate. Keep the instance label in the result so machines remain separate, and compare the result with a host tool over a similar time window. This is an average across cores, not a total that should exceed one hundred percent. See the official host-metrics guide for the counter and rate model.
Make Alerts Reach a Receiver
Prometheus evaluates alert rules; Alertmanager handles routing and notifications. A rule appearing as firing in Prometheus is only one checkpoint. You must also configure an Alertmanager address, a receiver, and a successful delivery test.
For the Linux service above, save this rule as /etc/prometheus/alerts.yml, readable by the service account:
groups:
- name: exporter-health
rules:
- alert: ExporterScrapeFailed
expr: up{job=~"linux|windows"} == 0
for: 2m
labels:
severity: warning
annotations:
summary: 'Exporter scrape failed: {{ $labels.instance }}'
Add these top-level keys to the existing prometheus.yml, alongside global and scrape_configs, replacing the example address with a running Alertmanager:
rule_files:
- alerts.yml
alerting:
alertmanagers:
- static_configs:
- targets: ['192.168.1.20:9093']
Validate the complete configuration again and restart the service. The rule stays pending until failure persists for two minutes; evaluation and notification routing can add delay. Test with a disposable exporter, then confirm recovery and the received message. This example detects failed configured scrapes, not every disappeared target. Windows needs the rule in its configuration folder; Docker needs an additional read-only bind for /etc/prometheus/alerts.yml. Follow the alert-rule reference and Alertmanager receiver configuration for the complete notification setup.
Storage, Security and Recovery
The Linux unit explicitly retains fifteen days. More hosts, labels or frequent scrapes increase storage requirements; there is no useful universal disk size for every homelab. Monitor the database directory and leave free-space headroom. A restart should retain history because the data path stays fixed. Deleting that directory or the Docker volume removes the locally stored samples.
Back up configuration, rules and the database using a consistent method. Upstream recommends TSDB snapshots; if you use a stopped-service backup, verify the copy and a restore separately. Do not put the active TSDB on NFS: Prometheus documents filesystem limitations and backup guidance. Grafana dashboards are a separate backup concern.
Prometheus supports native Basic authentication and TLS through a web configuration file; the old claim that it has no authentication support is incorrect. Loopback binding is a simple local starting point, not an entire remote-access design. Before exposing it beyond the host, configure private access or an authenticated TLS endpoint using the official HTTPS and authentication instructions. Recheck exporter firewall rules as well.
Conclusion
A useful monitoring server has more than a reachable homepage: it has successful scrapes, understandable queries, persistent history and a tested notification path. Record the actual target addresses and working configuration, then connect Grafana through the next article in this series. That gives you a baseline you can diagnose when a dashboard later looks empty or a machine stops reporting.



