Configuration — prometheus.yml
Everything Prometheus does is declared in one YAML file. prometheus.yml holds the global defaults, the list of scrape jobs, and pointers to rule files and Alertmanager — change what Prometheus monitors and this file is what you change. There is no database of checks and no UI wizard; the config is text, which means it diffs, reviews, and versions like any other code.
Mara's first version is a dozen lines: one job, six statically listed targets. It takes Harborline from zero telemetry to six hosts reporting every 15 seconds, and the two labels it establishes — job and instance — are how you will identify every series for the rest of the book.
File Anatomy
Four top-level blocks. global sets defaults every job inherits: scrape_interval, scrape_timeout, evaluation_interval, and the external_labels that stamp every sample leaving this server. scrape_configs is the heart of the file — the list of jobs. rule_files and alerting point at recording rules (Chapter 4) and Alertmanager (Chapter 9); both stay empty this chapter. A minimal working config is global plus one entry in scrape_configs.
A scrape_config, Line by Line
job_name becomes the job label on every series the job produces — Harborline names jobs after the service or exporter, job="node", job="postgres". metrics_path defaults to /metrics and scheme defaults to http, so most jobs set neither. static_configs lists targets as host:port strings, and each target's address becomes its instance label. Between them, job and instance pin down where any sample came from.
Intervals and Timeouts
The shipped default scrape_interval is 1 minute with a 10-second scrape_timeout; 15 seconds is the common production interval and what Harborline sets in global. The timeout must be shorter than the interval — Prometheus rejects the config otherwise. And 200 targets do not get hit in the same millisecond: Prometheus deliberately staggers each target's scrape start across the interval, spreading load on both the network and itself.
Mara's First Scrape
One job, six hosts, and the env="prod" label attached once in the config instead of once per host. This is the entire file that gives Harborline its first telemetry:
global:
scrape_interval: 15s
scrape_configs:
- job_name: "node"
static_configs:
- targets: ["app-01:9100", "app-02:9100", "db-01:9100",
"cache-01:9100", "mq-01:9100", "obs-01:9100"]
labels:
env: "prod"
Sixty seconds after the reload, up{job="node"} returns six series with value 1 and the Targets page shows six green rows. That is Harborline's first real data: CPU, memory, disk, and network for every host, every 15 seconds. Nothing about the five services yet — they stay dark until Chapter 5.
Validation and Reload
promtool check config prometheus.yml parses the file and catches both syntax and semantic errors — an unknown field, a timeout longer than the interval — before anything reaches the server. The running server picks up changes via SIGHUP or, with --web.enable-lifecycle set, an HTTP endpoint:
promtool check config prometheus.yml curl -X POST http://obs-01:9090/-/reload
The difference between reload and restart matters most when the file is broken. A reload validates the new file and, if it is bad, rejects it while the old config keeps serving. A restart parses the broken file at startup and exits — the same typo a reload shrugs off becomes a monitoring outage.
The Limits of static_configs
A hardcoded list is the right call for six long-lived VMs, and exactly wrong for fleets where targets churn — autoscaling groups, containers, anything with a lifespan shorter than a config review. Topic 14 replaces the static list with service discovery. Everything else in this file — intervals, labels, the job structure — carries over unchanged.
- Editing prometheus.yml and forgetting to reload — the server runs the old config for weeks. The Configuration page in the UI shows what is actually loaded, and it is the first thing to check when a "configured" target refuses to appear.
- Fixing a config typo with a restart instead of a reload — reload rejects the broken file and keeps the old config serving; restart parses the broken file at startup and exits, converting a typo into an outage.
- Setting
scrape_interval: 5sfleet-wide for better resolution — 12× the samples of a 1-minute interval on every series, with matching disk and memory cost, for resolution almost no alert or dashboard needs. Reserve sub-15 s intervals for the few jobs that justify them. - Mixing many scrape intervals across jobs —
rate()windows (Chapter 4) must cover several sample intervals, so a fleet running 5 s, 15 s, and 2 m jobs needs different query windows per job, and someone always gets one wrong. Standardize on one interval, two at most. - Pointing a target at the application's own port instead of its exporter — the scrape gets HTML or a connection reset,
upgoes to 0, and the Targets-page error ("unsupported Content-Type" or a parse failure) is the tell that you scraped the wrong port.
- Run
promtool check configin CI on every change to prometheus.yml, so a broken config is a failed pipeline instead of a failed reload at 02:00. - Enable
--web.enable-lifecycleand makePOST /-/reloadthe only way config reaches the running server — reloads are validated, restarts are not. - Set
external_labels(at minimumenv, laterreplica) inglobalfrom the first config — every sample that leaves this server via remote_write or federation needs them, and historical data cannot be retro-labeled. - Attach shared target labels like
env: prodin the scrape config, not by asking every exporter to add them — one line in one file instead of a flag on six hosts.
Knowledge Check
A series carries job="node", instance="db-01:9100". Where did each label come from?
- node_exporter set both when it rendered the response
jobfrom the job_name,instancefrom the target address- Both were stamped on by external_labels in the global block
instancefrom the job_name,jobfrom the exporter's response
The new prometheus.yml contains a typo. What happens on reload versus restart?
- Both fall back to the last good config automatically
- Reload crashes the server; restart safely ignores the bad file and keeps scraping
- Both suspend all scrapes until the file is fixed
- Reload rejects it and keeps serving; restart exits and leaves nothing running
What does dropping the scrape interval from 1 m to 5 s fleet-wide actually cost?
- 12× the samples, disk, and memory, for resolution few alerts need
- Less CPU, because each individual scrape gets smaller
- Nothing — retention time, not the interval, controls disk use
- Faster queries, since data points sit closer together
Why standardize on one or two scrape intervals across the fleet?
- Prometheus rejects configs with more than two distinct intervals
rate()windows must fit the interval, and mixed fleets force per-job math- Targets fail their scrapes when neighboring jobs use different intervals, and stay down until aligned
- Mixed intervals corrupt the WAL during replay
You got correct