GitLab Time Tracking: How to Log and Report Time on Merge Requests

Keito Team
17 April 2026 · 10 min read

Set up GitLab time tracking on merge requests using /spend and /estimate. Learn the steps, limits, and when to add automated activity-based tools.

Integration Guides

To track time on a GitLab merge request, post a comment with /spend 2h to log work and /estimate 1d to set the budget. Both quick actions write to the native time tracking widget in the MR sidebar and appear in the GitLab time tracking report.

Your team runs every part of the delivery pipeline inside GitLab — issues, merge requests, CI/CD, even the container registry. Yet when payroll Monday rolls around, hours still come from a separate stopwatch tool or a spreadsheet somebody updates from memory. The two sources never quite reconcile, and the developers who close the most merge requests often log the fewest hours. This guide covers GitLab’s built-in /spend and /estimate commands and how to apply them to merge requests. It also shows where the data ends up, and where teams hit the wall that pushes them toward automated tracking.

What are GitLab’s native time tracking quick actions?

GitLab ships with two quick actions for time data. /estimate sets the expected effort for a piece of work. /spend records how long something actually took. Both work inside issues, tasks, epics, and merge requests.

A quick action is a slash command typed into a comment or description box. When the comment posts, GitLab strips the command, applies the change, and stores the result against the work item. You can also use /remove_estimate to clear a budget and /remove_time_spent to wipe all logged entries at once (GitLab Docs).

What time units does GitLab accept?

GitLab recognises seconds (s), minutes (m), hours (h), days (d), weeks (w), and months (mo). The conversion is fixed and does not respect your calendar. One day is 8 hours, one week is 40 hours, and one month is 160 hours. Units can be combined in a single command, like /spend 1d 2h 30m.

Where does the time data appear?

Every entry shows up in three places. The MR sidebar displays total time spent, the estimate, and the remaining budget. Clicking the time spent figure opens a detailed time tracking report with per-user breakdowns and dates. At the instance level, /-/timelogs on your GitLab base URL lists entries across every project you can see.

How do you log time on a GitLab merge request step by step?

The simplest working flow is a three-step loop: estimate at creation, spend as you go, verify in the sidebar. The discipline is the hard part — the commands themselves take seconds.

Step 1: Check your project permissions

Time tracking is enabled by default, but writing entries requires the right role. On merge requests, you need the Developer, Maintainer, or Owner role to add estimates or log time. On issues and tasks the Planner and Reporter roles also work. Only entry authors, Maintainers, or Owners can delete individual timelogs.

If you manage a self-managed GitLab instance, also check whether the global time tracking report feature flag global_time_tracking_report is switched on. It is off by default on self-managed installs, which means contributors see data per MR but the instance-wide report is empty.

Step 2: Set an estimate when you open the MR

Add /estimate to the MR description or a comment when you open the merge request. A single estimate is kept per item — later /estimate 2d replaces the earlier value rather than adding to it.

/estimate 4h

Setting an estimate up front is worth the extra ten seconds. Without one, “remaining” in the sidebar shows nothing to react to, which defeats much of the reporting value later.

Step 3: Log time in small increments as you work

Add /spend comments on the MR while the work is fresh. Accepted forms include plain durations, back-dated entries, and negative corrections.

/spend 1h 30m
/spend 2h 2026-04-15
/spend -30m          # subtract an over-logged half hour

Every /spend adds to the running total rather than replacing it. A developer who logs five 30-minute entries sees 2h 30m against the MR by the end of the day. Back-dating with spentAt is essential for work done over the weekend or before the MR existed.

Step 4: Read the time data in the MR sidebar

The time tracking widget sits in the right sidebar of every MR with time data. It shows the estimate, the running total, and a progress indicator. The detailed report behind it lists each entry with timestamp, author, and any summary note.

Step 5: Pull data out via the API

For anything beyond the single-MR view, use the GraphQL API. Timelogs are exposed against merge requests, issues, projects, and groups. A basic query returns user, spentAt, and timeSpent for every entry inside a project.

