Monitor GitLab with Nightingale
The GitLab package bundles several exporters for the host, Rails/Sidekiq, Gitaly, Workhorse, PostgreSQL, Redis and NGINX; the template ships five dashboards.
Last reviewed
The GitLab Linux Package comes with a full set of exporters: node_exporter (host), gitlab_exporter (Rails and Sidekiq), Gitaly, Workhorse, postgres_exporter, redis_exporter and NGINX VTS. The five dashboards (overview, machine performance, NGINX, PostgreSQL, Redis) each use part of that set, so scrape all of these endpoints rather than a single /metrics.
The rules cover Sidekiq queue backlog, Gitaly and Workhorse error rates, database connections and Redis memory, HTTP 5xx and host resources.
What's in the template
- 5 dashboards, 52 panels in total
- 9 alert rules, each with runbook steps
How to connect
For an external Categraf, make the exporters listen on reachable addresses in /etc/gitlab/gitlab.rb and firewall them to the collector node:
prometheus['enable'] = false
node_exporter['listen_address'] = '0.0.0.0:9100'
gitlab_workhorse['prometheus_listen_addr'] = '0.0.0.0:9229'
gitlab_exporter['listen_address'] = '0.0.0.0'
gitlab_exporter['listen_port'] = '9168'
redis_exporter['listen_address'] = '0.0.0.0:9121'
postgres_exporter['listen_address'] = '0.0.0.0:9187'
gitaly['configuration'] = { prometheus_listen_addr: '0.0.0.0:9236' }
After gitlab-ctl reconfigure, list each port in the urls of the Categraf prometheus input with job = "gitlab" in labels. If you keep GitLab's bundled Prometheus, adding it as a data source works too.
- Open the integration center in Nightingale, find Gitlab, and import the dashboards and alert rules into a business group.
- Imported rules start disabled. Check that the metrics are arriving first, then enable the rules one by one with thresholds that fit your environment.
Bundled dashboards
| Dashboard | Panels |
|---|---|
| GitLab - Machine Performance | 10 |
| GitLab - NGINX VTS | 7 |
| GitLab - Overview | 10 |
| GitLab - PostgreSQL | 15 |
| GitLab - Redis | 10 |
Bundled alert rules
9 rules. Each lists its trigger condition (PromQL) and the steps to take when it fires. Thresholds are meant to be adjusted after import.
HighgRPCResourceExhaustedRate
High gRPC ResourceExhausted error rate
S2 Warning · for 60 min · Trigger:
sum without (grpc_code, grpc_method, grpc_service, grpc_type) (
rate(grpc_server_handled_total{grpc_code="ResourceExhausted"}[5m])
) / sum without (grpc_code, grpc_method, grpc_service, grpc_type) (
rate(grpc_server_handled_total[5m])
) * 100 > 1
Runbook:
- On the Gitaly node, check the logs to see which RPCs are being limited (gitaly_concurrency_limiting related);
- Use gitlab-ctl status plus node CPU/IO to confirm Gitaly is overloaded;
- Determine whether an oversized repository or a misbehaving client is pulling repeatedly, and throttle that source temporarily;
- If capacity is genuinely short, raise Gitaly's concurrency limits and add nodes.
HighPumaUtilization
Puma instance {{ $labels.instance }} has more than 90% thread utilization ({{ $value | printf "%.1f" }}%) over the last 60 minutes.
S2 Warning · for 60 min · Trigger:
instance:puma_utilization:ratio * 100 > 90
Runbook:
- On the GitLab node run gitlab-ctl status to check the puma process and look for timeouts in /var/log/gitlab/puma/;
- Combine with the PumaQueueing alert to judge the queue depth;
- Short term, raise puma['worker_processes'] and max_threads then run gitlab-ctl reconfigure;
- Look for slow requests (large MR diffs, big repository pages) holding workers for a long time and rate limit them if needed.
PostgresDatabaseDeadlocks
Postgres database has deadlocks
S2 Warning · Trigger:
increase(pg_stat_database_deadlocks[5m]) > 0
Runbook:
- On PostgreSQL confirm log_lock_waits=on and take the full SQL of both deadlock participants from the log;
- Join pg_locks with pg_stat_activity to identify which two tables are waiting on each other;
- In GitLab this commonly comes from CI concurrently updating the same records — reduce concurrency or upgrade GitLab (upstream frequently fixes these);
- Check whether a long transaction is amplifying the deadlock probability.
PostgresDown
The Postgres service {{ $labels.job }} instance {{ $labels.instance }} is not responding for more than 50% of the time for 5 minutes.
S2 Warning · Trigger:
avg_over_time(pg_up[5m]) * 100 < 50
Runbook:
- Log in to the PostgreSQL node and confirm the process is alive (gitlab-ctl status postgresql or systemctl status postgresql);
- If it is alive, connect by hand with psql to tell a hung instance from a network or pg_hba problem;
- If it is not running, read the tail of the PG log (full disk, full WAL directory, OOM) before starting it;
- If a standby exists, assess whether to fail over.
PumaQueueing
Puma instance {{ $labels.instance }} is queueing requests with an average of {{ $value | printf "%.1f" }} over the last 30 minutes.
S2 Warning · Trigger:
avg_over_time(puma_queued_connections[30m]) > 1
Runbook:
- Combine with HighPumaUtilization to confirm worker utilization;
- Check /var/log/gitlab/puma/puma_stdout.log and the gitlab-rails log for the slowest request paths;
- Short term, raise puma['worker_processes'] and run gitlab-ctl reconfigure;
- Longer term, find the root cause of the slow requests (large repositories, large MRs, unindexed queries) and add nodes if needed.
RedisDown
The Redis service {{ $labels.job }} instance {{ $labels.instance }} is not responding for more than 50% of the time for 5 minutes.
S2 Warning · Trigger:
avg_over_time(redis_up[5m]) * 100 < 50
Runbook:
- Log in to the Redis node and check the process with gitlab-ctl status redis or systemctl status redis;
- If it is alive, verify with redis-cli ping to tell a hang from a network or password problem;
- If it is not running, check the logs (OOM, or a full disk breaking the RDB write) before starting it;
- After recovery, check whether the Sidekiq queues are backed up and watch them drain.
ServiceDown
The service {{ $labels.job }} instance {{ $labels.instance }} is not responding for more than 50% of the time for 5 minutes.
S2 Warning · Trigger:
avg_over_time(up[5m]) * 100 < 50
Runbook:
- Take job/instance from the alert labels to identify the component (gitaly/puma/sidekiq/registry, etc.);
- On that node run gitlab-ctl status to check the process state;
- Use gitlab-ctl tail <service> to find the exit reason in the logs;
- After restarting, run gitlab-rake gitlab:check for an overall health check.
SidekiqJobsQueuing
Sidekiq has jobs queued
S2 Warning · for 60 min · Trigger:
sum by (name) (sidekiq_queue_size) > 0
Runbook:
- Take name from the alert labels to identify the backed-up queue;
- Check queue depth and failed jobs in the GitLab admin Background Jobs page or via gitlab-rails runner;
- Confirm the Sidekiq process is alive and that concurrency is not set too low (sidekiq['max_concurrency']);
- If it is caused by mass job failures and retries, find the failure root cause (Redis/PG problems, external timeouts) before clearing them.
WorkhorseHighErrorRate
Workhorse has high error rates
S2 Warning · Trigger:
(
sum without (job, code) (
job_route_method_code:gitlab_workhorse_http_request_duration_seconds_count:rate5m{code=~"5.."}
) /
sum without (job,code) (
job_route_method_code:gitlab_workhorse_http_request_duration_seconds_count:rate5m
) < 10
) * 100 > 50
Runbook:
- Run gitlab-ctl tail gitlab-workhorse to see the exact errors and the failing routes;
- The usual root cause is a puma timeout or Gitaly being unavailable — confirm both are healthy first;
- For large-file issues, check disk space and temporary directory permissions;
- If it lines up with a recent upgrade, roll back first.
Related
- Gitlab template source: the raw JSON for dashboards, rules and collector config. Pull requests welcome.
- Import dashboards and alert rules
- Verify that metrics are stored
- All integrations