Published on: April 28, 2026
8 min read
This practical guide to GitLab pipeline analytics helps self-managed users gain operational insights using Prometheus and Grafana.

CI/CD optimization starts with visibility. Building a successful DevOps platform at enterprise scale should include understanding pipeline performance, job execution patterns, and quantifiable operational insights — especially for organizations running GitLab self-managed instances.
To help GitLab customers maximize their platform investments, we developed the GitLab CI/CD Observability solution as part of our Platform Excellence program, which transforms raw pipeline metrics into actionable operational insights.
A leading financial services organization partnered with GitLab's customer success architect to gain visibility into their GitLab self-managed deployment. Together, we implemented a containerized observability solution combining the open-source gitlab-ci-pipelines-exporter with enterprise-grade Prometheus and Grafana infrastructure.
In this article, you'll learn the challenges they faced managing pipelines at scale and how GitLab CI/CD Observability addressed them with a practical, end-to-end implementation.
Before implementing any observability solution, define your measurement landscape:
Once deployed, the observability stack provides a set of Grafana dashboards that give real-time and historical visibility into your CI/CD platform. A typical deployment includes:
Each dashboard is provisioned automatically via Grafana's file-based provisioning, so it deploys consistently across environments. The dashboards can be further customized with Grafana variables to filter by project, ref/branch, or time range.

