Migrating from GitLab.com to self-hosted GitLab
When a team moves from GitLab.com to its own instance, the code is only part of the job. Issues, merge requests, labels, milestones and their history also need to move, preferably without manual copying.
GitLab has a built-in method for this called direct transfer. Your instance connects to GitLab.com over HTTPS with a personal access token and copies a group together with its projects. The import runs in the background, and you can check every migrated item when it finishes.
This guide shows the full migration of one group: preparing GitLab.com, starting the import, checking the result and finishing the steps that direct transfer does not cover. All steps were tested on a live migration from GitLab.com to GitLab 19.5 on Ubuntu 26.04.
What you need before migration
A self-hosted GitLab instance that is installed and running. If you do not have one yet, install it first, see the guide How to install GitLab on a dedicated server.
Administrator access to your GitLab instance.
The Owner role in the group you want to migrate on GitLab.com. The import page lists only groups where you are an Owner.
Outbound HTTPS access from your server to https://gitlab.com.
Enough free disk space on the server for all repositories you migrate, and free space in /tmp: GitLab unpacks temporary migration archives there.
GitLab.com always runs a newer version than a self-hosted instance. In our test, this did not prevent the migration.
Throughout this guide, replace gitlab.example.com with the domain of your instance.
What is migrated
Direct transfer moves:
- the group and its projects;
- repositories with all branches and commit history, commit hashes stay the same;
- issues and merge requests with their original numbers;
- labels, milestones and activity history;
- original creation dates.
Imported contributions and some memberships are first linked to placeholder users. You reassign them to real accounts on your instance after the import.
Direct transfer does not migrate:
- CI/CD variables, pipeline schedule variables and pipeline triggers;
- deploy keys and deploy tokens;
- container registry images and the package registry;
- CI/CD job logs and job artifacts;
- webhooks;
- environments and feature flags;
- merge request approval rules and linked issues;
- remote mirrors.
Pipelines are migrated as records with their statuses, but without job logs and artifacts.
Plan how you will restore these items before you start. CI/CD variables, deploy keys and deploy tokens often contain passwords and API keys, so you recreate them manually after the import. Container images and packages are not copied: push them to your instance again or rebuild them with CI.
Prepare your GitLab.com account
Make your email public. On GitLab.com, select your avatar in the top right corner and then Edit profile. In Public email, select your email address and select Update profile settings at the bottom of the page.

Create a personal access token. Your instance uses this token to read your data on GitLab.com. In the left menu of your profile, go to Access > Personal access tokens.

Select Generate token and then Legacy token.

Fill in the form:
- Token name: any name, for example
migration. - Expiration date: a few days are enough for the migration.
- Select scopes: select
api. Do not confuse it withread_api.


Select Generate token. Copy the token, it starts with glpat-, and save it in a safe place. GitLab.com shows it only once.

Do not send the token in chats or emails. Anyone who has it gets full API access to your GitLab.com account until the token expires or is revoked.
Enable direct transfer on your instance
Sign in to your GitLab instance as an administrator. Select Admin in the top right corner, go to Settings > General and expand Import and export settings. Under Allow migrating GitLab groups and projects by direct transfer, select the Enabled checkbox and select Save changes.

In the same section, check two settings that affect the reassignment of users after the import:
- Allow contribution mapping to administrators: when it is enabled, you can reassign contributions to administrator accounts too;
- Skip confirmation when administrators reassign placeholder users: when it is enabled, contributions are reassigned immediately, without approval from the user.
Connect your instance to GitLab.com
Select Create new (the plus icon) in the top bar, then New group > Import group.

Fill in the connection form:
- GitLab source instance base URL:
https://gitlab.com - Personal access token: the token you created on GitLab.com.
Select Connect instance.

The Import groups by direct transfer page opens with a list of your groups on GitLab.com.

Start the import
Find the group in the list. In Path of the new group, open Select parent group and choose where to place the group:
- No parent creates a top-level group, the same as on GitLab.com;
- an existing group on your instance imports the group into it as a subgroup. If the parent group is private, public groups and projects from GitLab.com become private after the import.
This choice is required. Until you select a parent group, GitLab shows Select a parent group and does not start the import.
Check the path of the new group, by default it matches the name on GitLab.com. Keep Import user memberships selected so that group members are carried over.