query {
  project(fullPath: "acme/checkout") {
    timelogs {
      nodes {
        user { username }
        spentAt
        timeSpent
        summary
        mergeRequest { iid }
      }
    }
  }
}

The REST endpoint GET /projects/:id/merge_requests/:merge_request_iid/time_stats returns the aggregated totals for a single MR, which is all you need for lightweight dashboards (GitLab GraphQL docs).

What are the limits of GitLab’s built-in time tracking?

Native tracking is well-suited to small teams with strong logging discipline. Past that size, five gaps show up quickly and cost real money.

No automatic capture. Every entry is a manual /spend command. Forget to post it and you are reconstructing the day from memory on Friday afternoon. A 2024 developer survey found recall and search overhead costs more than 30 minutes per developer per day (Stack Overflow 2024 Developer Survey).

No timer functionality. GitLab does not provide a start/stop button. You have to already know the duration before you log it, which encourages rough round numbers instead of accurate minutes.

Reporting is thin. The MR sidebar, the per-item report, and the instance-wide /-/timelogs view are the entire built-in reporting surface. Anything else — per-client rollups, billable vs non-billable splits, weekly approval workflows — has to be built on top of the GraphQL API.

No billable tagging. Every entry is treated the same. There is no field to mark time as billable, non-billable, overhead, or investment. Agencies relying on GitLab alone have to encode that in the summary text and parse it later.

Hard caps. The time tracking report UI shows at most 100 entries per item, and the per-item total cannot exceed 4 years. These only matter on very long-lived issues or epics, but they catch out teams migrating years of historical data.

How does GitLab track CI/CD pipeline time?

GitLab records pipeline duration automatically. Every job, stage, and full pipeline run carries a start and end timestamp visible in the CI/CD section of the project. This is separate from the time tracking system and does not appear in the MR sidebar widget.

The distinction matters for billing. Pipeline time is machine time — the wall-clock duration a runner spent executing steps. Developer time is the hours a person spent writing, reviewing, and shepherding the code that produced the pipeline. Conflating the two inflates invoices in one direction and masks slow pipelines that waste developer attention in the other.

For teams that bill infrastructure separately, pipeline analytics give a clean view of which stages are slow. Long queue times and runner contention show up as gaps between queued_at and started_at. Slow test suites appear as large duration values on a specific stage.

When should you add third-party time tracking to GitLab?

The honest test is a yes to any of these. Add third-party tracking if your team is above 10 active contributors. Also add it if you bill clients from GitLab activity, or if timesheet fatigue is a recurring complaint. Growing reconciliation spreadsheets are another clear signal.

The pattern that scales is observation rather than instruction. Activity-based trackers connect to GitLab via OAuth, subscribe to merge request, comment, review, and commit webhooks, and reconstruct a timesheet from what actually happened. The developer reviews the auto-generated draft, nudges anything misallocated, and approves.

This is the same principle behind mapping git commits to billable time, extended across the full GitLab event surface. Teams running multiple trackers will recognise the shape from the GitHub time tracking guide — the event names differ but the approach is identical.

How does the hybrid pattern work?

Many teams keep /spend for quick notes on a specific MR and use an activity-based tool for the primary record. The /spend entries remain visible to reviewers and become useful context inside the MR. The activity-based tool handles the billing, reporting, and export work that GitLab alone does not cover.

The worst outcome is three parallel systems with no clear source of truth. Pick one primary record — ideally the activity-based layer — and treat quick actions as annotations on top.

How do you set this up on a self-managed GitLab instance?

Self-managed deployments have two extra configuration steps. Administrators can modify webhook limits that the cloud version fixes, which matters for integrations receiving high-volume events. The push_event_hooks_limit application setting controls how many commits per push fire webhook deliveries.