The solution requires two exporters:
Prerequisites:
For enterprise environments, deploy each component as a separate Deployment within a dedicated namespace. This approach integrates with existing cluster infrastructure, secrets management, and network policies.
kubectl create namespace gitlab-observability
# Create the GitLab token secret (see Secrets Management section below
# for enterprise-grade approaches using external secret operators)
kubectl create secret generic gitlab-token \
--from-literal=token=glpat-xxxxxxxxxxxx \
-n gitlab-observability
# exporter-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitlab-ci-pipelines-exporter
namespace: gitlab-observability
spec:
replicas: 1
selector:
matchLabels:
app: gitlab-ci-pipelines-exporter
template:
metadata:
labels:
app: gitlab-ci-pipelines-exporter
spec:
containers:
- name: exporter
image: mvisonneau/gitlab-ci-pipelines-exporter:latest
ports:
- containerPort: 8080
env:
- name: GCPE_GITLAB_TOKEN
valueFrom:
secretKeyRef:
name: gitlab-token
key: token
- name: GCPE_CONFIG
value: /etc/gcpe/config.yml
volumeMounts:
- name: config
mountPath: /etc/gcpe
volumes:
- name: config
configMap:
name: gcpe-config
---
apiVersion: v1
kind: Service
metadata:
name: gitlab-ci-pipelines-exporter
namespace: gitlab-observability
spec:
selector:
app: gitlab-ci-pipelines-exporter
ports:
- port: 8080
targetPort: 8080
# node-exporter-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: gitlab-observability
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
containers:
- name: node-exporter
image: prom/node-exporter:latest
ports:
- containerPort: 9100
---
apiVersion: v1
kind: Service
metadata:
name: node-exporter
namespace: gitlab-observability
spec:
selector:
app: node-exporter
ports:
- port: 9100
targetPort: 9100
# prometheus-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: gitlab-observability
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
containers:
- name: prometheus
image: prom/prometheus:latest
ports:
- containerPort: 9090
volumeMounts:
- name: config
mountPath: /etc/prometheus
volumes:
- name: config
configMap:
name: prometheus-config
---
apiVersion: v1
kind: Service
metadata:
name: prometheus
namespace: gitlab-observability
spec:
selector:
app: prometheus
ports:
- port: 9090
targetPort: 9090
The Grafana deployment below starts with authentication disabled (GF_AUTH_ANONYMOUS_ENABLED: true) for initial setup convenience.
This setting allows anyone with network access to view all dashboards without logging in. For production deployments, remove this variable or set it to false and configure a proper authentication provider (LDAP, SAML/SSO, or OAuth) to restrict access to authorized users.
# grafana-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana
namespace: gitlab-observability
spec:
replicas: 1
selector:
matchLabels:
app: grafana
template:
metadata:
labels:
app: grafana
spec:
containers:
- name: grafana
image: grafana/grafana:10.0.0
ports:
- containerPort: 3000
env:
# REMOVE or set to 'false' for production.
# When 'true', any user with network access can
# view dashboards without authentication.
- name: GF_AUTH_ANONYMOUS_ENABLED
value: 'true'
volumeMounts:
- name: dashboards-provider
mountPath: /etc/grafana/provisioning/dashboards
- name: datasources
mountPath: /etc/grafana/provisioning/datasources
- name: dashboards
mountPath: /var/lib/grafana/dashboards
volumes:
- name: dashboards-provider
configMap:
name: grafana-dashboards-provider
- name: datasources
configMap:
name: grafana-datasources
- name: dashboards
configMap:
name: grafana-dashboards
---
apiVersion: v1
kind: Service
metadata:
name: grafana
namespace: gitlab-observability
spec:
selector:
app: grafana
ports:
- port: 3000
targetPort: 3000
Restrict inter-pod traffic to only the required communication paths:
# network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: observability-policy
namespace: gitlab-observability
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
# Prometheus scrapes exporter and node-exporter
- from:
- podSelector:
matchLabels:
app: prometheus
ports:
- port: 8080
- port: 9100
# Grafana queries Prometheus
- from:
- podSelector:
matchLabels:
app: grafana
ports:
- port: 9090
kubectl get pods -n gitlab-observability
kubectl port-forward svc/grafana 3000:3000 -n gitlab-observability
curl http://localhost:3000/api/health
# gitlab-ci-pipelines-exporter.yml (ConfigMap: gcpe-config)
log:
level: info
gitlab:
url: https://gitlab.your-domain.com
maximum_requests_per_second: 10
project_defaults:
pull:
pipeline:
jobs:
enabled: true
wildcards:
- owner:
name: your-group-name
kind: group
archived: false
# prometheus.yml (ConfigMap: prometheus-config)
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'gitlab-ci-pipelines-exporter'
static_configs:
- targets: ['gitlab-ci-pipelines-exporter:8080']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
# datasources.yml (ConfigMap: grafana-datasources)
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
# dashboards.yml (ConfigMap: grafana-dashboards-provider)
apiVersion: 1
providers:
- name: 'default'
folder: 'GitLab CI/CD'
type: file
options:
path: /var/lib/grafana/dashboards
| Metric | Description |
|---|---|
gitlab_ci_pipeline_duration_seconds | Pipeline execution time |
gitlab_ci_pipeline_status | Pipeline success/failure by project |
gitlab_ci_pipeline_job_duration_seconds | Individual job execution time |
gitlab_ci_pipeline_job_status | Job success/failure status |
gitlab_ci_pipeline_job_artifact_size_bytes | Artifact storage consumption |
gitlab_ci_pipeline_coverage | Code coverage percentage |
gitlab_ci_environment_deployment_count | Deployment frequency |
gitlab_ci_environment_deployment_duration_seconds | Deployment execution time |
gitlab_ci_environment_behind_commits_count | Environment drift from main |
| Metric | Description |
|---|---|
node_cpu_seconds_total | CPU utilization |
node_memory_MemAvailable_bytes | Available memory |
node_filesystem_avail_bytes | Disk space available |
node_load1 | 1-minute load average |
For offline environments, install plugins manually. Example for Kubernetes:
# Copy plugin zip into the Grafana pod
kubectl cp grafana-polystat-panel-2.1.16.zip \
gitlab-observability/grafana-<pod-id>:/tmp/
# Extract plugin
kubectl exec -it -n gitlab-observability deploy/grafana -- \
sh -c "unzip /tmp/grafana-polystat-panel-2.1.16.zip -d /var/lib/grafana/plugins/"
# Restart Grafana pod
kubectl rollout restart deployment/grafana -n gitlab-observability
# Verify installation
kubectl exec -it -n gitlab-observability deploy/grafana -- \
ls -al /var/lib/grafana/plugins/
For regulated industries, ensure:
GitLab's API-first design enables custom observability solutions that complement native capabilities like Value Stream Analytics and DORA metrics. The open architecture allows organizations to integrate proven open-source tooling — like the gitlab-ci-pipelines-exporter — directly with their existing enterprise infrastructure, without disrupting established workflows.
As your observability maturity grows, GitLab's built-in Observability capabilities provide a natural next step — offering deeper, integrated visibility without additional tooling. Learn more about what's available natively in the platform for GitLab Observability.
Enjoyed reading this blog post or have questions or feedback? Share your thoughts by creating a new topic in the GitLab community forum.
Share your feedback