Updated on: August 18, 2026
12 min read
Learn how easy it is to migrate from GitHub to GitLab using GitLab's project importer and GitLab Duo AI.
If you're evaluating a move to GitLab, the first question is almost always the same: How hard is the migration? For most DevSecOps teams, the fear of "heavy lifting" is the biggest obstacle between them and a single, AI-native DevSecOps platform.
Migrating to GitLab is easier than ever. GitLab's built-in importer moves the vast majority of your project data automatically and in the background. Additionally, translating your CI/CD pipeline, one part that has traditionally required a human, is now largely handled by GitLab Duo. Duo, our AI-native assistant, can convert GitHub Actions workflows into GitLab CI/CD.
In this post, I'll walk through the full migration end to end:
Let's get started.
GitLab's built-in GitHub importer is accessed directly from GitLab's project creation UI, and it runs as a background job, meaning you can kick off an import and walk away. Most of your project data comes across automatically. A smaller set of items are optional toggles or have caveats worth knowing about up front.
The table below breaks down what migrates and how:
| Data | Status |
|---|---|
| Git repository, branches, tags, and commit history | ✅ Automatic. Includes fork branches for open pull requests. |
| Issues | ✅ Automatic |
| Pull requests (become merge requests) | ✅ Automatic. Includes reviews, review comments, suggestions, assigned reviewers, and "merged by" info. |
| Issue and pull request comments | ✅ Automatic |
| Labels and milestones | ✅ Automatic |
| Release notes content | ✅ Automatic |
| Wiki pages | ✅ Automatic |
| Branch protection rules | ✅ Automatic |
| Issue and pull request events | ✅ Automatic |
| Collaborators (members) | ⚠️ With caveats. Optional toggle (on by default). Requires the read:org scope; GitHub roles are mapped to GitLab roles (see below). GitHub Enterprise Cloud custom roles aren't supported and are added manually. |
| Markdown attachments (in descriptions, comments, releases) | ⚠️ With caveats. Optional toggle. Attachments in private repos from before May 2023 can't be imported (a GitHub limitation); GitHub Enterprise Server imports only images and video. |
| Large comment volumes (~30,000+) | ⚠️ With caveats. Enable the alternative comments import to work around GitHub's per-issue API limits. Comments from before 2017 may import as separate threads. |
| Git LFS objects | ⚠️ With caveats. LFS must be enabled on the destination project before the import runs, or objects are silently skipped. |
| GitHub Actions workflows | 🛠️ Manual (AI-assisted). Converted to .gitlab-ci.yml . GitLab Duo can do most of this for you. |
| Secrets → CI/CD variables | 🛠️ Manual. Recreate secrets as |
GitHub and GitLab use different naming conventions, so a mapping is performed during migration. When collaborators are imported, GitHub roles map to GitLab roles as follows:
| GitHub role | GitLab role |
|---|---|
| Read | Guest |
| Triage | Reporter |
| Write | Developer |
| Maintain | Maintainer |
| Admin | Owner |
The prerequisites have gotten simpler over the years. Notably, you no longer need every GitHub author to have a matching public email address on GitLab. GitLab now handles attribution automatically with user contribution mapping (GitLab 17.8+). More on that below.
To import from GitHub.com or GitHub Enterprise Server into either GitLab.com or a GitLab Self-Managed instance, you need:
A couple of situational prerequisites:
read:org
scope on your token and at least Write or Maintain access on the GitHub project.Previously, contribution history only carried over cleanly if each GitHub user's public email matched their GitLab email. Today, GitLab creates placeholder users for any GitHub author, assignee, or reviewer without a matching GitLab account, and preserves their contributions.
After the import, a group Owner or Maintainer goes to Members → Placeholders and reassigns each placeholder to the real GitLab user, who then accepts the reassignment. This means you can migrate first and sort out attribution afterward.
There are three ways to run the import. Pick the one that fits your source and scale.
This is the fastest path for most teams.
Optional toggles:
| Toggle | Default | Use it when |
|---|---|---|
| Import collaborators | On | You want project members brought over with role mapping. |
| Import Markdown attachments | Off | You want images and files embedded in descriptions, comments, and releases. |
| Use alternative comments import | Off | Your project has ~30,000+ comments and you're hitting GitHub API limits. |
If OAuth isn't configured, authenticate with a token instead.
github.com/settings/tokens/new
with the repo
scope (add read:org
if you're importing collaborators or LFS). Note: Fine-grained tokens aren't supported.For migrating many repositories at once, scripting a move, or importing public repos you don't own, use the import API.
curl --request POST \
--url "https://gitlab.com/api/v4/import/github" \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"personal_access_token": "<github_classic_pat>",
"repo_id": 12345678,
"target_namespace": "my-group",
"new_name": "imported-project",
"optional_stages": {
"single_endpoint_notes_import": true,
"attachments_import": true,
"collaborators_import": true
}
}'
Track progress with:
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.com/api/v4/projects/<project_id>/import"
You'll need a GitHub classic PAT with the repo
scope plus a GitLab PAT with the api
scope.
Once the import finishes, do a quick sanity check:
Re-importing creates a fresh copy (you can't import into an existing project), so if something looks off, delete and re-run.
Migration doesn't have to be a hard cutover. Most teams move gradually, keeping GitHub in play while they stand up GitLab, validate their pipelines, and bring people over team by team. GitLab is designed to coexist with GitHub during that transition, so you can adopt it incrementally instead of flipping a switch overnight.
Here are the main ways the two platforms work side by side while you migrate:
.gitlab-ci.yml
conversion against real commits while the source of truth still lives in GitHub.CI/CD is one part of the migration that isn't automatic. It's your chance to modernize your pipelines. Many core concepts map cleanly between the two platforms:
| GitHub Actions | GitLab CI/CD |
|---|---|
.github/workflows/*.yml |
.gitlab-ci.yml |
| Workflow | Pipeline |
| Job | Job |
| Step in a job | Line in script: |
Event triggers (on: ) |
rules: / workflow: |
runs-on: / container: |
image: and runner tags: |
| Actions Marketplace | |
strategy.matrix
parallel.matrix
actions/checkout
actions/cache
cache:
keywordactions/upload-artifact
artifacts:
keywordA key difference to keep in mind: In GitLab, stages run sequentially and jobs within a stage run in parallel, and you can use needs:
to build an explicit Directed Acyclic Graph (DAG).
GitLab Duo Agent Platform includes a Convert to GitLab CI/CD flow that translates your GitHub Actions workflows into .gitlab-ci.yml
for you, so you're reviewing a draft instead of rewriting from scratch.
GitLab Duo helps across the whole migration, not just conversion:
needs:
DAGs, and CI/CD components.GitLab Duo Agentic Chat can pull in context from your issues, merge requests, and pipelines to answer questions in-platform, and Duo Code Suggestions help as you edit .gitlab-ci.yml
in the Web IDE.
If GitLab Duo isn't available in your environment, the same conversions work well with a frontier model using a structured prompt. A prompt that produces reliable results:
Convert this GitHub Actions workflow to GitLab CI/CD. Preserve matrix builds, job dependencies, artifacts, and conditional rules. Return a valid
.gitlab-ci.yml
.
A few guardrails when using any AI assistant for migration: Never paste secrets, tokens, or internal hostnames; always validate generated YAML with GitLab's CI Lint tool; and treat the output as a reviewable draft, not a final commit.
Whether GitLab Duo drafts the code or you write it by hand, it helps to see the translation. Here's a typical GitHub Actions build-and-test workflow:
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '18' }
- run: npm install
- run: npm test
- run: npm run build
And the equivalent GitLab CI/CD. Notice checkout disappears (GitLab clones for you), runs-on
becomes image
, and each step becomes a line in script:
:
stages: [test, build]
include:
- template: Jobs/SAST.gitlab-ci.yml
test:
stage: test
image: node:18
script:
- npm install
- npm test
build:
stage: build
image: node:18
script:
- npm install
- npm run build
artifacts:
paths: [dist/]
Matrix builds translate just as directly: strategy.matrix
becomes parallel.matrix
:
test:
image: node:${NODE_VERSION}
parallel:
matrix:
- NODE_VERSION: ['16', '18', '20']
script:
- npm install
- npm test
And conditional deployments map to rules:
:
deploy:
stage: deploy
script: ./deploy.sh
rules:
- if: $CI_COMMIT_BRANCH == "main"
Once you commit .gitlab-ci.yml
, GitLab runs the pipeline immediately. To learn more, see the GitLab CI/CD documentation.
GitHub isn't the only source. GitLab's importer supports one-click migration from several other platforms:
We also have documentation covering migrations from:
Thanks for reading! Migration doesn't have to be the scary part of adopting a new platform. GitLab moves your data automatically and GitLab Duo handles the CI/CD conversion, so you can focus on shipping. For more, follow the links below:
Are you just managing tools or shipping innovation?
Quiz will take 5 minutes or less
Enjoyed reading this blog post or have questions or feedback? Share your thoughts by creating a new topic in the GitLab community forum.
Start building faster today
See what your team can do with the intelligent orchestration platform for DevSecOps.