Case study
GitLab Consolidation: Five Source Systems Into One Instance
- GitLab
- DevOps
- Migration
- DevSecOps
A regional insurance company was running its source code across five platforms — spread over more than five separate instances, all self-managed or self-hosted: GitHub, GitHub Enterprise, GitLab CE, GitLab EE hosted on Google Cloud, and Azure DevOps. Aqvantiq consolidated them onto a single GitLab instance — over 1,000 repositories in total, 192 of them complex — migrated by Direct Transfer in waves of 200 to 300, with near-zero disruption to business-as-usual delivery.
Five source systems meant five of everything else
The estate had grown the way estates do: a team picked a platform, a project inherited one, a vendor brought another, and none of them were ever turned off. By the time we were called in there were more than five instances, spanning five platforms across two vendors plus Azure DevOps, some self-managed and some hosted on Google Cloud.
The goal was stated in two parts. The first was management: one platform to run, patch, back up, license and grant access to, instead of five. The second was DevSecOps, and it is the part that makes the business case. A scanning policy, a branch-protection standard, a secrets rule or a pipeline-security control is only worth writing once — and it can only be written once when there is one platform to write it on. Across five, every control is either duplicated and left to drift, or lives only on the platform with the loudest owner.
The migration ran in waves of 200 to 300 repositories
Nobody moves a thousand repositories in one window, and the projects that try are the ones that end up with a rollback plan nobody has rehearsed. The migration was chunked into waves, each carrying 200 to 300 repositories and taking about eight hours to run.
For waves sourced from the GitLab instances, those eight hours are a freeze. Developers cannot push, and the pipelines are frozen for the duration. That is a genuine constraint and there is no clever way around it — so the answer was scheduling rather than cleverness. GitLab-source waves ran outside working hours and over weekends, which put the freeze where nobody was pushing anyway. Across every wave, the impact on business-as-usual delivery was close to zero.
Wave sizing is doing real work here. Two to three hundred repositories is small enough that a wave fits inside one off-hours window with room to verify, and large enough that the work does not stretch across so many weekends that the team loses the thread. Get that number wrong in either direction and you either overrun into Monday morning or you are still migrating six months later.
Non-GitLab sources moved as a development copy, then a delta
The GitHub and Azure DevOps repositories could not be handled the same way — and it turned out they did not need to be. Their pipelines were not GitLab pipelines, so they had to be refactored rather than transferred, and refactoring is the slow part of any consolidation.
So those sources were moved in three steps:
- The repository is migrated into GitLab as a development copy. The source system stays live and its pipelines keep running exactly as before.
- CI/CD is refactored on the GitLab side against that copy, and tested there until it passes properly, not until it looks plausible.
- A delta migration brings across everything that changed in the meantime, and the cutover happens.
For the non-GitLab CI/CD, that produced zero downtime. Pipelines kept running on the source system right up to the moment of cutover, which means the pipeline refactoring — the part that takes weeks and cannot be rushed — happened entirely off the critical path. Teams found out their new pipeline worked before they were asked to depend on it.
The numbers behind the consolidation
| Measure | Result |
|---|---|
| Source platforms consolidated | Five — GitHub, GitHub Enterprise, GitLab CE, GitLab EE on Google Cloud, Azure DevOps — across more than five instances |
| Repositories migrated | Over 1,000 |
| Complex repositories | 192 |
| Wave size | 200–300 repositories |
| Wave duration | About 8 hours |
| BAU impact, GitLab sources | Near-zero — code and pipeline freeze scheduled off-hours and at weekends |
| BAU impact, GitHub and Azure DevOps | Zero downtime — pipelines ran on the source until cutover |
One migration path failed, and the fix was an ordering change
Not every wave was uneventful. One path through the standard tooling failed repeatedly, for hours, without returning a usable error code. Token scopes, the migrating user’s permissions on the target top-level group, name collisions with existing groups and a trial into a different subgroup were all checked, and all came back clean.
The fix was an ordering change rather than a permissions one: migrate the group structure first, fully created with its settings and CI/CD variables re-applied, then migrate the projects into groups that already exist. It then worked without issue. The full diagnosis is written up as the GitLab Direct Transfer failure that a groups-first migration fixed.
What made this land without a BAU outage
Three things, none of them technically exciting.
The wave was sized to a window, not to an ambition. The freeze was scheduled where the business was not working, rather than negotiated with people who were. And the sources whose pipelines had to be rebuilt were given a development copy to be rebuilt against, so the risky work happened while the old system was still carrying production.
The engagement was managed by Anson Lau, the founder, and delivered by the same group of engineers across every wave — the engineer who scoped the migration stayed accountable for it through cutover, which across this many waves matters more than any single technique in it.
Consolidating repositories, running or migrating self-managed GitLab instances, and building the pipelines that go on top is the work described on DevOps as a Service. The Thai side of the same practice, delivered by the same group of engineers, is on DevOps as a Service in Thailand.