What a GitLab migration involves
A GitLab migration moves source repositories, merge requests, issues, users and CI configuration from GitHub, Bitbucket Cloud or Server, Gitea, another GitLab instance, or plain Git repositories reached by URL or manifest, into a self-managed GitLab instance that your organization runs in its own infrastructure. The work is rarely one copy operation. Each source system exposes different objects to the importer, each has its own identity model, and each needs a cutover plan that tells developers where work now happens. Teams usually start this work for one of four reasons: a licensing or hosting policy that rules out a hosted service, data residency requirements, consolidation of several tools into one, or the retirement of an existing instance.
The GitLab importers do not carry the same things across. Repository history and branches generally move, but the set of merge request discussions, issue metadata, attachments, CI variables and pipeline history that come across depends on the source and the import method. Direct transfer between GitLab instances carries more than URL-based Git imports, and Subversion needs an external conversion step before import. Before committing to a scope, the migration lead should run a test import of a representative project from each source and compare what arrived against a written inventory. Confirm the current import matrix for each source in the GitLab import documentation, because it changes between releases.
A small team with one or two sources and modest history can often run a pilot import itself, provided it has an administrator who can run imports and a clear rollback plan. Multi-source estates, large histories, identity mapping that ties old authors to new accounts, and regulated cutovers with audit evidence are usually scoped with a partner. In those cases the hard work is in the decisions: which contributions are mapped to which people, how credentials and CI secrets are re-entered, what freezes on the old system during the cutover window, and who signs off that the new instance is the system of record.
What the migration includes
The migration runs as a set of work packages, each with a defined output. The exact set of data that carries over depends on the importer or transfer method used, so confirm it per source before the plan is final.
Source inventory and mapping
We catalog repositories, branches, tags, large files, merge requests, issues, wikis, CI configurations and integrations in the source system, then decide which move, which are archived and which are retired. The output is a mapping sheet that ties each source project to a target path, group and owner. Check early whether any repositories depend on hosted-only features you will not have after the move.
Target instance preparation
Before any data moves, we configure the self-managed GitLab instance with the import settings you need, the group and namespace structure, and a permissions model that mirrors your access policy. Decide early who may create top-level groups and who may import, because these settings shape everything that follows. Confirm that sign-up and import restrictions match your security baseline.
Repository and history transfer
Repositories move with the importer that matches the source, such as the GitHub, Bitbucket or Gitea importer, or a direct transfer between GitLab instances. Each method has its own limits on what history, refs and attachments arrive intact. We run test imports on representative projects and compare commit counts, branches and tags against the source before any bulk run.
Merge request, issue and wiki mapping
Review history, issue threads and wiki pages are mapped only where the chosen importer supports them, so some items may arrive as references or summaries rather than native objects. We document each object type in the validation log and agree with the team how gaps will be handled. Confirm the carry-over set with the importer documentation for your source before you promise it to stakeholders.
User contribution mapping
Imported contributions are attributed to GitLab users only when the source account is matched to a target account, usually by verified email or a supplied mapping file. We build the user map, reassign unmatched contributions to placeholder or owner accounts, and record every decision. Check with HR or identity owners how departed contributors and shared service accounts should be handled.
CI pipeline translation
Pipeline definitions in other systems are rewritten as .gitlab-ci.yml, with variables, secrets, images and environment rules moved to GitLab equivalents. Each pipeline also depends on runners with the right tags, executors and network access, which must exist before the first job runs. Plan runner capacity and secret storage with the pipeline inventory, not after cutover.
Subversion conversion
GitLab does not import Subversion directly, so history is converted with external tools before the result is loaded as Git. Conversion depends on the repository layout, branch conventions and author mapping, and it can change commit identifiers. Agree the conversion rules with the owning teams and test the output on copies, since errors in author or path mapping are hard to undo after the cutover.
Staged cutover with delta sync
Cutover runs in stages: a bulk import, a write freeze on the source for the affected projects, then a delta sync that carries changes made after the first pass. The freeze window and the delta size are the main drivers of downtime, so schedule them with the teams that push code. Verify the final state against the mapping sheet before the source is set to read-only.
Deliverables and runbook handover
You receive a migration report, the mapping sheet, the validation log, updated developer documentation for new repository locations and pipelines, and the runbook for repeating or reversing steps. The runbook is handed to your platform team so they can run later imports without us. Ask for the exact format of each deliverable in the written scope before work starts.
What drives cost and duration
Each row names a factor that changes how much work a GitLab migration takes, why it matters, and what to do to keep it under control.
| Driver | Why it matters | How to keep it down |
|---|---|---|
| Number and size of repositories | Each repository needs its own import run, verification and access check. Large histories and many repositories multiply rehearsal and validation time. | Inventory every repository with its size and last activity. Archive or skip inactive projects, and move large history in a separate, scheduled batch. |
| Number of source systems and versions | GitHub, Bitbucket Cloud, Bitbucket Server, Gitea and older GitLab instances each have their own importer behavior, limits and authentication. Older source versions may need extra steps. | Group repositories by source system and rehearse one group at a time. Confirm the importer and source version combination in the GitLab import documentation before scoping. |
| Merge request and issue volume | Merge requests, comments, reviews and issues carry references, labels and attachments that must map correctly. High volumes increase import time and review of results. | Decide which open items must move and which can be closed or linked as archive references. Set a clear cutoff date for in-flight work. |
| Contribution mapping and identity cleanliness | Imported authorship only maps to real accounts when the source email matches a verified user on the new instance. Duplicate or unverified identities leave work attributed to placeholder users. | Export the user list from each source early, resolve duplicates and departed staff, and agree the mapping rules with the people who own the identities. |
| CI/CD complexity and pipeline count | Pipeline definitions, variables, secrets and runner tags do not move automatically in the same form. Each pipeline needs translation, testing and runner capacity on the new side. | Start with a pipeline inventory. Group pipelines by runner need, reuse shared templates, and plan runner setup before cutover. Confirm which secrets must be re-entered by hand. |
| Subversion or other non-Git sources | Subversion history needs external conversion tools before it can enter GitLab. Conversion of long or messy histories can take more effort than the import itself. | Limit conversion to the history the business needs. Test conversion on a copy of a representative repository and agree the result with its owners before the main run. |
| Binary files and LFS content | Large binaries and Git LFS objects increase transfer size, storage and the time to verify. LFS objects may need to be copied separately from the Git history. | Measure binary volume per repository. Move large artifacts to the artifact or package store you control and confirm how LFS objects will be transferred. |
| Compliance evidence requirements | Audit or assessment scope may require records of who moved what, when, and how access was checked. Collecting this evidence after the fact takes longer than planning for it. | Define the evidence list before the first rehearsal. Keep import logs, mapping decisions and access reviews in a controlled location. Compliance assessors decide sufficiency, so confirm requirements with them. |
| Environments and air-gapped operation | Staging, production and disaster recovery instances each need configuration and testing. An air-gapped network adds offline package transfer and limits access to external source systems during the move. | Plan one repeatable rehearsal environment that matches production. For air-gapped sites, confirm early how source data reaches the isolated network and which transfer method your security team approves. |
| Number of cutover waves | Each wave adds a freeze window, a delta sync, a communication step and a rollback plan. More waves reduce risk per step but increase coordination work. | Group teams by dependency rather than by size. Keep the number of waves small, and use the same runbook for each one. |
| Tier choice (Free, Premium or Ultimate) | The tier does not change how the import works. It affects which features teams rely on after the move, such as advanced CI/CD or security and compliance features, and therefore the configuration and training needed. | Decide the tier from the features your teams will use after cutover, not from the import. Confirm current tier features on the GitLab pricing page before the decision. |
Migration phases and outputs
A staged sequence that moves repositories, review history and pipelines into self-managed GitLab with a checked rehearsal and a controlled cutover.
Inventory sources and dependencies
Your team and DeployOpen list every repository, branch rule, merge request, issue, wiki, pipeline and integration on the source system, then flag what GitLab's importers cannot carry over. Ownership of each project is recorded. The closing output is an inventory that the written scope references, and the step is done when the owners of the source projects have signed off on it.
Design target groups, permissions and identity
Groups, subgroups, project visibility, roles and the identity provider are designed against the inventory, and the user contribution mapping approach is agreed so that imported comments and merge requests attribute to the right people. DeployOpen drafts the structure; your identity and security owners approve it. The output is a target design document and the mapping sheet of source users to GitLab accounts.
Run a pilot import of representative projects
A small set of projects that cover the awkward cases, such as large histories, LFS objects, many merge requests, unusual branch names and heavy CI use, is imported with the built-in importers or direct transfer. Each result is compared against the source. The pilot report lists what arrived intact, what was partial and what needs a workaround, and the step closes when that report is accepted.
Map pipelines and translate CI configuration
Source pipeline definitions are rewritten as GitLab CI/CD configuration, secrets are re-entered through protected variables, and runners are matched to the jobs that need them. Where a source system used a plugin or external tool, the replacement is recorded. The output is a mapping sheet linking each source pipeline, its target job, its runner tag and its open issues, and the step is done when a test pipeline passes on the target.
Rehearse the full import and validate
The complete migration runs against a non-production instance on the same schedule and steps planned for cutover. Repository counts, commit hashes, branch protections, merge request states, user mappings and pipeline results are checked with a written validation list. Every failure is logged with its cause and fix. The rehearsal log is the record that the cutover plan is trusted, and the step closes when a clean run is recorded.
Execute a staged cutover with freeze and delta sync
Teams are told when writes to the source stop, the freeze window opens and the final import captures changes made since the earlier passes. Access to the old system is then restricted and the new system is opened to developers. The cutover record notes times, operator, validation results and any exceptions. Timing is set in the written scope, not assumed here.
Harden the target and decommission the source
After cutover, the new instance is hardened against the agreed baseline, backups are verified with a restore test, and integrations are repointed. The source is kept read-only for an agreed period, then archived and shut down once no dependency remains. The decommissioning checklist records access revocation, data retention copies and sign-off, and the migration closes when that checklist is complete.
What stays in-house and what to outsource
This table shows which parts of a GitLab migration your own people should keep and which parts a partner can usually take on, with the skills each part requires.
| Work part | Usually kept in-house | Usually outsourced | Skills required |
|---|---|---|---|
| Repository ownership and retirement | Deciding which repositories move, who owns each one after import, and when old GitHub, Bitbucket or GitLab projects are archived or made read-only | Preparing an inventory report of repositories, sizes, branches and last activity to support those decisions | Engineering management, repository governance, knowledge of team structure |
| Project validation | Developers checking that code history, branches, tags, merge requests and issues arrived intact and that their workflows still function | Writing reconciliation checks that compare counts and references between source and target, and logging the discrepancies found | Hands-on Git and GitLab use, familiarity with each project's build and review practices |
| User and identity mapping | Identity and access owners approving which source accounts map to which GitLab users, and deciding how contributions are attributed | Building mapping scripts or CSV templates, running dry-run comparisons and reporting unmatched accounts | Directory administration, SSO and SCIM concepts, privacy and attribution rules |
| CI pipelines and runners | CI owners rewriting or approving pipeline definitions, secrets and variables, and deciding which runners will execute jobs after cutover | Helping convert pipeline definitions where needed and setting up runner capacity or images on request | CI/CD design, runner operations, secrets handling, container image management |
| Target instance platform | Platform engineers owning the self-managed GitLab instance, its sizing, backups, upgrades and access policies after go-live | Installing and configuring the instance, applying hardening and documenting the build, with ongoing support available under a retainer | Linux administration, PostgreSQL and object storage basics, backup and restore practice |
| Import execution and tooling | Approving the import order, timing and rollback criteria, and receiving the migration logs | Running the built-in GitHub, Bitbucket, Gitea and GitLab transfer tools, writing automation for bulk imports and converting Subversion history with external tools where needed | Scripting against GitLab and source platform APIs, Git internals, troubleshooting large imports |
| Rehearsal and cutover runbooks | Business owners signing off on the cutover window, freeze rules and go or no-go decision | Drafting the staged runbook, running at least one rehearsal on a copy of the data and documenting timings and failure handling | Change management, incident response, runbook writing, cross-team coordination |
| Source system shutdown and link updates | Teams updating local remotes, webhooks, deploy keys and external links that point to the old platform | Listing webhooks, integrations and API consumers found in the source system so the owners can update them | Integration inventory, webhook and token administration, communication with dependent teams |
What usually goes wrong
Most GitLab migrations that stall or lose data do so for a small set of predictable reasons. Each card names one failure mode and the step that prevents it.
Importers skip items silently
The built-in importers for GitHub, Bitbucket and Gitea move repositories, merge requests, issues and some CI data, but not every object type carries over the same way. Teams often assume wikis, releases, attachments or certain review comments moved when they did not. Run a rehearsal import, then compare object counts per project against the source using a written inventory of what must move.
Contributions attributed to placeholder users
When an imported author cannot be matched to an existing GitLab account, history is attributed to a placeholder user instead of the original person. This affects blame, review history and audit trails that auditors may sample. Review the user mapping before the final import, and decide in advance who approves mappings that cannot be matched automatically.
Usernames and emails that do not match
Mapping depends on matching source identities to GitLab accounts, and mismatched usernames or email addresses block that step. People who changed employers, renamed accounts or used personal addresses in commits are the usual cases. Export the source user list early, reconcile it against your identity provider, and agree on a rule for contributors who have left the organization.
Large or LFS-heavy repositories time out
Repositories with long histories, large binaries or Git LFS objects can exceed import time limits or fail partway through, and LFS objects need their own transfer path. Identify the largest repositories during planning, test them separately in the rehearsal, and confirm how LFS content is moved and verified on your instance and storage backend.
CI definitions waiting on runners and secrets
Imported pipeline definitions run only when a matching runner exists with the right tags, and variables or secrets used by jobs are not always carried over. Pipelines then fail or sit in a pending state on cutover day. Register runners and tags before cutover, recreate protected variables in your secrets store, and run a pipeline for each critical project in the rehearsal.
Webhooks and integrations still point at the old source
Chat notifications, deployment triggers, issue trackers and scheduled jobs often reference the old host or hold tokens tied to the old system. After cutover they stay silent or keep building the wrong code. Build an inventory of webhooks, integrations and service accounts for each project, repoint them during the cutover window, and confirm each one fires a test event.
Subversion history lost in conversion
GitLab does not import Subversion directly, so teams rely on external conversion tools, and the converted history can differ from the original in branch layout, tags, author names or ignored properties. Decide which history must be preserved, convert a sample repository in the rehearsal, and sign off the converted output with the teams who depend on it before retiring the Subversion server.
Cutover without a write freeze
If developers keep pushing to the old system after the final import, commits land on a source that no longer reflects the new state. Reconciling those changes by hand is error prone. Set a freeze window on the old system, run a final incremental sync after the freeze starts, and use a post-cutover checklist to confirm no new commits arrived after the cutover point.
Instance hardening and backups missing at go-live
A migrated project is only as safe as the instance it lands on. Teams sometimes import data before backups, restore testing, access controls and the runner and secrets setup are in place, so the first production change has no recovery path. Make a backup and restore test a go-live gate, and document who can approve the cutover.
How the work changes by starting point
GitLab includes built-in importers for GitHub and Gitea, so most of the effort goes to credentials, user mapping and checking what each import leaves behind. Run a test import on a sample group before you move the full list.
- Importer path: the built-in GitHub or Gitea importer, started from the new project menu, using a token that can read the source projects.
- Typically carried over: repository history and the issue and pull request content the importer supports. Confirm in your pilot which review comments, labels, milestones and attachments arrive.
- Needs rebuilding: GitHub Actions workflows cannot run unchanged on GitLab, so translate them into .gitlab-ci.yml and provide runners. Webhooks, GitHub Apps and repository secrets are recreated by hand.
- User mapping: authors are matched to GitLab users by username or email. Confirm how your identities line up with your identity provider before cutover, because unmatched authors keep placeholder attribution.
GitLab has separate importers for Bitbucket Cloud and Bitbucket Server or Data Center, and the two differ mainly in how GitLab reaches the source. Confirm the access path first, since it decides whether the import can run from your own network.
- Importer path: the built-in Bitbucket Cloud importer, or the Bitbucket Server importer for self-managed Bitbucket. Both need a credential with read access to the projects.
- Network reach: a Bitbucket Server or Data Center host must be reachable from the GitLab instance. Cloud imports depend on outbound access from your network to the hosted service.
- Typically carried over: repositories and the pull request and issue data the importer supports. Confirm the coverage for your Bitbucket version in a pilot, including comments and attachments.
- Needs rebuilding: Bitbucket Pipelines or Server build integrations need to be rewritten as .gitlab-ci.yml jobs, and runners, branch protection rules and access groups must be set up in GitLab.
Moving between GitLab instances is the most complete path because both sides share the same data model. The main choice is between direct transfer and file export, and the right answer depends on whether both instances can reach each other during the window.
- Direct transfer: run from the destination with owner or administrator rights on the source. It requires network access between the two instances and version compatibility, so check the upgrade paths before you start.
- File export: groups or projects are exported to files and imported on the destination. Use this when the instances cannot connect. Confirm what each export type includes, since secrets, runner registrations and some settings are typically excluded.
- Version gap: if the source is several releases behind, plan required upgrade stops on the source first. Those stops fall at x.2, x.5, x.8 and x.11 from GitLab 17.5 onward.
- Recreated by hand: CI/CD variables, deploy keys, tokens, runner registrations and integrations usually need to be re-entered and verified.
Subversion needs external conversion before anything reaches GitLab, and plain Git repositories imported by URL or manifest carry the least metadata. Treat both as code-history projects first and plan the collaboration data separately.
- Subversion path: a conversion tool such as git-svn or a similar converter turns the history into Git, after which the result is pushed to GitLab. Confirm the tool fits your repository layout, including trunk, branches and tags.
- Author mapping: build an authors file that maps Subversion usernames to GitLab identities before conversion. Unmapped authors are a common source of messy history.
- Git by URL or manifest: the importer clones each listed repository. Expect branches, tags and commits only, with no issues, merge requests or pipelines.
- Needs rebuilding: issues, review history, wiki content and CI definitions must come from a separate source or be recreated in GitLab, and this work is often larger than the code move.
Isolated and tightly controlled networks change the order of work. Importers that reach external hosts may not work at all, so the usual approach is to stage data inside the controlled zone and move it in through an approved transfer path.
- Connectivity check: confirm early whether the chosen importer needs internet or source-host access. If it does not, plan an export-based path instead.
- Staging and transfer: export from the source in a controlled zone, transfer through your approved process, and verify checksums and content on arrival. Record each step for your change and audit records.
- Offline components: GitLab documents offline environments, but runner images, package sources and any security scanners must also be staged. Confirm which components your instance needs offline in its installation design.
- Regulated data: repositories may contain personal data, secrets or export-controlled material. Confirm with your security and compliance owners what may be moved, scanned and retained before import, and document the decision.
What you need to provide
Before work starts, the client supplies a target GitLab instance with an installation method agreed in writing, plus admin access to it. The installation method decides how the instance is built and patched, so confirm it early with the platform team that will run it. Each source system (GitHub, Bitbucket, Gitea or another GitLab) also needs a service account with read access to every repository in scope, and an owner who can approve changes to that account's permissions.
Next, supply an inventory of repositories and their owners, including who holds maintainer rights and which projects are archived or abandoned. Decide which history and items must move: full Git history, branches and tags, merge requests, issues, wiki pages, releases and attachments. Each item type has different import behavior, so a written decision here prevents rework later. Name who signs off on that list, because it sets the scope of the migration.
For contribution mapping, provide the identity source and a user list that matches source accounts to people, with the email addresses each person uses. Decide whether mapping happens by verified email or by manual review, and name the person who resolves unmatched accounts. Also provide the CI inventory: which pipelines, variables, and secrets exist, and which runners are available in the target network. Confirm runner capacity with the team that owns it, since jobs will need runners as soon as pipelines are re-enabled.
Finally, name the people who make the cutover decisions. Provide change windows agreed with operations, a freeze owner who can stop merges on the source during the final sync, and named decision-makers who approve retirement of each old source. Retirement should not start until the decision-makers have confirmed that teams no longer depend on the old system. Confirm these roles in writing before the first test import.
Frequently asked questions
Can our own team migrate repositories to self-managed GitLab without a partner?
Yes, many teams do the work in-house, and the deciding factors are capacity, history volume and risk tolerance. The built-in importers for GitHub, Bitbucket and Gitea, and the direct transfer between GitLab instances, cover the common cases. Your team needs someone who owns the target instance, knows your branch protection and CI setup, and can run a rehearsal before the cutover date. Ask the partner for a written scope that lists which of these tasks they would take on, so you can compare it with your own staffing plan.
What drives the cost of a GitLab migration project?
The main cost drivers are the number of repositories and users, the volume of merge requests, issues and CI configuration, the number of source systems, and any non-Git history that needs conversion. Fixed-scope projects are priced from a written scope, so there is no public price list to quote from. Ask for a quote that separates one-time work (planning, mapping, rehearsals, cutover) from any recurring retainer for support or upgrades, and that states what the client must provide, such as admin access and a named approver.
How long does a migration take, and what sets the schedule?
Duration depends on the number of repositories, the size of the largest histories, how much review and issue history must follow the code, and how many CI pipelines need rework. Access approvals, the availability of key developers for user mapping and the number of cutover windows also shape the timeline. Ask for a schedule with explicit dependencies, a rehearsal stage and a freeze window, and confirm which milestones the written scope commits to.
Does GitLab import merge requests and issues from GitHub or Bitbucket?
GitLab's import documentation lists GitHub, Bitbucket Cloud and Server, and Gitea as sources for its built-in importers, and these importers can bring over more than repository content, but you must confirm which object types each importer carries for your version and source. Test this on a sample project before planning the full migration. Jira and FogBugz issue imports are also documented separately. Check any gaps, such as attachments, review comments or CI variables, against the import documentation for your target GitLab version.
Can Subversion history move into GitLab, and how do contributors map to users?
Subversion is not imported directly by GitLab, so it needs an external conversion tool, and the conversion has to be tested for author mapping, branch layout and tags before any cutover. Contributor mapping links historical commit author names and emails to GitLab user accounts. Plan this with the people who know the contributor list, decide how departed or external authors are represented, and confirm that commit authorship is preserved in the target instance.
What happens to CI pipelines and cutover with no lost commits?
Imported repositories do not automatically run CI as the source system did, so pipeline definitions, runners, variables and secrets must be reviewed and rebuilt in GitLab CI/CD. For cutover, the usual approach is a short write freeze on the source, a final sync of refs and a verification that commit counts, tags and default branches match. Ask the partner to show the verification checklist, the rollback path and who signs off, and confirm these steps in the written scope.
How DeployOpen delivers GitLab migrations
DeployOpen plans and runs GitLab migrations from GitHub, Bitbucket or another GitLab instance onto a self-managed GitLab you operate in your own infrastructure. The work moves repositories, merge requests, issues and CI configuration through a staged cutover with user contribution mapping checked before go-live.
How we scope
- The written scope lists which repositories, merge requests, issues and pipeline configurations move, and which stay behind as archives.
- Source system inventory covers projects, groups, users, large files, integrations and any Subversion repositories that need external conversion.
- Scope sets the target GitLab edition, reference architecture and whether Runners are migrated or rebuilt on the new instance.
- Engagements are fixed-scope, so scope, deliverables and timelines are agreed in writing before migration work starts.
How we build
- Migration rehearsals run on a test instance using the GitHub, Bitbucket, Gitea or GitLab import path that fits each source.
- User contribution mapping links commits, comments and merge requests to the correct GitLab accounts before the production import.
- CI/CD configuration is reviewed for runner tags, protected variables and secrets so pipelines run on the target instance without manual repair.
- Each rehearsal is checked against an agreed list of repository, issue and merge request counts, with differences documented and resolved.
How we hand over
- Cutover runbook records the freeze window, final sync, DNS or remote changes, rollback steps and the owner of each step.
- Documentation covers the configured instance, backup jobs, restore procedure and the upgrade stops your team must plan for.
- Handover includes a walkthrough with administrators and a list of source systems to decommission or set to read-only after cutover.
- Acceptance criteria in the written scope define what counts as a completed migration before the project closes.
How we support
- Ongoing support, upgrades and continuous testing are available as retainers after the migration closes.
- Post-cutover support covers reconciling late-arriving issues, fixing mapping gaps and answering questions from teams during their first weeks.
- Retainer support can include planned GitLab upgrades that pass through required upgrade stops, rather than ad hoc version jumps.
- Ask in your quote which post-cutover support is included, how long it runs and how requests are prioritized and recorded.

