Skip to main content

Jenkins

Jenkins exposes Prometheus text at /prometheus/ (trailing slash) on the web port :8080 when the Prometheus Metrics plugin is installed. The Collector's prometheus receiver scrapes it, collecting 350+ metrics across build results, executor usage, queue depth, node / agent status, plugin health, HTTP request handling, and the JVM. This guide installs the plugin, configures the receiver, and ships metrics to base14 Scout.

Prerequisites

RequirementMinimumRecommended
Jenkins2.479.3LTS (latest)
Prometheus Metrics pluginCurrentlatest
OTel Collector Contrib0.90.00.153.0
base14 ScoutAny-

Before starting:

  • The Jenkins web port (8080) must be reachable from the host running the Collector.
  • The Prometheus Metrics plugin must be installed - Jenkins exposes no metrics without it. The plugin uses Jenkins date-based versioning (for example 852.v…) rather than semantic versions; a current release requires Jenkins 2.479.3 or later.
  • A Scout account and OTLP endpoint.
  • OTel Collector installed - see Docker Compose Setup.

What You'll Monitor

Metrics are grouped into three tiers by how you use them. Scrape Core always, alert on Operational, and reach for Diagnostic during an incident or capacity review. Every row traces to a metric Jenkins actually emits on this endpoint.

A few things about this surface shape what you see:

  • Two liveness signals. up is the prometheus receiver's scrape liveness - it reads 1 when /prometheus/ responds. default_jenkins_up is the plugin's own gauge - 1 means the Jenkins controller is up. default_jenkins_uptime is controller uptime in milliseconds; a reset flags a restart.
  • Collection-period gotcha. The Prometheus plugin collects metrics on a timer (COLLECTING_METRICS_PERIOD_IN_SECONDS, default 120s) and serves the last cached snapshot. Immediately after start, /prometheus/ returns 200 with an empty body until the first collection completes, so early scrapes carry only up and the scrape_* meta. This is expected, not a failure.
  • Two Jenkins namespaces share the endpoint. default_jenkins_* is the Prometheus plugin's curated export - per-job build results (default_jenkins_builds_*, labeled by jenkins_job) plus controller-wide executors, up, uptime, and version. jenkins_* is the underlying metrics (Dropwizard) plugin re-exposed - executor, queue, node, project, job, task, health-check, plugins, and run-result families, most as a _value (current) and a _history (server-windowed) pair.
  • _history series are pre-windowed. The plugin computes rolling windows server-side, so system_cpu_load_x100_window_{1m,5m,15m,1h} and the *_history variants carry aggregates the receiver did not compute; the _value series is the instantaneous read.
  • Run-result counters are the CI-health signal. jenkins_runs_success_total, jenkins_runs_failure_total, jenkins_runs_unstable_total, jenkins_runs_aborted_total, and jenkins_runs_not_built_total are the controller-wide build outcomes; default_jenkins_builds_last_build_result_ordinal is the per-job last result (0 = success ... 4 = not built).
  • Two parallel JVM exports. vm_* is the Dropwizard JVM instrumentation (vm_memory_* is the largest family on this endpoint - per-pool used / committed / max / init). jvm_* is the Prometheus client's own JVM export. They overlap; keep one in production.

Core - is it up and are builds passing