Also check auto-disabling behaviour. Since GitLab 15.10 the auto_disabling_web_hooks flag was disabled on self-managed project webhooks, which stops an integration from being silently turned off after repeated delivery failures. If your instance turns it back on, an outage at the receiver end can quietly halt time data capture for days before anyone notices.

Finally, enable global_time_tracking_report if you want the instance-wide /-/timelogs view. Without it, administrators cannot see aggregated time data outside individual items.

How do you pick the right approach for your GitLab team?

Team sizeRecommended approachWhy
1-5 developers/estimate + /spend on every MRZero extra tooling, discipline is manageable
5-15 developersQuick actions + GraphQL reporting scriptNative logging with external dashboards
15+ developersActivity-based automation via webhooksManual logging breaks at scale; observation scales
Agencies billing clientsActivity-based with repo-to-client mappingAudit trail, billable tagging, export to invoicing

There is a reasonable middle path: start with /spend while you prove the process, then graduate to an activity-based tool once volume makes manual logging unsustainable. Teams already running a write-once pattern for their issue tracker will recognise the logic from the Jira time tracking fix guide. The same structural argument applies to GitLab.

What should you do once tracking is live?

Three actions turn a policy into a habit. First, agree a review cadence. Friday afternoon works because the week is fresh and invoicing deadlines land on Monday. Second, wire approved hours into whatever tool sends invoices; copy-paste between systems is where evidence trails break. Third, run a monthly audit comparing tracked hours against merged MRs. The aim is not to police individuals but to spot teams heading for burnout or under-counting reviews.

Key Takeaway: GitLab’s /spend and /estimate are enough for small teams. Past 10 developers, add an activity-based tracker that reads merge request and review webhooks.

Frequently Asked Questions

How do I log time on a GitLab merge request?

Post a comment containing /spend followed by the duration, for example /spend 2h or /spend 1d 30m. The entry appears in the MR sidebar time tracking widget and in the detailed time tracking report. You can back-date with /spend 2h 2026-04-15 and subtract with a negative value.

Can GitLab track time automatically?

No. The built-in /spend and /estimate quick actions are entirely manual. Automatic capture requires a third-party integration that reads GitLab webhooks or the GraphQL API and reconstructs time from merge request, commit, and review events.

How do I export time tracking data from GitLab?

Use the GraphQL API timelog queries or the REST endpoint GET /projects/:id/merge_requests/:merge_request_iid/time_stats. GraphQL is the richer option because it exposes per-user, per-date, and per-item filters. There is no built-in CSV export in the UI on standard plans.

Does GitLab time tracking work on merge requests or only issues?

Both. The /spend, /estimate, /remove_estimate, and /remove_time_spent quick actions all work on merge requests, issues, tasks, and epics. The permissions differ slightly — merge requests require the Developer role or higher, while issues also accept the Planner and Reporter roles.

Can I track billable versus non-billable time in GitLab?

Not natively. GitLab does not distinguish billable from non-billable entries in the time tracking system. Teams usually encode the distinction in the /spend summary or in labels, then parse it via the GraphQL API. A dedicated activity-based tracker handles this as a first-class field.

What time units does the /spend command accept?

Seconds (s), minutes (m), hours (h), days (d), weeks (w), and months (mo). Conversion is fixed: one day is 8 hours, one week is 40 hours, one month is 160 hours. Units can be combined, for example /spend 1w 2d 3h.

Does pipeline duration count toward developer time tracking?

No. CI/CD pipeline duration is recorded separately by GitLab and represents machine time, not developer effort. It does not feed the MR sidebar widget or the time tracking report. Pipeline analytics are useful for runner capacity planning but should not be billed as developer hours.

Ready to stop typing /spend on every merge request?

Connect your GitLab group to Keito and let your actual merge request, review, and commit activity build your timesheet. Approve on Friday, export straight to invoicing, and keep /spend for the annotations that still matter.

Connect GitLab to Keito

Ready to track time smarter?

Flat-rate time tracking with unlimited users. No per-seat surprises.