# GitHub-to-GitLab migration the easy way

> Source: <https://about.gitlab.com/blog/github-to-gitlab-migration-made-easy/>
> Published: 2026-08-19 20:23:52+00:00

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](https://about.gitlab.com/topics/devsecops/) teams, the fear of "heavy lifting" is the biggest obstacle between them and a single, AI-native [DevSecOps platform](https://about.gitlab.com/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](https://about.gitlab.com/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](https://docs.gitlab.com/user/project/import/github/) 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](https://docs.gitlab.com/user/project/import/#user-contribution-and-membership-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](https://docs.gitlab.com/api/import/).

```
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:`

keyword`actions/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](https://about.gitlab.com/gitlab-duo-agent-platform/) includes a [Convert to GitLab CI/CD flow](https://docs.gitlab.com/user/duo_agent_platform/flows/foundational_flows/convert_to_gitlab_ci/) 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](https://docs.gitlab.com/user/gitlab_duo_chat/agentic_chat/) can pull in context from your issues, merge requests, and pipelines to answer questions in-platform, and [Duo Code Suggestions](https://docs.gitlab.com/user/project/repository/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](https://docs.gitlab.com/ci/yaml/lint/); 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]

# GitLab-maintained SAST is one line away
# a nice upgrade to add during migration.
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](https://docs.gitlab.com/ci/).

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.
