Source: Dev.to | Dec 17, 2025 (UTC)
Migrating thousands of repositories can feel like one of those nightmares that wake engineers up in the middle of the night. But for me, it became one of the most transformative experiences of my journey with DevOps and automation.
This article is an honest and practical report of how I automated the migration of approximately 4,000 GitLab Community Edition projects to GitLab Enterprise Edition — without disrupting teams, without losing history, and without breaking production pipelines.
It is a story about chaos, discipline, automation, and, above all, how errors are an essential part of the process.
Why This Migration Needed to Happen
GitLab Community Edition works well in the beginning. But as teams grow, repositories multiply, and CI/CD volume explodes, clear limitations appear:
- lack of governance
- inconsistent configurations
- heterogeneous runners
- unstable pipelines
- absence of essential enterprise features
Migrating to GitLab Enterprise Edition was more than a technical improvement.
It was a step toward organizational maturity.
The challenge?
There is no official migration process that preserves everything correctly.
With no alternative, I had to create a custom solution.
What Makes This Migration So Complex?
Moving 4,000 projects is not just moving folders.
It means preserving an entire ecosystem, including:
- full commit history
- protected branches
- release tags
- variables and secrets
- scattered CI/CD includes
- issues and comments
- archived-state preservation
- runners and permissions
GitLab’s native import/export tool breaks large parts of this along the way.
I needed automation capable of migrating everything — reliably.
The Toolkit That Built the Bridge
I developed a modular set of Bash scripts to maintain absolute control over each part of the migration.
It is publicly available here:
👉 https://github.com/clcesarval/migrar-gitlab
The architecture had two clear layers:
1. The Git Layer
Responsible for:
- cloning all repositories
- reconstructing branches
- ensuring DAG integrity
- preserving tags exactly as in CE
- reconfiguring remotes
- restoring archived state
2. The API Layer
- Responsible for:
- variables and secrets
- issues and comments
- group hierarchies
- permissions
- recreating projects in the destination
Each script did only one thing — but did it precisely.
Some key components:
- clone-projects.sh
- replace_gitlab-ci.sh
- push_projects.sh
- migrar-variaveis.sh
- migrate-issues.sh
- recursive scripts for subgroups
- complete runner inventory
It was purposeful automation — not magic.
The Hidden Chaos That Appeared
The official documentation helped, but only partially. Many sections were inconsistent, incomplete, or simply failed when applied at large scale.
I also identified issues such as:
- CI includes using absolute paths that would break after migration
- duplicated or unsafely scoped variables
- runners with unpredictable configurations
- archived projects reappearing as active when using import/export
- tags being incorrectly recreated
Using the official process could have caused severe operational impact across the organization.
Pilot Tests… and the Errors That Saved the Migration
To ensure everything would work, I ran several pilot tests in isolated environments.
And all of them — without exception — failed at first.
I faced:
include: /old-group/subgroup/template.yml
Enter fullscreen mode
Exit fullscreen mode






Залишити відповідь