Scaling GitLab for large monorepos: when headcount stops being the right sizing input
Large monorepos and heavy concurrent CI change GitLab sizing, so a user count alone is no longer a safe basis for hardware. GitLab's reference architectures run from 1,000 users at 20 API requests per second (RPS) up to 50,000 users at 1,000 RPS, which means two organizations with the same headcount can need very different environments. Plan around RPS, and count the monorepo clone traffic and pipeline activity that generate those requests alongside the people using the instance. This planning matters when your repositories are large enough that Git operations are expensive, when developers push and fetch many times a day, or when CI runs many pipelines in parallel against the same projects. Those patterns land mainly on Gitaly, which serves Git data, and on the Rails and Sidekiq tiers that handle API calls and background jobs. Ask your team to measure current request volume, clone and fetch frequency, repository size and peak pipeline concurrency, then match those figures to the reference architecture tables in the GitLab documentation before you pick a node count. This sizing exercise is not a substitute for load testing. A reference architecture is a starting baseline built on GitLab's own test workloads, and your monorepo's object layout, branch churn and CI job shape can behave differently. Confirm the target with a load test against a staging environment before go-live, and confirm current workload guidance with GitLab, since the documented targets can change between releases.
Which workloads GitLab lists as performance risks
GitLab's reference architecture documentation names workloads that strain an instance as load grows, and they often compound each other. A large monorepo is expensive to read, a busy pipeline fleet multiplies requests, and background jobs queue behind both. Treat this summary as a starting point and confirm the current wording in the reference architecture documentation before you size hardware.
High CI job concurrency
Each running job calls back to GitLab for job requests, logs, artifacts and status updates, so a burst of jobs multiplies API traffic and background work. Runner count, executor type and per-runner concurrency decide how fast that burst arrives. Record your peak concurrent job count over a typical month, including scheduled pipelines and merge trains. Ask whoever operates your runner fleet how autoscaling limits are set, because those limits cap the peak you need to plan for.
Very large repositories
Gitaly serves Git reads and writes for every repository, and a repository with deep history, large binary files or many refs puts sustained load on the node that stores it. Simultaneous clones, fetches and pushes from many developers and build systems are the usual pressure point. Check repository size, history depth, large file use and whether CI clones shallowly or fully. Find out whether your largest monorepo sits on a single Gitaly node, because that node becomes the bottleneck for everything stored on it.
Concurrent API request volume
GitLab's reference architectures are sized in API requests per second, from 20 requests per second for 1,000 users to 1,000 requests per second for 50,000 users. Automation drives much of that traffic through CI jobs, bots, dashboards, scripts and synced tools that poll endpoints. Measure your current request rate, counting automation as well as people. Identify integrations that poll GitLab on a timer and check whether they can use webhooks instead.
Sidekiq background job backlog
Pushes, merges, webhooks, notifications and cleanup tasks run as background jobs in Sidekiq. When job volume rises faster than Sidekiq processes can drain it, work queues up and users notice delays in merge requests, mirrors and pipeline status updates. Check queue depth and job latency during your busiest hour, not the daily average. Separating Sidekiq nodes from Rails nodes is one scaling option, and the decision should follow queue growth you have measured.
PostgreSQL and Redis under load
Every API request and background job reads or writes PostgreSQL, and Redis holds cache and queue data. As concurrency rises, database connection counts and query latency can become the first limit, and Redis memory and eviction behavior matter under pressure. The reference architecture documentation recommends a high availability strategy for 3,000 or more users and keeps PostgreSQL and Redis outside Kubernetes. Confirm connection limits and failover behavior with your database team before you add application nodes.
Starting large and scaling down: choosing an initial reference architecture
When you already know the repository size, the number of developers pushing and the CI job concurrency you expect at peak, you can translate that into requests per second and match it to a published reference architecture. The reference architectures run from 1,000 users at 20 API requests per second to 50,000 users at 1,000 requests per second, so headcount alone rarely tells you the right tier. Large monorepos and heavy pipeline fan-out raise Gitaly and API load well beyond what the user count suggests.
- Estimate peak, not average: Use the busiest hour of a normal week, including release days, as the planning figure.
- Count CI as load: Pipelines that clone a large repository many times per hour add Gitaly reads that users never see directly.
- Size Gitaly separately: Confirm the storage and CPU needed for the repository size you are moving in, because it does not scale with user count alone.
- Start at the tier that covers the peak: Choose the reference architecture whose requests per second target meets or exceeds your estimate.
- Confirm the HA requirement: The documentation generally recommends high availability from 3,000 users, so check that the tier matches your availability needs.
Before committing, ask your DeployOpen or internal team to confirm the published node specifications for the chosen tier against your environment.
Many teams adopt GitLab while the monorepo and CI workload are still growing, and nobody has measured it yet. In this situation, the safer choice is usually to start one tier above your best estimate and measure. Undersizing during rollout tends to show up as slow pushes, delayed pipelines and support tickets during the period when people are learning the platform, which is the worst time for them. Oversizing costs more for a while, but it gives you room to observe real behavior before you cut capacity.
- Start larger during rollout: Pick a tier that leaves headroom for onboarding waves and early CI adoption.
- Measure from day one: Record request rates, Gitaly latency, Sidekiq queue depth and pipeline wait times from the first week.
- Set review points: Schedule a fixed review after the rollout reaches its planned user population, not after an incident.
- Document the assumption: Write down the tier chosen and why, so a later scale-down decision can be checked against it.
- Plan for cost of idle capacity: Ask for infrastructure costs per tier in any quote, since larger nodes recur every month.
Check that your monitoring can show Gitaly and CI queue trends before you go live, because those signals are what justify any later reduction.
Teams moving from a hosted GitLab instance or another hosted Git platform often have useful history, but they lack a self-managed baseline. Hosted usage data can tell you peak user activity and pipeline volume, yet it does not map directly to your new infrastructure, because network paths, storage and runner placement all change. Import activity also creates a temporary spike in Gitaly and API load while repositories, issues and pipelines are moved, so plan for that period separately from steady-state use.
- Export usage history first: Gather peak request and pipeline figures from the hosted instance before choosing a tier.
- Account for import load: Repository and history imports can overload a tier that handles steady state comfortably, so schedule them deliberately.
- Keep the starting tier during cutover: Hold the larger tier through cutover and the first full business cycle after it.
- Validate with a load test: Use the GitLab Performance Tool against the target before go-live to compare results with the tier's targets.
- Confirm migration tooling limits: Check which import sources and data types the import documentation supports for your case.
Confirm with the vendor documentation which import paths your source platform supports before you set the migration schedule.
Scaling down should be a decision backed by evidence, not a budget reaction. The signals that matter are sustained Gitaly resource use, request rates against the tier's target, and CI queue behavior over a full business cycle, including release peaks. A single quiet month does not prove a tier is oversized, because monorepo activity often clusters around sprint ends and releases. Treat any reduction as a controlled change with a rollback path.
- Watch Gitaly over time: Look for sustained CPU, memory and disk latency well below what the tier would allow, across several peaks.
- Compare request rates to target: Check whether peak API requests per second stay clearly under the tier's target.
- Read CI queue data: Short runner queues and low job wait times at peak suggest capacity is sufficient, but confirm this across the full cycle.
- Repeat the load test: Run the GitLab Performance Tool against the smaller tier before reducing production capacity.
- Keep a rollback plan: Keep the previous node sizes available so you can scale back quickly if latency rises.
Before changing any node size, confirm the scale-down steps and supported topology changes in the reference architecture documentation for your version.
Sizing options for Gitaly and the Git tier
This table compares the three main ways to run the Git storage tier for a large monorepo: a single Gitaly node, sharded Gitaly and Gitaly Cluster with Praefect. Read each row against your repository size, failure tolerance and the user count where GitLab recommends high availability.
| Option | Failure handling | Operational complexity | When to choose it |
|---|---|---|---|
| Single Gitaly node on a Linux package install | One node holds the repositories, so a node failure stops Git operations until it is restored from backup | Lowest. Managed with the same Linux package tooling as the rest of the instance | Smaller user counts where a planned restore window is acceptable and the monorepo fits one node's disk and memory |
| Sharded Gitaly | Each repository lives on one storage node, so a failure affects only the repositories on that node | Moderate. You plan how repositories are distributed across storage nodes and rebalance them as the load changes | Several large monorepos or many teams where spreading load across nodes matters more than automatic repository-level failover |
| Gitaly Cluster with Praefect | Repositories are replicated across nodes, so the cluster can keep serving if one node fails | Highest. Adds Praefect nodes, replication management and its own backup and restore considerations | Repository-level high availability is required, typically in the high availability designs GitLab recommends from 3,000 users |
| Heavy CI clone traffic on a monorepo | Long clones and fetches compete for the same Git node, so slow responses can appear before any outage | Depends on the option chosen. Watch Gitaly latency and saturation rather than only up or down status | Confirm with a load test against your own repository size and job concurrency before choosing the layout |
| Repository size and disk growth | A very large repository increases restore time and replication time on every option | Adds planning for storage capacity, backup duration and rebalancing effort | Ask the vendor or your own tests how backup and restore time scales for your largest repositories |
| Resource sizing for the Git node | Undersized CPU or memory slows pack operations and can cause timeouts during peak pushes and clones | Requires monitoring of Gitaly resource use and a plan to increase capacity | Start from the reference architecture that matches your user and request targets, then adjust for monorepo size |
How to size and validate a large monorepo deployment
This sequence turns repository size, Git traffic and CI concurrency into a reference architecture choice, a starting size, load test evidence and a production monitoring baseline before any capacity is removed.
Measure repository sizes and Git operation volume
Your platform team records the size of the largest repositories, including history and LFS content, and the rate of clones, fetches and pushes across a typical business week, with peaks at release or month-end windows. Pull these figures from your current Git host, the repository storage on disk and any existing proxy or access logs. The step is done when you have a written inventory listing each large repository, its size, and the peak operations per second you observed. Confirm with the GitLab documentation for your chosen version which repository metrics it exposes so the baseline can be compared later.
Measure CI concurrency and job profile
Your CI owners count the number of pipelines and jobs that run at the same time at peak, how long clone and fetch steps take inside jobs, and which pipelines clone the full history. Shallow clone settings and monorepo-aware pipelines change this load, so record them alongside the numbers. The output is a concurrency figure per hour and a list of pipelines responsible for most Gitaly reads. If your organization has no historical data, say so and plan a measurement window before sizing rather than assuming a profile.
Map the workload to a reference architecture
Your architects compare the inventory and the peak API request rate with GitLab's published reference architectures, which are sized by users and requests per second rather than headcount alone. Monorepo size and CI load can push a team to a larger tier than its user count suggests, so note that adjustment explicitly. The check is a written decision naming the reference architecture, the install method and the reason for any upward adjustment. Confirm the RPS targets and the recommended high availability strategy in the GitLab reference architectures documentation for your version before you commit.
Provision Gitaly and supporting nodes at a starting size
Your infrastructure team builds the environment at the larger starting size the mapping suggests, with dedicated Gitaly storage that has enough disk throughput and memory for large repositories, plus Sidekiq and Rails capacity sized for the CI load. Keep the first build simple so that each node role is visible when you measure it. The step is complete when every node passes health checks and the configuration is recorded in version control. Whether Gitaly Cluster or sharded Gitaly fits your repository-level HA needs is a separate decision, so confirm its current limitations in the GitLab documentation before you finalize the layout.
Run load tests with the GitLab Performance Tool
A performance engineer runs the GitLab Performance Tool against the new environment, using a request mix that reflects the Git operations and API calls measured in the first two steps. Include your largest repositories and a CI-like concurrency pattern so that Gitaly and Sidekiq are exercised together. The output is a results report compared with the request rate and latency targets for the chosen reference architecture. If results miss the targets, fix the bottleneck and rerun rather than accepting the size. Confirm the tool's current version, supported test setup and result interpretation in the GitLab documentation.
Monitor Gitaly and Sidekiq queues in production
After go-live, your operations team watches Gitaly latency and error signals, repository storage utilization, and Sidekiq queue depth and job latency through the Prometheus metrics that GitLab exposes. Set alert thresholds based on the load test baseline, not on generic values. The check is a dashboard and alert set in place for at least one full peak cycle, with a documented review owner. Confirm the metric names and monitoring setup for your version in the GitLab monitoring documentation, since they change between releases.
Scale down only after sustained evidence
Your capacity owner considers reducing node sizes only after production data shows headroom across several peak cycles, including the month-end and release windows identified in the first step. Make one change at a time, rerun the load test after each change, and keep the previous configuration available for a rollback. The step is done when the reduced size passes the load test, production metrics stay within the agreed thresholds, and the decision is recorded with the evidence. Starting large and reducing later is safer than the reverse, because a shortfall during a release is harder to recover from than an extra node in the bill.
Security and operational considerations for large repositories
Large monorepos and heavy CI concentrate risk in a few places: the runners that execute untrusted jobs, the Gitaly data that holds every repository, and the upgrade and maintenance windows that touch the whole instance. The cards below are the checks to settle alongside sizing, not after go-live.
Isolate runners from sensitive repositories
Runners execute whatever jobs their projects send them, so a runner that serves a monorepo with broad access can also reach secrets and code it should not. Plan separate runner fleets or tagged pools for sensitive projects, and decide which executor each pool uses before scaling CI concurrency. Ask your partner or team to confirm how runner isolation, registration tokens and job networking will be enforced in your environment, using the GitLab Runner documentation as the reference.
Confirm Gitaly backup coverage
Gitaly holds the repository data that grows with a monorepo, and backup coverage at that size is easy to assume rather than verify. Confirm in the backup and restore documentation which Gitaly data the backup task captures, how long a full restore takes at your repository size, and whether your storage layer offers snapshots. Gitaly Cluster does not offer snapshot backups, so the restore procedure must be tested end to end.
Plan for upgrade stops on large instances
GitLab requires upgrades to pass through required upgrade stops, and on a large instance each stop adds migration time, background job drain and a longer rollback window. Map your current version against the stops before scheduling work, and ask whether your topology supports the zero-downtime procedure for each step. Budget test upgrades on a copy of production data, since migration duration on a monorepo-heavy database is the main unknown.
Monitor and schedule maintenance windows
Heavy CI concurrency hides saturation: queues grow, Gitaly latency rises and Sidekiq backs up before anyone sees an outage. Define the health checks, Prometheus metrics and log sources you will watch for Gitaly, Sidekiq and the runner fleet, and set alert thresholds from your own baseline. Agree maintenance windows with CI teams in advance, because any restart or upgrade stops pipelines across every monorepo project at once.
Roll out hardening gradually
GitLab's hardening guidance covers application settings, CI/CD, gitlab.rb, the operating system and NIST SP 800-53 alignment, and GitLab describes it as a work in progress to be rolled out gradually. Treat each control as a change with its own test, owner and rollback. Record which settings you adopted, which you deferred and why, so the evidence matches what runs in production when an assessor or internal auditor asks.
Questions teams ask before scaling GitLab for monorepos
How much does it cost to scale self-managed GitLab for a large monorepo?
Cost has three components: infrastructure, licensing and implementation effort. Infrastructure is the compute, memory, storage and network your cloud account or data center bills, and it recurs every month. Licensing depends on the tier you choose: the GitLab pricing page lists GitLab Free at 0 US dollars, Premium at 29 US dollars per user per month billed annually, and Ultimate as custom priced, with all three available for self-managed installations. Implementation effort is usually one-time, covering sizing, build, load testing and cutover. Ask any partner for a quote that separates these lines, states which tier is assumed, and lists the recurring infrastructure items the estimate excludes, such as storage growth and runner capacity.
How long does sizing and validating a monorepo deployment take?
The duration depends on how much measured data exists before work starts. The main drivers are the size and shape of the largest repositories, how many CI jobs run at peak, whether you already have a reference architecture running, how many environments need validation, and how quickly your change windows and approvals move. Teams with clean metrics from Gitaly, Sidekiq and their CI system usually move faster than teams who must first instrument their systems. Load testing adds time because each test run needs a stable environment and a review of results. Ask for a timeline built from these steps in the written scope, not a generic number.
Can our own team size and validate a monorepo GitLab deployment without a partner?
Yes, many teams do, provided they have engineers who understand Linux administration, PostgreSQL, Redis, Gitaly storage behavior and CI runner design. They also need a way to generate realistic load. GitLab publishes reference architectures with request-per-second targets, and the GitLab Performance Tool can test an environment against those targets before go-live. Your team should be able to read the results, find the bottleneck, and change configuration with a rollback plan. If any of these skills are thin, a partner can cover the gaps, but the decision should rest on the skills you have today and the change windows you can protect.
Should we start with a large GitLab architecture and scale down later?
Starting larger can reduce the risk of a painful resize during a busy period, but it raises recurring infrastructure cost and can leave capacity idle. Starting smaller is cheaper at first but requires a tested path to add nodes, rebalance repositories and move stateful services without long outages. The right choice depends on how fast your monorepos and CI concurrency are growing and how much headroom you can pay for. A common approach is to size against measured peak load, document the scale-up steps in advance, and run a load test at the planned size before committing to a smaller footprint.
Is Gitaly Cluster required for a large monorepo?
No. Gitaly Cluster with Praefect is one option for repository-level high availability, and the GitLab reference architectures generally recommend a high availability strategy at 3,000 or more users. That recommendation is about availability, not repository size alone. Sharded Gitaly is an alternative that spreads repositories across nodes. Whichever you choose, large repositories need fast disks, enough memory for Git operations and careful monitoring of repository growth. Confirm current Gitaly Cluster limitations with GitLab before choosing it, since the documentation covers backup behavior and other constraints that affect the design.
What do we need to provide so a partner can size a monorepo deployment?
Provide repository metrics, CI data and operational constraints. For repositories, that means the size of the largest repositories, total storage, growth over recent months, the count of branches and tags, and typical clone and fetch frequency. For CI, provide job volume by hour, peak concurrency, average and longest job durations, the executors in use and the largest pipelines. You should also share current user counts, API request patterns if known, existing monitoring data, backup and recovery targets, and the change windows available for cutover and load testing. Missing data is normal, but the quote should say which assumptions were made.
What should a written quote for monorepo scaling include?
A useful quote lists the scope in numbered deliverables: current-state review, target reference architecture, infrastructure design, configuration of Gitaly, PostgreSQL, Redis, Sidekiq and runners, load testing against stated targets, cutover support, and documentation. It should name the GitLab tier and version assumed, state which environments are covered, and identify what the client provides, including data, access and change windows. It should separate one-time implementation fees from recurring items such as retainers or support. Finally, it should state exclusions, such as cloud spend, licensing your organization buys directly from GitLab, and any work not reflected in the agreed deliverables.
How DeployOpen scopes and supports GitLab sizing work
DeployOpen plans, implements, tests, hardens and supports self-managed GitLab inside your own infrastructure, whether that is your cloud accounts, your data centers or an air-gapped network. For sizing work on large monorepos, that means we start from your measured repository sizes, CI concurrency and request patterns, then map them to a reference architecture and a Gitaly layout before any hardware or cloud resources are ordered. Most engagements are fixed-scope, with the scope, deliverables and timelines agreed in writing before work starts. The written scope should name the environments covered, the load test targets, the documents handed over and the items the client must provide, such as access, network diagrams and current repository statistics.
Pricing follows from that written scope. DeployOpen does not publish a price list, so the quote reflects the components in scope, the number of environments, and how much validation the client wants. When you request a quote, ask the vendor-independent questions that drive cost: how many environments must be sized (production, staging, disaster recovery), whether load testing is included and against which targets, whether the team needs knowledge transfer, and which recurring items are excluded. Ask for each item to be marked as one-time or recurring so the total cost of ownership is visible. Confirm current GitLab license tier requirements directly with GitLab's pricing page because feature availability affects scope.
Ongoing support is available as a retainer for the period after go-live. In practice this covers monitoring review, reviewing Gitaly and runner capacity as monorepo growth continues, planning upgrades through required upgrade stops, and re-running performance checks after significant changes. Agree in writing what the retainer includes, how requests are prioritized, and whether scaling changes are covered or billed separately. DeployOpen does not issue audit reports, attestations or certifications. If your organization needs independent assurance of its GitLab environment, that evidence comes from an independent auditor or certification body, and our role is to prepare the systems, configuration and records they will review.

