Evidence ledger
One row per claim in The monolith that could not be split: ten years of GitLab architecture, 2016-2026: who published it, what grade it carries, when it was written, when the link was last checked, and the quote or figure it rests on. Nothing in the guide is cited from memory, so anything not in this table is not in the guide.
Topic: how GitLab re-architected a single Rails monolith between 2016 and 2026 while shipping the same product as a hosted service and as software customers run themselves.
Research date: 2026-09-06. Every row was fetched in this session. Handbook and repository rows were
fetched through the public GitLab REST API (https://gitlab.com/api/v4/projects/<id>/repository/files/<path>/raw?ref=<ref>)
and are cited at their human-readable blob URL, which is the same file at the same ref. Incident
rows were fetched at https://gitlab.com/api/v4/projects/7444821/issues/<iid> and are cited at the
web_url the API returns.
Access constraint on this dig. The session ran under a network egress policy that permitted
gitlab.com and github.com and refused every other host, including engineering blogs, USENIX,
arXiv and the vendors' own status pages. Nothing here is cited from memory; instead the guide is
built entirely from first-party primary artefacts (design documents, architecture decision records,
incident reviews, runbooks, merge requests). What that excludes is stated in the guide: no
conference talks, no papers, no third-party or competitor accounts, and no independent measurement
of any figure GitLab publishes about itself.
Project ids used: 278964 gitlab-org/gitlab, 42817607 gitlab-com/content-sites/handbook,
2009901 gitlab-org/gitaly, 1148549 gitlab-com/runbooks, 7444821 gitlab-com/gl-infra/production.
| # | Org | Title | Tier | Published | Checked | URL | Claim taken from it | Supporting quote or figure |
|---|---|---|---|---|---|---|---|---|
| 1 | GitLab | Gitaly design document | adr | 2016 onward (living doc) | 2026-09-06 | https://gitlab.com/gitlab-org/gitaly/-/blob/master/doc/DESIGN.md | The original reason for Gitaly was to stop Git running against network storage | "This was, in fact, the first design reason for Gitaly - to avoid having the Git binary operate on remote storage." |
| 2 | GitLab | Gitaly design document | adr | 2016 onward | 2026-09-06 | https://gitlab.com/gitlab-org/gitaly/-/blob/master/doc/DESIGN.md | The trigger was a filesystem-bound latency measurement, not a throughput target | "our P99 spikes up to 30 wall seconds, while the CPU time keeps in the realm of the 15 milliseconds. Pointing at filesystem access as the culprit." |
| 3 | GitLab | Gitaly design document | adr | 2016 onward | 2026-09-06 | https://gitlab.com/gitlab-org/gitaly/-/blob/master/doc/DESIGN.md | Active-active Git was deferred at the outset, and replication was explicitly out of scope | "Use Git ketch to allow active-active ... This is far in the future"; "To maintain the focus of the project, the following subjects are out-of-scope for the moment: 1. Replication and high availability (including multi-master and active-active)." |
| 4 | GitLab | Gitaly README | vendor | living doc | 2026-09-06 | https://gitlab.com/gitlab-org/gitaly/-/blob/master/README.md | All Git access now goes through the RPC layer; clustering is optional | "All application code accesses Git repositories via Gitaly." |
| 5 | GitLab | Scaling Git design document | adr | 2026-06-16 | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/scaling-git/_index.md | Praefect (Gitaly Cluster) is judged a failure by the team that built it | "While we tried to address this issue with read distribution via Praefect, that effort has basically failed to yield a horizontally scalable cluster due to various issues."; "This solution is thus treated as a failed design." |
| 6 | GitLab | Scaling Git design document | adr | 2026-06-16 | 2026-09-06 | (same as 5) | Why Praefect failed, in four specific mechanisms | "The use of Postgres results in multiple sources of truth"; "Transactional voting has proven to be unreliable in practice"; "Writes need to happen on all nodes at once"; "replication runs that are never able to catch up with concurrent writes." |
| 7 | GitLab | Scaling Git design document | casestudy | 2026-06-16 | 2026-09-06 | (same as 5) | The per-repository ceiling is memory, not CPU | "git-pack-objects for a gitlab-org/gitlab clone holds ~4–6 GB of anonymous memory, capping in-flight clones per node regardless of CPU." |
| 8 | GitLab | Scaling Git design document | adr | 2026-06-16 | 2026-09-06 | (same as 5) | Networked filesystems were the generation before Praefect and were rejected for latency and POSIX edge cases | "Before we used Praefect we used networked filesystems ... the increased access latency has proven to be a challenge." |
| 9 | GitLab | Gitaly MR 7312, "Document steps for Praefect-to-Raft migration" | source | opened 2024-09-30, closed unmerged | 2026-09-06 | https://gitlab.com/gitlab-org/gitaly/-/merge_requests/7312 | A Praefect-to-Raft migration path was drafted and closed without merging | Merge request state closed, merged_at: null. |
| 10 | GitLab | CI/CD Scaling design document | casestudy | created 2021-01-21, approved 2021-04-26 | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/ci_scale/_index.md | A 32-bit primary key was hours of arithmetic away from taking the site down | "In early 2021 we had estimated that would run out of the capacity of the integer type to store primary keys in ci_builds table before December 2021. If it had happened without a viable workaround or an emergency plan, GitLab.com would go down." |
| 11 | GitLab | CI/CD Scaling design document | casestudy | 2021 | 2026-09-06 | (same as 10) | Build volume and the growth curve | "we are creating more than 5 million builds each day on GitLab.com"; "On February 1st, 2021, GitLab.com surpassed 1 billion CI/CD builds created. In February 2022 we reached 2 billion"; goal "making processing 20M builds in a day possible." |
| 12 | GitLab | CI/CD Scaling design document | casestudy | 2021 | 2026-09-06 | (same as 10) | The table size fed back into development velocity, not just latency | "Our static analysis tools prevent adding more columns to this table."; "we are at the limits of vertical scaling of the CI primary database nodes." |
| 13 | GitLab | CI/CD Scaling design document | adr | 2021 | 2026-09-06 | (same as 10) | Redis was evaluated and rejected for build queuing in favour of a dedicated Postgres queue table | "We evaluated using Redis queuing, or a separate table that would accelerate SQL queries used to build queues. We decided to proceed with the latter." |
| 14 | GitLab | Database Scalability: limit table size to <100 GB | casestudy | 2021-06-23, status accepted | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/database_size_limits/_index.md | The largest table was 2,975 GB and 22.7% of the database | Table row: ci_builds total 2975 GB, table 1551 GB, index 941 GB, 30 indexes, 22.7% of total database size, mid-June 2021. |
| 15 | GitLab | Database Scalability: limit table size to <100 GB | casestudy | 2021-06-23 | 2026-09-06 | (same as 14) | Table size becomes a deployment constraint through index builds and vacuum | "in 2021, we see B-Tree creation take up to 6 hours for a single B-Tree index"; "index creation time varies between 1.5 to 6 hours during busy times. This process blocks deployments"; GIN trigram reindex on notes "aborted after 12 hours upon first try". |
| 16 | GitLab | Database Scalability: limit table size to <100 GB | adr | 2021-06-23 | 2026-09-06 | (same as 14) | The 100 GB target is a pragmatic operational limit, with an explicit act-at-10 GB rule | "we target a table size less than 100 GB for a physical table on GitLab.com (including its indexes)"; "start action to reduce table sizes when the table is around 10 GB." |
| 17 | GitLab | CI/CD data time decay design document | adr | 2021-09-10 | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/ci_data_decay/_index.md | Queue partitioning uses a product rule, not a storage rule | "builds created more 24 hours ago need to be removed from the queue. This business rule is present in the product since the inception of GitLab CI." |
| 18 | GitLab | CI mirrored tables runbook | source | living doc | 2026-09-06 | https://gitlab.com/gitlab-com/runbooks/-/blob/master/docs/decomposition/ci-mirrored-tables.md | Splitting the database converted two foreign keys into a monitored replication problem | "namespaces and projects that reside on the main database, are partially copied to their corresponding gitlab_ci database tables ci_namespace_mirrors and ci_project_mirrors ... This even happens on single database mode."; runbook exists for "alerts about inconsistent records". |
| 19 | GitLab | Sidekiq sharding runbook | source | living doc | 2026-09-06 | https://gitlab.com/gitlab-com/runbooks/-/blob/master/docs/sidekiq/sharding.md | Redis Cluster was rejected for Sidekiq; sharding is done in the application | "Redis is not horizontally scalable and Redis Cluster is not suitable for Sidekiq since there are only a small subset of hot keys."; "The recommended setup is to have 1 queue per shard." |
| 20 | GitLab | Runbooks docs/ tree |
source | read 2026-09-06 | 2026-09-06 | https://gitlab.com/gitlab-com/runbooks/-/tree/master/docs | The shared tier is now four Postgres clusters and roughly twenty Redis estates | Directory listing contains patroni, patroni-ci, patroni-registry, patroni-sec, matching pgbouncer* directories, and 20 redis* / redis-cluster-* directories plus memorystore*. |
| 21 | GitLab | Container registry metadata database design document | casestudy | 2020-09-29, status implemented | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/container_registry_metadata_database/_index.md | Metadata in the blob store forced offline garbage collection | "the registry needs to be either shutdown or set to read-only, remaining like that during the whole GC run"; "may require multiple hours/days to complete ... not feasible for platforms with tight availability requirements." |
| 22 | GitLab | Container registry metadata database design document | adr | 2020-09-29 | 2026-09-06 | (same as 21) | Feature work was frozen until the storage model changed | "we decided to freeze the development of new features until we have a solution in place to overcome all these foundational limitations." |
| 23 | GitLab | ClickHouse usage design document | adr | 2023-02-02 | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/clickhouse_usage/_index.md | Analytics moved off the OLTP database, but only for the hosted product at first | "As of FY24 Q3 ClickHouse is available for use with GitLab.com."; "We did not move forward attempting to integrate with self managed at this time due to the uncertain costs and management requirements for self-managed instances." |
| 24 | GitLab | Siphon design document | adr | 2024-11-20 | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/siphon/_index.md | They built their own Postgres CDC because the established tools fail the self-managed test | "Debezium requires Kafka (this can be a problem for self-managed instances) and brings in a lot of new dependencies."; PeerDB "was recently acquired by ClickHouse"; Airbyte "no support for schema changes." |
| 25 | GitLab | Siphon design document | casestudy | 2024-11-20 | 2026-09-06 | (same as 24) | Analytical load on the OLTP database has a production-incident history | "running analytical workloads in our OLTP database has led to numerous production incidents over the years"; "Currently, we have at least 3 distinct methods to synchronize data from PostgreSQL to ClickHouse." |
| 26 | GitLab | Next Runner Auto-scaling Architecture | adr | 2022-01-19 | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/runner_scaling/_index.md | CI autoscaling depended on an abandoned upstream for years, then on a fork | "Work on that project was paused in July 2018"; "In 2018 ... we decided to create our own fork"; "On September 26th, 2021 the project got archived"; "one of its most important features ... depends on an external product that is abandoned." |
| 27 | GitLab | Next Runner Auto-scaling Architecture | casestudy | 2022-01-19 | 2026-09-06 | (same as 26) | Peak build volume, and the platform gap the dependency created | "we now run around 4 million builds per day at peak"; "only one of three of our officially supported platforms - Linux, Windows and MacOS - have a fully-featured support for CI/CD auto-scaling." |
| 28 | Docker | docker/machine issue 4537, now docker-archive-public/docker.machine | source | 2018-07-13; repo archived 2025-07-18 | 2026-09-06 | https://github.com/docker-archive-public/docker.machine/issues/4537 | The upstream abandonment is confirmed outside GitLab | "we're officially closing the faucet for non-bugfix changes, starting today." Repository header: "This repository was archived by the owner on Jul 18, 2025. It is now read-only." |
| 29 | GitLab | Cells design document | adr | created 2022-09-07, read 2026-09-06 | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/cells/_index.md | All three cell iterations are on hold and replaced | "(On hold) The Cells 1.0 ... (On hold) The Cells 1.5 ... (On hold) The Cells 2.0"; "Protocells replaces Cells 1.0, Cells 1.5, and Cells 2.0 with a new focus of permanently reducing load on the database." |
| 30 | GitLab | Cells design document, decision log | adr | 2022-09-07 onward | 2026-09-06 | (same as 29) | Twenty-eight numbered ADRs exist for the cell architecture, including a reversal on disaster recovery | Decision log lists ADR-001 to ADR-028, including "ADR 006: Use Geo for Disaster Recovery" and "ADR-024: Use Backup and Restore for Disaster Recovery". |
| 31 | GitLab | Cells 1.0 iteration document | adr | living doc | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/cells/iterations/cells-1.0.md | The first cell targeted internal users only, and cells may run different versions | "The phase names Cells 1.0, 1.5, and 2.0 are replaced by Protocells."; "Cells need to be able to run independently with different versions."; "All identifiers ... are unique across the cluster, so that we can perform logical re-balancing at a later time." |
| 32 | GitLab | Protocells design document | adr | 2026-02-15 | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/protocells/_index.md | The re-scoped programme names GitLab.com itself the legacy cell and drops four earlier promises | "GitLab.com operates as a single monolithic instance with a shared database, which creates a fundamental scalability bottleneck."; non-goals include "Regional compliance", "Multi-Cloud support", "Organization load re-balancing", "Global Admins". |
| 33 | GitLab | Cells ADR-001, routing technology | adr | living doc | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/cells/decisions/001_routing_technology.md | Routing runs on Cloudflare Workers, with the self-managed gap accepted explicitly | "Cloudflare Workers meets all our requirements apart from the self-managed, which is a low priority requirement."; "More vendor locking with Cloudflare, but are already heavily dependent on them."; Istio, request buffering and routes learning all considered and rejected. |
| 34 | GitLab | Cells ADR-009, initial cell sizes | adr | living doc | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/cells/decisions/009_cell_initial_sizing.md | Resizing a cell's stateful services is expected to need downtime | "we might need to be scale it from 25k to 50k which can result in downtime because we need to resize stateful services like Gitaly, Database, and Redis ... for Dedicated on AWS this is around 20-40 minutes." |
| 35 | GitLab | Cells ADR-026, Hono for the HTTP router | adr | 2026-02-20 | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/cells/decisions/026_hono_for_http_router.md | The hand-rolled routing rule engine is being replaced two years in, progressively | "the custom rule engine applies rules one after another in a linear manner, which becomes increasingly inefficient as the number of rules grows."; "falling through to the custom engine for rules not yet migrated." |
| 36 | GitLab | Rejected proposal: stateless router with buffering requests | adr | living doc (rejected) | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/cells/rejected/proposal-stateless-router-with-buffering-requests.md | A full alternative routing design is preserved in a rejected/ directory |
Document lives under cells/rejected/ and is cited as rejected by ADR-001. |
| 37 | GitLab | cells/http-router project | source | created 2024-02-15, active 2026-09-04 | 2026-09-06 | https://gitlab.com/gitlab-org/cells/http-router | The routing service is a real, actively developed component, not a diagram | Project metadata: created_at 2024-02-15, last_activity_at 2026-09-04. |
| 38 | GitLab | Rails monolith decomposition design document | adr | created 2023-05-22, status proposed | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/modular_monolith/_index.md | Three years on, decomposition is still a proposal, now re-argued for agents | Frontmatter status: proposed, creation-date: "2023-05-22"; section "Why now: the agentic imperative". |
| 39 | GitLab | Rails monolith decomposition design document | adr | 2023-05-22 onward | 2026-09-06 | (same as 38) | Data coupling, not code coupling, is what blocks extraction | "over 2.2 million lines of Ruby code"; "Boundaries are silent on the database. Even apparently isolated domains share tables, hold cross-boundary foreign keys, and keep ORM associations that span the line. Data coupling is what actually blocks extraction." |
| 40 | GitLab | Organization design document | adr | 2023-04-05 | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/organization/_index.md | The tenancy boundary is a product change, not only an infrastructure one | "The GitLab.com platform will be split into two distinct experiences."; "These enterprise Organizations will operate in complete isolation from all other Organizations, including the default organization." |
| 41 | GitLab | Object storage direct upload consolidation | adr | 2021-11-18, status accepted | 2026-09-06 | https://gitlab.com/gitlab-com/content-sites/handbook/-/blob/main/content/handbook/engineering/architecture/design-documents/object_storage/_index.md | Object storage handling was consolidated rather than left per-feature | Frontmatter status: accepted; sections "Enable direct upload by default on every upload", "Simplify object storage code". |
| 42 | GitLab | Gitaly administration documentation | vendor | living doc | 2026-09-06 | https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/administration/gitaly/_index.md | Network storage for repositories is now unsupported, the end state of the 2016 decision | "For repository data, only local storage is supported for Gitaly and Gitaly Cluster (Praefect) for performance and consistency reasons. Alternatives such as NFS or cloud-based file systems are not supported." |
| 43 | GitLab | NFS documentation | vendor | living doc | 2026-09-06 | https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/administration/nfs.md | The same rule stated from the other side | "NFS cannot be used for repository storage." |
| 44 | GitLab | Incident review, 2022-11-30 site-wide outage | postmortem | 2022-11-30 | 2026-09-06 | https://gitlab.com/gitlab-com/gl-infra/production/-/work_items/8100 | A deleted Consul release took down the Postgres HA layer and the whole site | "~100% of traffic"; "Our CDN reports ~13M requests that resulted in 503"; "it resulted in performing a change to production that took down a core component (Consul) of our Postgres HA solution (Patroni)." |
| 45 | GitLab | Incident review, Redis primary CPU saturation | postmortem | 2026-05-20 | 2026-09-06 | https://gitlab.com/gitlab-com/gl-infra/production/-/work_items/22170 | One worker class saturated a shared Redis shard, and the mitigation amplified it | "audit event jobs were dropped globally" for "3 hours, 7 minutes"; "Redis memory grew from normal levels to 35 GiB before intervention"; "deferring ... jobs caused sidekiq itself to re-enqueue more of them, amplifying the problem"; "a near-miss repeat of the S1 incident from one week earlier." |
| 46 | GitLab | Incident review, Sidekiq queueing SLO violation | postmortem | 2026-03-11 | 2026-09-06 | https://gitlab.com/gitlab-com/gl-infra/production/-/work_items/21520 | Long-lived transactions in background jobs exhausted the connection pool and blocked deploys | "PgBouncer saturation ... caused by Sidekiq workers such as MergeRequestMergeabilityCheckWorker and WebHooks::LogExecutionWorker holding long-lived database transactions"; "Deployments and feature flags were blocked"; total duration "8 hours, 24 minutes". |
| 47 | GitLab | Incident review, paused pgBouncer in Patroni sec cluster | postmortem | 2025-07-10 | 2026-09-06 | https://gitlab.com/gitlab-com/gl-infra/production/-/work_items/20154 | A maintenance playbook timing out left the pooler in a paused state | "Customers Affected 17,383"; "445,621 HTTP Git requests, 59,777 Sidekiq jobs"; "PgBouncer nodes of the sec Patroni cluster being stuck in a paused state"; "the playbook timed out during that step while waiting for the yubikey." |
| 48 | GitLab | Incident review, slow Gitaly operations | postmortem | 2026-03-24 | 2026-09-06 | https://gitlab.com/gitlab-com/gl-infra/production/-/work_items/21629 | A single storage node still decides availability for the namespaces on it | "A single Gitaly storage node ... experienced a sharp spike in anonymous memory usage"; "After rebooting the node as mitigation, it failed to mount its data disk"; "All requests within gitlab-org namespace"; "3 hours, 28 minutes". |
| 49 | GitLab | Incident review, Google Cloud outage | postmortem | 2025-06-12 | 2026-09-06 | https://gitlab.com/gitlab-com/gl-infra/production/-/work_items/19996 | A provider control-plane failure hit every storage-dependent feature at once | "Severity 1"; "2 hours, 56 minutes"; affected "Git operations ... GitLab Pages (complete outage) ... Container Registry ... CI/CD runners ... LFS, artifacts, traces"; "the EOC receiving 17+ pages within minutes." |
| 50 | GitLab | Incident review, 2025-12-05 GitLab.com down | postmortem | 2025-12-05 | 2026-09-06 | https://gitlab.com/gitlab-com/gl-infra/production/-/work_items/20942 | The CDN and edge layer is a total-outage dependency, and the cell router now lives there | "All SaaS users"; "~155 million requests"; "25 minutes"; "A widespread Cloudflare outage prevented access to GitLab.com"; "taking follow-up actions to reduce Cloudflare dependency." |
Gaps in this evidence base
- No papers, talks, or third-party accounts. Blocked by the egress policy, not by absence in the public record. Any claim here about how GitLab compares to peers would be unsupported, so the guide makes none.
- No cost figures. GitLab publishes cloud spend in aggregate financial reporting, which was not reachable, and no design document in this corpus states a dollar figure for any component.
- No independent measurement. Every number is GitLab measuring its own system. They are internal operational figures rather than marketing claims, which makes them credible but not independent.
- Incident reviews start in late 2022. The
incident-reviewlabel's earliest issues ingitlab-com/gl-infra/productionare from October 2022, so the failure catalogue cannot cover 2016 to 2021 from primary incident material; design documents are the only witness for that half of the decade.