Do not change the group and its projects on GitLab.com after the import starts. Changes made during the migration may not reach your instance. Ask the team to stop pushing code and editing issues until the import is complete.
Select Import with projects in the row of the group. The status changes to Importing.
If there is no Import with projects button, check Default minimum role required to create projects in Admin > Settings > General > Visibility and access controls. When it is set to No one, GitLab cannot import projects.

The import runs in the background, so you can close the page. The time depends on the number and size of the projects: in our test, a group with two small projects took about two minutes.
Check the result
When the import finishes, the status changes to Complete.
If the status is Failed or Partially completed, select Show errors to see which items were not imported. You can repeat a failed or partial migration from the same page or through the API.

Select Migration details. The Migration history page shows the group and each project with its own status.

Open the imported group and check its content.
Projects. All projects of the group must be in the list.

Issues and merge requests. Open Work items and Merge requests in the group menu. Numbers, labels and milestones are kept, and the imported items have the Imported badge.

Repository. In a project, go to Code > Branches. Check that all expected branches are present and that their latest commits match the source repository.

The authors of issues and merge requests are shown as Placeholder users, for example Placeholder John Smith. This is expected, you reassign them later in this guide.
If a merge request shows Cannot be merged automatically, open it and check the Changes tab. If it shows no changes, the merge request had no commits on GitLab.com either, so there is nothing to merge. This is not a migration error.

Recreate CI/CD variables
On GitLab.com, open the project and go to Settings > CI/CD, expand Variables and note every variable with its options, for example Protected and Masked.

On your instance, open the same project and the same section. The list of project variables is empty.

Select Add variable and create each variable with the same key, value and options. Repeat this for group variables if you use them.
Pipeline schedules are migrated without their variables. If your schedules use variables, open Build > Pipeline schedules in the project and add them to each schedule.
Reassign placeholder users
During the import, GitLab creates placeholder users for the authors of contributions and for imported members from GitLab.com. To link their issues, merge requests and comments to real accounts, reassign the placeholders. Create the accounts of your team on the instance first, so that you have someone to reassign to.
Open the top-level group, the imported group itself or its parent if you imported it as a subgroup, and go to Manage > Members, then select the Placeholders tab. You need the Owner role in this group. The Awaiting reassignment list shows all placeholder users.

In the row of a placeholder, open Select user and choose the account on your instance. Check that the right account is selected in the right row, then select Reassign and Confirm reassignment.

Reassignment cannot be undone. A wrong choice gives one person's contributions and access to another user, so check every row before you confirm.
The status changes to Reassigning. After a minute, refresh the page: the placeholder moves to the Reassigned tab.


The issue now shows the real account as its author, and the original creation date is kept.

If Skip confirmation when administrators reassign placeholder users is enabled, as on our test instance, contributions are reassigned immediately. If it is disabled, the selected user must accept the reassignment first.
Service accounts of GitLab.com, for example Duo Code Review, also come as placeholders. They have no match on your instance: leave them as they are or select Do not reassign. For many users, use Reassign with CSV file on the same tab.
Finish the migration
Revoke the token. On GitLab.com, go to Access > Personal access tokens and select Revoke next to the migration token.
Update the remote address on developer machines. In every local copy of a repository, point Git to your instance:
git remote set-url origin [email protected]:group/project.git
Replace group/project with the path of the project on your instance.
Set up integrations and automation again. Webhooks, deploy keys, deploy tokens and pipeline triggers are not migrated, so recreate them on your instance. Register CI/CD runners with your instance. Reconnect external services, such as chat notifications or issue trackers, in the settings of each project.
Run new pipelines on imported merge requests. Imported pipelines keep their original status and can satisfy Pipelines must succeed without running again. Before you merge an imported merge request, run a new pipeline for it on your instance.
Close the old copy. When everything works on your instance, archive or delete the group on GitLab.com so that nobody keeps working there.
To choose a server for your GitLab instance, see the Dedicated servers for GitLab page.