Prometheus Error Guide: 'target_limit exceeded' — Cap or Filter Discovered Targets
Fix Prometheus 'target_limit exceeded': see why a scrape pool blew past target_limit, filter service discovery with relabeling, right-size the cap, and recover down targets.
- #prometheus
- #monitoring
- #troubleshooting
- #errors
Stuck on this Prometheus & Monitoring error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
A scrape job that has target_limit set will refuse to scrape once service discovery hands it more targets than the limit allows. Prometheus logs the rejection and marks the whole scrape pool as failing:
ts=2026-07-06T09:12:44.183Z caller=scrape.go:604 level=error component="scrape manager" scrape_pool=kubernetes-pods msg="target_limit exceeded (number of targets: 512, limit: 200)"
On /api/v1/targets the affected targets carry the same reason in lastError:
"health": "down",
"lastError": "target_limit exceeded (number of targets: 512, limit: 200)",
"scrapePool": "kubernetes-pods"
target_limit is a per-scrape-config cap on the number of active targets (post-relabeling) a job may have. When discovery produces more than the limit, Prometheus rejects the entire target set for that pool rather than scraping a partial subset — so a pool that was healthy can go fully dark after a scale-up.
Symptoms
- A previously green scrape pool goes
downall at once after a deployment, node scale-up, or SD change. - The Prometheus UI Targets page shows the pool with a red
target_limit exceeded (number of targets: N, limit: M)error. upfor the whole job drops to0; no partial subset keeps reporting.- Alerts that depend on those series fire
no data/absent()even though the exporters are healthy. - It correlates in time with autoscaling, a new namespace, or a relabeling change that widened what got kept.
Common Root Causes
- Legitimate growth past a stale cap — the fleet grew (more pods, more EC2 instances) and
target_limitwas set once and never revisited. - Over-broad service discovery — a Kubernetes/EC2/Consul SD that discovers far more than intended because
relabel_configskeep too much. - A missing or wrong
keep/droprelabel rule — a rule regression stops dropping targets that used to be filtered out, doubling or tripling the active set. - Label churn creating duplicate targets — relabeling that fails to dedupe (e.g. every container port becomes its own target) inflates the count.
- A cap copied from a smaller environment — the same job YAML promoted from staging (50 targets) into production (thousands) without resizing the limit.
Diagnostic Workflow
First, confirm the failing pool and the exact numbers from the error:
curl -s http://localhost:9090/api/v1/targets \
| jq -r '.data.activeTargets[] | select(.lastError|test("target_limit")) | [.scrapePool,.health,.lastError] | @tsv' \
| sort -u
kubernetes-pods down target_limit exceeded (number of targets: 512, limit: 200)
Count how many targets discovery is actually producing for that pool (active + dropped) so you can see whether relabeling is filtering as intended:
curl -s 'http://localhost:9090/api/v1/targets?scrapePool=kubernetes-pods' \
| jq '.data.activeTargets | length, (.data.droppedTargets | length)'
Read the configured limit straight from the running config:
curl -s http://localhost:9090/api/v1/status/config | jq -r '.data.yaml' \
| grep -A15 'job_name: kubernetes-pods' | grep -E 'target_limit|action|regex'
Watch scrape-pool sizing and reloads over time in PromQL:
prometheus_sd_discovered_targets{config="kubernetes-pods"}
count by (job) (up{job="kubernetes-pods"})
Before changing the config, dry-run your relabel rules against real target labels so you can see what should be kept versus dropped:
promtool check config /etc/prometheus/prometheus.yml
Example Root Cause Analysis
A platform team ran kubernetes-pods with target_limit: 200, copied from a staging cluster. After a product launch, the namespace scaled from ~150 to ~500 pods and the whole pool went red with target_limit exceeded (number of targets: 512, limit: 200).
Checking droppedTargets showed only 40 dropped — meaning discovery was keeping almost everything it found. The relabel_configs had a keep rule on __meta_kubernetes_pod_annotation_prometheus_io_scrape=true, but a recent Helm change had added that annotation to a large DaemonSet, so hundreds of extra pods were now legitimately in scope.
Two fixes applied together: (1) the cap was raised to a realistic 600 with headroom over the real count, and (2) a drop rule was added to exclude the DaemonSet’s sidecar port, which had been double-counting. After promtool check config passed and a reload, the active set fell to 480, well under the new limit, and the pool returned to up.
The key lesson: target_limit did its job as a tripwire against runaway discovery, but the durable fix was tightening relabeling, not just raising the number.
Prevention Best Practices
- Set
target_limiton every SD-driven job as a tripwire, sized with real headroom (e.g. 1.5–2x current active targets), and revisit it whenever the environment scales. - Filter at the discovery layer first: use Kubernetes SD
selectors, EC2 filters, or tightkeep/droprelabel_configsso you don’t discover-then-reject thousands of targets. - Alert on approach, not just breach: watch
prometheus_sd_discovered_targetstrending toward the configured limit. - Keep environment-specific limits — never promote a staging cap into production unchanged.
- Pair
target_limitwithkeep_dropped_targetsso a churny SD source can’t also balloon dropped-target memory. - Review relabel changes in code review specifically for their effect on how many targets get kept.
Quick Command Reference
# Which pools are failing on target_limit, with the numbers
curl -s http://localhost:9090/api/v1/targets \
| jq -r '.data.activeTargets[] | select(.lastError|test("target_limit")) | [.scrapePool,.lastError] | @tsv' | sort -u
# Active vs dropped target counts for a pool
curl -s 'http://localhost:9090/api/v1/targets?scrapePool=kubernetes-pods' \
| jq '{active: (.data.activeTargets|length), dropped: (.data.droppedTargets|length)}'
# Configured target_limit + relabel rules for the job
curl -s http://localhost:9090/api/v1/status/config | jq -r '.data.yaml' \
| grep -A15 'job_name: kubernetes-pods'
# Validate config and reload after editing
promtool check config /etc/prometheus/prometheus.yml \
&& curl -s -XPOST http://localhost:9090/-/reload
# Discovered-target trend
# (PromQL) prometheus_sd_discovered_targets{config="kubernetes-pods"}
Conclusion
target_limit exceeded is Prometheus protecting a server from a scrape pool that suddenly discovered far more targets than expected. Treat it as a signal, not just a number to raise: confirm the real active-target count, decide whether the growth is legitimate, and prefer tightening service discovery and relabeling over blindly bumping the cap. Right-size the limit with headroom, filter at the SD layer, and pair it with keep_dropped_targets so both scraped and dropped targets stay bounded. Done that way, the same guardrail that took the pool down becomes the early warning that keeps your TSDB and memory footprint predictable as the fleet grows.
Fixed it? Get 500 Prometheus & Monitoring & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.