MetricWhat it tells you
upPrometheus scrape liveness - 1 = the /prometheus/ endpoint responded. The monitoring liveness signal.
default_jenkins_upThe Jenkins controller is up (the plugin's own check).
jenkins_runs_failure_totalBuilds that finished as failure - the headline CI-health signal.
jenkins_runs_success_totalBuilds that finished successfully - build throughput.
jenkins_queue_size_valueCurrent build-queue depth - the backlog of builds waiting for an executor.
default_jenkins_executors_busyExecutors currently running builds - utilization against capacity.

Operational - what to alert on

MetricWhat it tells you
default_jenkins_uptimeController uptime in milliseconds - a reset indicates a restart.
default_jenkins_quietdown1 = the controller is in quiet-down, refusing new builds.
default_jenkins_executors_available, _idle, _online, _defined, _connectingExecutor pool: available, idle, online, defined, and connecting counts - capacity headroom.
default_jenkins_executors_queue_lengthItems queued waiting for an executor.
jenkins_runs_unstable_total, jenkins_runs_aborted_total, jenkins_runs_not_built_totalBuilds that finished unstable / aborted / not-built.
default_jenkins_builds_last_build_result_ordinalPer-job last build result (0 = success ... 4 = not built) - which job last broke.
default_jenkins_builds_health_scorePer-job health score (0-100, the weather icon).
default_jenkins_builds_last_build_duration_millisecondsPer-job last build duration - regressions in build time.
default_jenkins_builds_duration_milliseconds_summaryPer-job build-duration distribution.
jenkins_queue_blocked_value, jenkins_queue_buildable_value, jenkins_queue_stuck_valueQueue breakdown - blocked, buildable, and stuck items (stuck = cannot proceed).
jenkins_executor_count_value, jenkins_executor_free_valueTotal and free executors.
jenkins_node_online_value, jenkins_node_offline_valueBuild-node online and offline counts - agent availability.
jenkins_job_building_duration, jenkins_job_queuing_durationTime jobs spend building and queuing (summaries).
jenkins_health_check_scoreThe controller's aggregate health-check self-assessment.
jenkins_plugins_failed, jenkins_plugins_activePlugins that failed to load (non-zero breaks functionality) and active plugin count.
http_requestsHTTP request rate / latency to the controller (summary).
http_responseCodes_serverError_totalController 5xx responses.
system_cpu_loadSystem CPU load on the controller host.
jvm_memory_bytes_usedJVM heap + non-heap bytes used - controller memory pressure.
vm_memory_heap_usageJVM heap usage ratio (Dropwizard).
jvm_threads_currentLive JVM threads.

Diagnostic - for investigation and tuning

Higher cardinality and drill-down detail; reach for these during an incident or a capacity review. They are grouped here rather than enumerated - run a curl against /prometheus/ for the exhaustive list.

GroupRepresentative membersWhen you reach for it
Version infodefault_jenkins_version_infoConfirm the controller version on the wire.
Per-job bookkeepingdefault_jenkins_builds_last_build_logfile_size_bytes, default_jenkins_builds_discard_active, the _created timestamp seriesLog-size growth, discard policy, and Prometheus client _created bookkeeping.
Pre-windowed historyjenkins_queue_size_history, jenkins_executor_count_history and the other _history gaugesServer-computed rolling windows of the _value gauges.
CPU-load windowssystem_cpu_load_x100_window_1m / _5m / _15m / _1hRolling CPU-load windows (value x100), computed server-side.
Non-error HTTP codeshttp_responseCodes_ok_total and the other non-error response countersTraffic mix on the controller's web layer.
Dropwizard JVM memoryvm_memory_* (per-pool used / committed / max / init - the largest family on this endpoint)Heap and per-pool memory drill-down.
JVM detailvm_gc_*, vm_file_descriptor_*, the jvm_memory_pool_* and jvm_threads_* seriesGC, file-descriptor, and thread-state investigation across both JVM exports.
Scrape metascrape_duration_seconds, scrape_samples_scrapedReceiver-side scrape health, not from Jenkins.

Key Alerts to Configure

Threshold guidance for the most useful Core and Operational series. State alerts read a value Jenkins reports directly; the rest are relative to your own baseline. These are starting points - tune them to your workload.

AlertConditionWhy it matters
Jenkins unreachableup == 0 for > 1m, or default_jenkins_up == 0The endpoint or the controller is down - check the Jenkins process and port 8080.
Build failures risingrate(jenkins_runs_failure_total) rising vs baselineBuilds are breaking - identify the job via default_jenkins_builds_last_build_result_ordinal.
Queue backing upjenkins_queue_size_value rising vs baseline, or jenkins_queue_stuck_value > 0Builds are waiting for executors (or are stuck) - add executors / agents or clear the blocker.
Executors saturateddefault_jenkins_executors_busy == default_jenkins_executors_available sustainedNo free executors - throughput is capped; scale agents.
Agents offlinejenkins_node_offline_value > 0Build agents are offline - capacity is reduced; check agent connectivity.
Plugin load failuresjenkins_plugins_failed > 0One or more plugins failed to load - functionality is broken; check the plugin and Jenkins logs.
Build duration regressiondefault_jenkins_builds_last_build_duration_milliseconds rising vs baseline for a jobA job got slower - inspect recent changes and agent load.
HTTP server errorsrate(http_responseCodes_serverError_total) > 0The controller is returning 5xx - check the Jenkins log and load.
Controller memory pressurejvm_memory_bytes_used rising toward max, or vm_memory_heap_usage near 1The JVM is running out of heap - raise -Xmx or reduce load; GC thrash slows everything.
Quiet-down stuck ondefault_jenkins_quietdown == 1 unexpectedlyThe controller is refusing new builds - cancel quiet-down if it was not intended.

Access Setup

Install the Prometheus Metrics plugin, then point the receiver at /prometheus/.

Install via the Jenkins UI under Manage JenkinsPluginsAvailable plugins (search for "Prometheus Metrics"), or bake it into the controller image with the CLI:

jenkins/Dockerfile
FROM jenkins/jenkins:lts-jdk17
RUN jenkins-plugin-cli --plugins prometheus

The /prometheus/ endpoint is unauthenticated by default. The exposure path, collection period, and access settings are configurable under Manage Jenkins → the Prometheus plugin settings.

Verify the endpoint is serving (the trailing slash is required):

Verify access
# Check Jenkins is up
curl -so /dev/null -w "%{http_code}" http://localhost:8080/login

# Verify the Prometheus metrics endpoint (trailing slash required)
curl -s http://localhost:8080/prometheus/ | head -20

Right after start the body may be empty until the first collection cycle completes (see the collection-period note above) - re-run after the cycle.

Configuration

The prometheus receiver scrapes /prometheus/ on :8080. The metric_relabel_configs keep filter scopes collection to the Jenkins families and drops the JVM vm_* / jvm_* noise; remove it to keep the full JVM detail.

config/otel-collector.yaml
receivers:
prometheus:
config:
scrape_configs:
- job_name: jenkins
scrape_interval: 10s
metrics_path: /prometheus/ # Trailing slash required
static_configs:
- targets:
- ${env:JENKINS_HOST}:8080
metric_relabel_configs:
- source_labels: [__name__]
regex: 'jenkins_.*|default_jenkins_.*|up'
action: keep

processors:
resource:
attributes:
- key: deployment.environment.name
value: ${env:ENVIRONMENT}
action: upsert
- key: environment
value: ${env:ENVIRONMENT}
action: upsert
- key: service.name
value: ${env:SERVICE_NAME}
action: upsert

batch:
timeout: 10s
send_batch_size: 1024

exporters:
otlphttp/b14:
endpoint: ${env:OTEL_EXPORTER_OTLP_ENDPOINT}
tls:
insecure_skip_verify: true

service:
pipelines:
metrics:
receivers: [prometheus]
processors: [resource, batch]
exporters: [otlphttp/b14]

Environment Variables

.env
JENKINS_HOST=localhost
ENVIRONMENT=your_environment
SERVICE_NAME=your_service_name
OTEL_EXPORTER_OTLP_ENDPOINT=https://<your-tenant>.base14.io

Authentication

If you secure /prometheus/, add basic auth to the scrape config and use a Jenkins API token rather than a password:

config/otel-collector.yaml (auth)
receivers:
prometheus:
config:
scrape_configs:
- job_name: jenkins
metrics_path: /prometheus/
basic_auth:
username: ${env:JENKINS_USER}
password: ${env:JENKINS_TOKEN}
static_configs:
- targets:
- ${env:JENKINS_HOST}:8080

Semconv version note: deployment.environment.name is the current OTel attribute (semantic conventions v1.27+, stable as of v1.41.0). Scout's UI filters on the lowercase environment key, so emit it alongside the OTel-native deployment.environment.name. The legacy deployment.environment is still accepted for backward compatibility.

Verify the Setup

Start the Collector and check for metrics within 60 seconds:

# Check Collector logs for scraped Jenkins metrics
docker logs otel-collector 2>&1 | grep -i "jenkins"

# Check the metrics endpoint directly (trailing slash required)
curl -s http://localhost:8080/prometheus/ | grep jenkins_

# Run a build so the build-result families advance
# (or wait for a scheduled job to fire)

Troubleshooting

No Jenkins metrics on /prometheus/

Cause: The Prometheus Metrics plugin is not installed - Jenkins exposes no metrics without it.

Fix:

  1. Confirm the plugin under Manage JenkinsPluginsInstalled plugins.
  2. If it is missing, install it (UI or jenkins-plugin-cli --plugins prometheus) and restart Jenkins.
  3. Confirm the URL includes the trailing slash: /prometheus/, not /prometheus.

Endpoint returns an empty body right after start

Cause: The Prometheus plugin collects metrics on a timer (default 120s) and serves the last cached snapshot, so the first cycle has not completed yet.

Look at: up and the scrape_* series - early scrapes carry only these until the first collection populates the Jenkins families.

Fix:

  1. Wait for the first collection cycle (up to COLLECTING_METRICS_PERIOD_IN_SECONDS, default 120s).
  2. Re-run curl http://localhost:8080/prometheus/ - the body fills once the cycle completes.

Build metrics are missing

Cause: The build families need at least one job that has run.

Look at: jenkins_runs_* and default_jenkins_builds_* - these stay absent on a controller with no executed builds, while the executor, queue, and node families appear immediately.

Fix:

  1. Run a job (or wait for a scheduled trigger to fire).
  2. Build-result and per-job metrics populate after the first execution.

Connection refused on port 8080

Cause: The Collector cannot reach Jenkins at the configured address.

Fix:

  1. Verify Jenkins is running: docker ps | grep jenkins.
  2. Jenkins takes 30-60 seconds to start - check docker logs jenkins for startup progress.
  3. Check firewall rules if the Collector runs on a separate host.

No metrics appearing in Scout

Cause: Metrics are collected but not exported.

Fix:

  1. Check Collector logs for export errors: docker logs otel-collector.
  2. Verify OTEL_EXPORTER_OTLP_ENDPOINT is set correctly.
  3. Confirm the pipeline includes both the receiver and the exporter.

FAQ

Does this work with Jenkins running in Kubernetes?

Yes. Set the scrape target to the Jenkins service DNS endpoint on port 8080 (e.g., jenkins.default.svc.cluster.local:8080) and bake the Prometheus Metrics plugin into the controller image. The Collector can run as a sidecar or DaemonSet.

What is the difference between up and default_jenkins_up?

up is the prometheus receiver's scrape liveness - 1 means the /prometheus/ endpoint responded. default_jenkins_up is the plugin's own gauge - 1 means the Jenkins controller itself reports healthy. Alert on both: a down endpoint and a down controller are different failures.

Why is /prometheus/ empty right after startup?

The Prometheus plugin collects metrics on a timer (COLLECTING_METRICS_PERIOD_IN_SECONDS, default 120s) and serves the last cached snapshot. Until the first cycle completes, the endpoint returns 200 with an empty body and scrapes carry only up and scrape_*. Wait one collection period.

Why are there no build metrics?

The build families (jenkins_runs_*, default_jenkins_builds_*) need at least one job that has run. A controller with no executed builds shows the executor, queue, and node families but no build results. Run a job to populate them.

Should I keep both vm_* and jvm_*?

No. vm_* is the Dropwizard JVM instrumentation (with the large per-pool vm_memory_* family) and jvm_* is the Prometheus client's own JVM export - two parallel exports of the same data. Keep one in production; the keep filter in the config drops both, so add back the one you want.

What's Next?

  • Create Dashboards: Explore pre-built dashboards or build your own. See Create Your First Dashboard.
  • Monitor More Components: Add monitoring for ArgoCD and other CI/CD components.
  • Fine-tune Collection: The jenkins_.*|default_jenkins_.*|up keep filter scopes collection to the Jenkins families; remove it to keep the full JVM detail (vm_* / jvm_*).
Was this page helpful?