Migrate from GitLab.com to self-managed GitLab: what the move involves
Moving groups and projects from GitLab.com to a self-managed instance is a direct transfer between two GitLab installations, and it suits a team that must keep source code, merge requests and pipelines inside infrastructure it controls. The effort depends on how many groups and projects move, how heavy the CI load is, how large the runner fleet is, how identity is connected, and which install method the target instance uses. Because source and target are both GitLab, most core objects map cleanly, so the real work sits in two places: what the transfer does not carry, and how the target instance is operated once it holds production data. Start by listing what the transfer leaves behind. Group and project settings, members, and most repository content move, but you should confirm against the current import documentation which items are included, which need manual recreation, and how attachments, container registry images, package registries and CI/CD variables are handled. Pipelines depend on runners, so plan the runner fleet on the target before cutover, and treat CI/CD variables and protected values as secrets that must be re-entered or reviewed rather than assumed to follow the project. Identity is the other gap: user accounts on the target must be provisioned and linked to the right people before contributions are mapped.
Plan the target as a production system from the start. A self-managed instance needs its own backup schedule, upgrade path and monitoring, and the install method you choose (for example the Linux package, the Helm chart or the GitLab Operator) sets how those tasks are carried out. Check the GitLab backup and restore documentation before the first real project arrives, and confirm that your backups include the secrets file, not only the database and repositories. Confirm the license tier you need for the features your groups use, and align it with the tier you have bought before cutover, since GitLab.com subscriptions do not automatically carry over to a self-managed license. Ask your team to name owners for the target instance, the runner fleet and the cutover schedule before any transfer begins.
What maps cleanly from GitLab.com and what does not
This table compares each GitLab.com artifact against what direct transfer carries, what needs manual recreation, and whether the result depends on the target's edition or identity setup. Read each row before scheduling the cutover, and confirm every gap against the import documentation for your target version.
| Item | Direct transfer | Manual work after transfer | Depends on edition or identity |
|---|---|---|---|
| Repositories and branch history | Carried with groups and projects | Verify refs, tags and default branch after transfer | No |
| Merge requests | Expected to carry, including discussions | Spot-check reviewers and approval state | Approval rules need Premium or Ultimate |
| Issues and comments | Expected to carry | Check labels, milestones and attachments | No |
| Wiki content | Expected to carry with the project | Confirm links and attachments resolve | No |
| CI/CD configuration files | Pipeline YAML travels with the repository | Re-check includes, templates and external references | Some features need a tier, such as advanced CI/CD on Premium |
| CI/CD variables and secrets | Not expected to carry | Recreate at group or project level, with masking | No |
| Runners | Not part of transfer | Register new runners to the self-managed instance | No |
| Container and package registries | Not expected to carry | Re-push images and packages to the target registry | No |
| Members and permissions | Mapped to users who already exist on the target | Recreate roles and invite users who are missing | Depends on SAML, LDAP or SCIM user matching |
| Access tokens and deploy keys | Not expected to carry | Issue new tokens and keys, then update integrations | No |
Documented direct transfer and export paths
Moving from GitLab.com to a self-managed instance has two practical routes: a direct transfer between GitLab instances and a file export and import. The right choice depends on how your groups are nested, how much data each project carries, and what your target instance has enabled, so confirm those facts before you pick a path.
Direct transfer between instances
Direct transfer moves groups and projects from one GitLab instance to another through the API, without building an export file first. GitLab.com is a supported source, and the GitLab import documentation lists other GitLab instances as a source. Check the source and target versions, and confirm with GitLab that your target release supports the transfer you plan.
Admin setting on the target
Direct transfer does not start until an administrator enables the relevant import source on the self-managed target instance. Confirm who holds that admin role, when the change is approved, and whether your change process requires a record of it. Turn the setting off again once the migration window closes, so the import path does not stay open longer than needed.
Group-level versus project-level transfer
A group transfer carries subgroups, projects and their group-level settings together, which suits organizations whose structure mirrors their teams. A project transfer moves one repository at a time and gives finer control over sequencing. Map your current group tree before deciding, and note which projects depend on shared group settings or memberships that a single project move will not recreate.
File export and import as fallback
When direct transfer cannot run between your instances, a file export followed by an import is the fallback. The export packages project data into a file that you move across the network and import on the target. Plan storage for the file, the transfer method, and how you will verify that issues, merge requests and repository data arrived intact.
Data volume and sequencing
Large repositories, long histories and heavy attachments change how long a transfer takes and how much the target must absorb at once. Split the work into waves rather than moving everything in one run. Ask GitLab support or your own test environment for observed timings on a representative project before you commit to a cutover date.
Confirm the path before planning
Each path has limits that depend on your group structure, data volume and target version, so the choice should come from a test run rather than assumption. Run a pilot transfer of one group with representative projects, record what arrived, and compare it against the source. Use the results to decide between direct transfer and export for the rest of the estate.
How user mapping and contribution attribution work after transfer
Settle the identity model on the target instance before you start a direct transfer, because the transfer itself only carries groups, projects and their history. Users on the target are matched to the people who authored or reviewed work on GitLab.com, and that match decides whether merge requests, issues and comments keep the right attribution. Decide first how people will sign in to the self-managed instance. If you plan to use SAML, LDAP, SCIM or a generic OAuth 2.0 provider, configure it, test it with a pilot group, and confirm that the username and email attributes it sends match what your team uses on GitLab.com. Changing the identity source after transfer means reconciling accounts twice.
Make three explicit decisions and record them. First, whether matching relies on verified email addresses or on usernames, and who approves exceptions when an address changed after a person joined. Second, what happens to people who have left the organization: whether their accounts are blocked or deprovisioned before transfer, and how their past contributions will appear afterward. Third, how bot and service accounts are handled, including project access tokens, deploy tokens and integration users, since automation accounts often have no matching human identity and may need to be recreated with the same scoped permissions. Confirm each decision against the current GitLab documentation for your release, because the exact behavior of user matching depends on the version and settings you choose.
Contributions from authors who do not yet have an account on the target are not lost, but they are not attributed to a live user either. They wait as placeholder records until someone with the right access claims them, which is why the number of people who must be reassigned matters for a large transfer. The detailed steps for reviewing placeholders, reassigning contributions and accepting them are covered in the separate topic on mapping users and contributions after a GitLab import. Read it before you choose a cutover date, and plan who on your side will perform reassignments and within what window.
Runners, CI/CD variables and pipelines after the move
GitLab.com shared runners and group runners belong to GitLab.com and do not run jobs for a self-managed instance. After the move, the organization needs runners it operates and registers to the new instance. The GitLab Runner documentation covers the executor choices, including shell, Docker and autoscaling executors, and the work is usually a fleet decision rather than a single install. Keep the Runner version aligned with the GitLab version on the instance, as the Runner documentation advises keeping major and minor versions matched.
- Inventory current runners: List which projects and groups depend on shared runners, group runners and project runners, along with their tags.
- Re-register under the new instance: Runner registration tokens and authentication come from the target instance, so every runner must be registered there again.
- Match tags and executors: Jobs that select runners by tag need equivalent runners with the same tags, or the pipeline definitions need updating.
- Align versions: Plan Runner upgrades alongside GitLab upgrades so major and minor versions stay matched.
- Size for demand: Confirm peak concurrent job load from the current pipeline history, since shared capacity on GitLab.com does not carry over.
Confirm with your platform team which executor and cloud or hypervisor model they will operate before cutover, and test a representative job on each runner type.
CI/CD variables are stored in the GitLab instance database, not in the repository, so moving a repository does not carry its variables with it. Protected and masked settings describe how a variable is exposed to jobs, and they should be reviewed as part of the move. Confirm what the transfer includes in the target version, and plan to re-enter secret values through the organization's approved secrets process rather than copying them from a GitLab.com export. Values held in external secrets managers may be better fetched at job runtime than stored as variables.
- Export a variable inventory: Record the key, scope (environment or group), protected and masked flags, and whether each variable is file-type.
- Re-enter secrets deliberately: Enter values through the approved secrets workflow, and record who approved each value.
- Review protected scope: Protected variables only reach protected branches and tags, so confirm that branch rules on the new instance still match.
- Check masking rules: Masked variables must meet the character requirements, so test values that failed masking on GitLab.com.
- Rotate credentials: Treat any secret that lived on GitLab.com as exposed to the old instance and rotate it at cutover.
Ask the instance owner which variable fields and scopes the transfer carries in your target version, and keep a record of every value that was re-entered.
Pipeline definitions live in .gitlab-ci.yml in each repository, so they transfer with the repository content. The risk is in references that point outside the repository. Includes, templates, container images and trigger projects may still resolve to GitLab.com paths, or to projects that were not moved. A pipeline that appears to import cleanly can fail at job start when a remote include cannot be reached.
- Search for GitLab.com references: Scan
.gitlab-ci.ymlfiles and included templates for hard-codedgitlab.comhostnames and project paths. - Check includes and components: Confirm each
includesource, including projects in other groups, is present on the target and that access is granted. - Update image registry paths: Container images pulled from the GitLab container registry on GitLab.com need new paths on the target instance.
- Review multi-project triggers: Downstream projects triggered by name must exist under the same paths, or the trigger logic must change.
- Run pipelines on a test branch: Execute a representative pipeline on each major project before switching the team over.
Confirm with the source owners which external references each pipeline uses, and record the changed paths in the migration runbook.
Runners must reach the instance to request jobs, download code and upload artifacts, and jobs must reach the services they call. On a self-managed deployment, these paths sit inside the organization's network boundary, so they need to be designed, not assumed. Runners that run in a segmented or air-gapped network may need internal mirrors for images, packages and templates. GitLab documents offline and air-gapped operation, and the same planning applies to runner traffic.
- Map runner-to-instance paths: Confirm that each runner network can reach the instance URL over the required ports and protocols.
- Plan egress for jobs: List the external endpoints that jobs call, such as package registries, cloud APIs and scanners, and decide which must be allowed or mirrored internally.
- Address certificates: Runners must trust the internal certificate authority that signs the instance certificate.
- Confirm registry access: Test pulls and pushes against the new container registry path from every runner network.
- Restrict scope: Limit which runners can serve which projects, so that production deployments run only on hardened runners.
Confirm these paths with your network team before cutover, and test a job from each runner network against the new instance URL and registry.
Sequence for inventory, pilot, parallel run and cutover
This sequence moves groups and projects from GitLab.com to a self-managed instance in controlled stages, so each transfer is checked before the next one starts. It ends with a decommission step that only runs after the target instance is formally accepted.
Inventory groups, projects, runners and integrations
Your platform or DevOps lead builds a register of every top-level group, subgroup and project on GitLab.com, with owners, active pipelines, registry images, CI/CD variables, protected branches, webhooks, deploy keys and group or project access tokens. Include every runner that serves these projects, and mark which settings the direct transfer does not carry so they can be recreated by hand. The step is done when the register is reviewed by each group owner and nothing is left unassigned. Confirm the current list of what direct transfer includes in the GitLab import documentation before you finalize the register.
Build and validate the target instance
Your infrastructure team installs the self-managed target and chooses the edition that matches the features your teams already use on GitLab.com. They then configure identity, backups, monitoring and the runner fleet, and test a restore from backup on this instance. Enabling the direct transfer setting and confirming that your administrators can use it is part of this step. The step closes when a test backup restores cleanly, sign-in works through your chosen identity provider, and a test runner picks up a test job.
Transfer a pilot group and verify it
Choose one low-risk group with a small number of projects and move it with direct transfer, first enabling the setting on the target. Verify the result project by project: a pipeline runs on the target, merge requests and issues show the expected history, members have the right roles, and a sample of artifacts and container images can be downloaded. Record every difference, such as contributions shown as placeholders or variables that did not arrive, and decide whether each one is acceptable. The pilot is complete when the group owners sign off on the list of differences in writing.
Run both instances in parallel
Transfer the remaining groups in waves while GitLab.com stays available for read access and rollback. Each wave is checked with the same pipeline, permission and artifact tests used in the pilot, and the platform team tracks the differences register. Confirm that runners on each side are not picking up jobs for projects that have already moved, and that scheduled pipelines are paused or disabled on the source. The parallel run ends when every wave passes its checks and the open differences are either fixed or accepted by their owners.
Block CI jobs and switch over in a cutover window
During an agreed window, the team stops new CI job requests on GitLab.com, takes a final transfer of any changed projects, and repeats the verification on the target. Update DNS, Git remotes, webhooks and any external systems that point to the old instance, and confirm that developer clones and pushes now land on the target. Runner registrations and CI/CD variables are checked against the target before jobs are re-enabled. The window closes when a sample of production-relevant pipelines succeeds and no traffic is still routed to GitLab.com. Ask your provider to confirm the exact cutover steps for your version before you schedule the window.
Accept the target and decommission GitLab.com access
Once the business owners and security team formally accept the target, the team archives the source groups on GitLab.com, revokes remaining tokens, deploy keys and runner registrations, and removes service accounts that no longer need access. They keep a final export or backup of the source for the retention period your policy requires. The step is done when an access review shows that only accepted users remain on the old platform, and the decommission record is signed by the platform owner. This record is also useful evidence for change management reviews.
Aligning the license tier before the target goes live
GitLab Free, Premium and Ultimate are all available for self-managed installations, so the tier you run on the target is a separate decision from the one you hold on GitLab.com. Premium lists advanced CI/CD and priority support. Ultimate lists application security testing, software supply chain security, vulnerability management and compliance features. Premium is listed on the GitLab pricing page at 29 US dollars per user per month billed annually, and Ultimate is custom priced, so the quote for Ultimate depends on your user count and the features you need. Confirm current pricing and terms with GitLab before you commit, because the listed figures can change.
Before cutover, inventory what your groups and projects use today and map each feature to a tier. Check pipelines for advanced CI/CD configuration, review whether scanners, merge request approval rules, security dashboards or compliance pipelines are in use, and list any support expectations your team relies on. Also note the group and project settings that the transfer will carry over, such as protected branches and approval rules, and confirm which of them behave differently on the target tier. A feature that worked on GitLab.com but is missing on the self-managed tier will show up as a broken pipeline or an absent control on the first day, so resolve these gaps in the plan rather than during the cutover window.
Settle the tier with the people who sign for it: engineering leads for CI/CD needs, security for scanning and vulnerability management, and procurement for seat counts. Seats should cover every user who will be mapped during the import, including service accounts and users who only contribute issues. Ask GitLab or your reseller to confirm how the license is applied, how seats are counted and how the license is loaded on the instance before the transfer begins. Record the decision in the migration plan so that the tier, the seat count and the feature list can be checked again at the go-live review.
What drives effort and duration on this migration
Effort on a GitLab.com to self-managed move rises with the volume of groups, projects, data and CI configuration, and with how many identities must map cleanly to the new instance. The cards below list each driver, which way it pushes the schedule, and what to count before you request a quote from any partner or run the work in-house.
Groups and projects in scope
Each group carries its own membership, settings, variables and subgroup tree, and each project carries its own issues, merge requests and wiki. Effort grows with the number of projects you move and with how deeply nested your group structure is. Count top-level groups, subgroups and projects, and mark which ones are archived, empty or retired so they are excluded before the quote is written.
Repository, LFS and registry size
Large repositories, Git LFS objects, container registry images and package artifacts take longer to export, transfer and verify, and they consume disk on the target. Check the largest repositories and the total storage each group uses on GitLab.com, and confirm which of these data types your direct transfer will carry. Plan a separate path for anything the transfer does not cover.
CI job volume and runner fleet
Pipeline history is not the expensive part. The expensive part is the number of pipelines, the tags and executors each job needs, and whether your runners are shared, group-level or project-specific. Every runner must be installed, registered and tested against the new instance. Count active runners by executor type, the tags they serve, and the jobs that depend on Docker, Kubernetes or cloud-specific images.
CI/CD variables and external integrations
Each CI/CD variable is a value someone must locate, classify and re-enter or re-create, and protected or masked variables need extra care. Webhooks, deploy keys, external secrets managers, container registries and chat integrations also need new endpoints and credentials. Count variables at group and project level and list every integration that calls out from or into GitLab, since each one is a separate verification step.
Identity provider integration
Moving to self-managed GitLab means your sign-in path changes. Confirm which authentication methods the target will use, such as SAML, LDAP, OAuth 2.0 providers, or SCIM provisioning, and whether they are configured before the first user signs in. Identity work is often on the critical path, so decide early which identity provider owns each user and how group roles map to your directory.
Target install method and availability
The target installation and its high availability needs set how much infrastructure you must build before any data arrives. A single-node Linux package install needs far less preparation than a multi-node design with Gitaly Cluster, PostgreSQL with Patroni and Redis Sentinel, which the docs generally recommend at 3,000 or more users. Decide the install method, sizing and disaster recovery target first, and ask any partner to price that infrastructure separately from the migration itself.
Users requiring mapping
Every contributor, assignee and commenter must map to an identity on the target, and the mapping decides whether authorship history reads correctly after the move. Count active human accounts, bot and service accounts, and former employees whose contributions must be preserved. Large user counts lengthen the review and reassignment work, and they should be handled as a named step in the quote.
What usually goes wrong and how to prevent it
Why do CI pipelines fail after moving projects off GitLab.com?
Pipelines usually fail because jobs, runner registrations or deployment scripts still reference GitLab.com hostnames, registry paths, package sources or API endpoints. Runners registered to GitLab.com do not pick up jobs from the new instance, and any CI_SERVER_URL assumption hard-coded in scripts will point at the wrong host. Prevention: inventory every runner, include file, container registry path and external URL before cutover, register new runners to the target instance, and run a test pipeline on a non-critical project first. Confirm the exact runner and registry behavior for your version in the GitLab Runner documentation before you rely on it.
Which CI/CD variables and secrets must be re-entered after a direct transfer?
Treat CI/CD variables, deploy keys, webhooks tokens and integration credentials as things you re-enter, not things you assume came across. Masked and protected variables, file-type variables and secrets held by external vaults often need to be recreated by someone with the right role, and the values themselves cannot be read back from GitLab.com. Prevention: export a list of variable names, scopes, protected and masked flags from each project, obtain the values from the owning teams through your secrets process, and store them in the target instance or your secrets manager. Rotate any credential that was exposed in a shared channel during the move.
How do you stop contributions from being orphaned when users do not match?
Contributions are orphaned when the usernames or emails on the source instance do not match accounts that exist on the target, so issues, merge requests and comments end up attributed to placeholder users. Prevention starts with a user map: match each GitLab.com identity to a self-managed account using the same email or a verified SAML or SCIM identity, decide which accounts must exist before the transfer, and agree who handles unmatched contributors. Plan how placeholder reassignment and acceptance will run, and check that the target authentication setup, such as SAML or LDAP, is working before users sign in. Confirm the import behavior for your version in the GitLab import documentation.
What license tier gaps appear after a GitLab.com cutover?
Tier gaps appear when teams enable features on GitLab.com, such as advanced CI/CD, security scanning, compliance controls or SAML group sync, that the self-managed license does not cover. Features can silently disable or show upgrade prompts, and seat counts can be wrong if license users and active users are not reconciled. Prevention: list the features and settings used on GitLab.com per group, compare them with the tier details on the GitLab pricing page, and buy or confirm the license for the target seat count before the cutover window. Ask GitLab or your reseller to confirm which features your chosen tier includes.
How do you avoid double webhooks and duplicate CI jobs during cutover?
Double processing happens when both GitLab.com and the new instance receive the same push, merge request event or scheduled pipeline during the overlap. Webhooks can trigger external deployments twice, scheduled pipelines can run on both sides, and runners can pick up jobs from either system. Prevention: pick a single cutover moment, disable or pause CI schedules and webhooks on the source, block new CI job requests on the source once the final sync is done, and re-enable only on the target after DNS and runner checks pass. Document who owns the switch and the rollback step.
What should be planned for upgrades and backups before go-live?
Go-live fails operationally when the target instance is on an unplanned version, has no tested backup, or has no upgrade path to the next release. GitLab requires passage through required upgrade stops, and security fixes are backported only to recent monthly releases, so a version chosen casually can fall behind quickly. Prevention: select a version from the maintenance policy, confirm the upgrade stops ahead of it, take a backup and restore it into a separate environment, and schedule the first upgrade before you move production traffic. Include the gitlab-secrets.json file in your backup scope.
Can a team run the GitLab.com to self-managed move without outside help?
Many teams can, provided they have an owner for each area: runner fleet, identity, secrets, backup and the cutover runbook. The work is less about the transfer itself and more about the inventory of settings, integrations and users that sit outside the repositories. Ask a partner for a written scope that covers the inventory, a rehearsal in a non-production instance, the user mapping approach, and the cutover and rollback runbook, so you can compare it with your own staffing. Cost depends on group count, integration count and runner complexity, which should be stated in the quote.
Official documentation to check before scoping
Before you scope a move from GitLab.com to a self-managed instance, read the GitLab import and migrate documentation for direct transfer. It is the source of truth for which groups, projects and related objects move, which settings must be enabled on the target instance, and which items a direct transfer does not carry. Treat the list in this guide as a starting point and confirm each item against the page for your target release, because import behavior changes between versions and the gaps you find in a test transfer should drive your cutover plan.
Next, compare the GitLab pricing page with your current subscription. The pricing page lists which features belong to Free, Premium and Ultimate for self-managed installations, including advanced CI/CD, priority support and the security and compliance features. Confirm that the tier you hold on GitLab.com maps to the tier you will run on your own infrastructure, and ask your GitLab account contact to confirm license transfer and seat counts in writing. Pricing details can change, so verify them on the page on the day you request a quote.
Finally, read the GitLab Runner documentation and the GitLab backup and restore documentation. The Runner guidance explains how to keep Runner versions aligned with your GitLab version and how to register runners that replace the shared and group runners you use on GitLab.com. The backup and restore documentation sets the procedure you will rely on before and after the move, including what must be preserved so that the restored instance can decrypt stored secrets. Confirm both against the docs for the exact release you plan to install, and record the version you tested.
How DeployOpen scopes and runs a GitLab.com to self-managed move
DeployOpen plans, builds and hands over the move of groups and projects from GitLab.com into a self-managed GitLab instance in your own infrastructure. Each engagement starts from a written scope that names the groups, projects, pipelines, runners and identity settings in play.
How we scope
- Inventory groups, projects, members, CI/CD variables and runners to confirm what the direct transfer will carry.
- Map GitLab.com users to your identity provider and agree how account matching and sign-in will work.
- Agree the license tier for the target instance and confirm which features your teams depend on before the build starts.
- Record the scope, deliverables and timeline in writing before any work starts, since most engagements are fixed-scope.
How we build
- Prepare the self-managed instance, including install method, backups and access controls agreed in the scope.
- Run a test direct transfer of representative groups and compare repositories, members, pipelines and variables against the source.
- Rebuild runner registration, executor configuration and CI/CD variables on the target, with secrets re-entered and verified.
- Document the cutover sequence, including the freeze window, DNS or remote changes and the rollback steps for each group.
How we hand over
- Deliver runbooks covering the transfer procedure, verification checks, known gaps and the post-cutover clean-up tasks.
- Provide a reconciliation report listing what moved, what did not and what needs manual action by your team.
- Walk your administrators through backup, upgrade and runner maintenance on the new instance before closing the engagement.
- Hand over configuration and evidence that your auditors or security reviewers can use for change records.
How we support
- Offer ongoing support, upgrades and continuous testing as a retainer after the move, scoped separately from the migration.
- Review the first weeks of pipeline and runner activity on the new instance and resolve issues found in the verification log.
- Confirm with your team which GitLab version and upgrade stops apply to the target instance before scheduling later upgrades.
- Ask for a quote built from the written scope, listing one-time migration work separately from any recurring retainer.

