Exporters
Almost nothing you run speaks Prometheus natively. Linux reports state through /proc, PostgreSQL through the pg_stat views, Redis through the INFO command — three systems, three protocols, none of them the text format from topic 11. An exporter is the translator: a small process that sits next to one system, queries it in its own protocol when a scrape arrives, and republishes the answers as /metrics text.
This topic wires up the exporters that turn Harborline's hosts and stores from dark boxes into scrape targets: node_exporter on all six machines, postgres_exporter on db-01, redis_exporter on cache-01 — and mq-01, which turns out to need no exporter at all. The five services stay dark; instrumenting code is Chapter 5's job. Hosts and stores first.
The Sidecar-Translator Pattern
The pattern is one exporter per instance, running next to the thing it translates. When Prometheus scrapes node_exporter, the exporter reads /proc and /sys at that moment; scrape postgres_exporter and it runs queries against the pg_stat views; scrape redis_exporter and it sends INFO to Redis. The exporter gathers on demand and renders the result as text. Nothing happens between scrapes.
Exporters hold no history and no state — they are stateless protocol adapters. Restart one and you lose nothing, because there is nothing in it to lose: history lives in the TSDB on obs-01, current state lives in the system being translated. That statelessness is also why the deployment rule is strict. The exporter runs where its system runs, so the instance label on the translated series tells the truth about which machine the numbers describe.
node_exporter
node_exporter is the one exporter every host gets. It reads CPU, memory, filesystem, disk I/O, and network state from /proc and /sys and serves it on :9100 — roughly a thousand series per host with the default collectors — more or less depending on how many disks, filesystems, and network interfaces the box has. It lands on all six Harborline machines and becomes the data source for every "is the box healthy" question Chapter 4 asks. Which collectors run is a per-flag decision (--collector.<name>), and that flag list is where a host's series budget gets set.
blackbox_exporter
blackbox_exporter is the outside view. Instead of exposing a system's internals, it probes — an HTTP GET, a TCP connect, an ICMP ping, a DNS lookup — and reports probe_success, probe_duration_seconds, and per-phase timings for whatever address you aim it at. Mara points it at https://harborline.example, and for the first time there is a number for "can a customer reach us," measured from outside the machines that serve the answer.
The exporter has one structural oddity: the thing to probe is not a scrape target in the usual sense — it arrives as a URL parameter on the scrape request. Configure it naively and Prometheus monitors the exporter process while zero probes run. Making it work takes a three-line relabeling trick, and that trick is exactly where topic 14 begins.
The Store Exporters
postgres_exporter lands on db-01 and serves :9187, translating pg_stat_database and its siblings into connection counts, transaction rates, and cache-hit ratios. redis_exporter lands on cache-01 on :9121: memory usage, hit and miss counts, connected clients. File those last series away — connected clients on cache-01 is the sort of unglamorous number that earns its keep later.
# prometheus.yml — the stores join the target list
- job_name: "postgres"
static_configs:
- targets: ["db-01:9187"]
- job_name: "redis"
static_configs:
- targets: ["cache-01:9121"]
- job_name: "rabbitmq"
static_configs:
- targets: ["mq-01:15692"]
The three new jobs point at the two exporters and at mq-01 directly, because the third store needs no exporter at all. RabbitMQ ships a rabbitmq_prometheus plugin; one rabbitmq-plugins enable later, the broker serves native /metrics on :15692. This is the increasingly common case — modern infrastructure software grows its own Prometheus endpoint, and the translator layer thins out one system at a time.
Scrape-Time Cost
Because exporters gather on demand, an expensive collection becomes a slow scrape. A postgres_exporter custom query that takes 12 seconds against a loaded database blows straight through the default 10-second scrape_timeout, and the result is not slow data but no data — the scrape fails, up drops to 0, and every metric from that exporter vanishes for the interval. Exporter latency is part of the scrape budget; heavy custom queries belong in a separate job with a longer interval, or nowhere.
Buy Before Build
The exporter ecosystem covers hundreds of systems: databases, message queues, network gear, JVMs, cloud provider APIs. Writing your own is a Chapter 5 skill and sometimes the right call for in-house software, but for anything off the shelf the first move is checking whether the exporter already exists. It almost always does, and twenty minutes evaluating one beats a month of maintaining a homegrown translator.
- Alerting on
upfor an exporter target and believing it covers the database —up{job="postgres"}says the exporter answered, and it answers happily while PostgreSQL is down. The exporter publishes its own connectivity metric (pg_up,redis_up), and that is the one that means what you thinkupmeans. - Running node_exporter as a plain container — it reads the container's
/procand reports the namespace's view, not the host's. Run it as a native systemd service, or as a container with host PID and network plus the root filesystem mounted and--path.rootfspointed at it. - Scraping blackbox_exporter like a normal target — a bare scrape of :9115/metrics monitors the exporter process itself, and every probe you thought you configured silently doesn't run. The probed target must be injected via the
__param_targetrelabel dance (topic 14). - Enabling every node_exporter collector "for completeness" — optional collectors like
systemdandperfmultiply series count and scrape time per host. Six hosts of unneeded collectors is thousands of junk series before a single service is instrumented. - Leaving exporter ports open beyond the monitoring network — store exporter metrics leak internals (database names, connection counts, queue names), and the exporter itself holds real credentials to the store it queries. Firewall :9100, :9187, :9121, and :15692 to
obs-01.
- Deploy exporters next to what they translate — same host, one exporter per instance — so the
instancelabel tells the truth and a host's failure takes down exactly its own metrics. - Alert on the exporter's own reachability metric (
pg_up == 0,redis_up == 0) alongsideup, so exporter-dead and store-dead arrive as distinguishable pages. - Enable node_exporter collectors deliberately with
--collector.<name>flags instead of accepting whatever a distro package turned on — series count per host is a budget, and this is where it's set. - Prefer native
/metricsendpoints when the software offers one, as RabbitMQ's plugin does — one process fewer to run, and no translation lag.
Knowledge Check
PostgreSQL on db-01 is down, yet up{job="postgres"} reads 1. Why?
upis cached from the last successful scrapeupcovers the exporter;pg_upcovers the database- The exporter is replaying buffered samples from before the outage
- The scrape hit PostgreSQL's own port and misparsed the response
Prometheus scrapes redis_exporter at 09:00:15. Where does the returned value come from?
- A cache inside the exporter, refreshed once a minute
- The exporter's write-ahead log
- The exporter runs
INFOagainst Redis at that moment - An extrapolation from the previous scrape's value
What can blackbox_exporter tell Mara that node_exporter structurally cannot?
- How much CPU time each core spent in iowait, broken out per mode from the host's
/proc - Whether a request from outside actually reaches
harborline.example - Redis's cache hit ratio on
cache-01 - Disk space remaining on
app-01's root volume
A postgres_exporter custom query takes 12 s against a loaded database; scrape_timeout is the default 10 s. What does Prometheus store?
- The metrics that were ready before the timeout hit
- The full result, attributed to the next interval
- Everything — the timeout extends automatically under load
- Nothing — the scrape fails and
uprecords 0
You got correct