Published on: December 3, 2025
13 min read
Learn how to migrate from Azure DevOps to GitLab using GitLab Professional Services migration tools — from planning and execution to follow-up tasks.

Migrating from Azure DevOps to GitLab can seem like a daunting task, but with the right approach and tools, it can be a smooth and efficient process. This guide will walk you through the steps needed to successfully migrate your projects, repositories, and pipelines from Azure DevOps to GitLab.
GitLab provides both Congregate (maintained by GitLab Professional Services organization) and a built-in Git repository import for migrating projects from Azure DevOps (ADO). These options support repository-by-repository or bulk migration and preserve git commit history, branches, and tags. With Congregate and professional services tools, we support additional assets such as wikis, work items, CI/CD variables, container images, packages, pipelines, and more (see this feature matrix). Use this guide to plan and execute your migration and complete post-migration follow-up tasks.
Enterprises migrating from ADO to GitLab commonly follow a multi-phase approach:
High-level migration phases:
graph LR
subgraph Prerequisites
direction TB
A["Set up identity provider (IdP) and<br/>provision users"]
A --> B["Set up runners and<br/>third-party integrations"]
B --> I["Users enablement and<br/>change management"]
end
subgraph MigrationPhase["Migration phase"]
direction TB
C["Migrate source code"]
C --> D["Preserve contributions and<br/> format history"]
D --> E["Migrate work items and<br/>map to <a href="https://docs.gitlab.com/topics/plan_and_track/">GitLab Plan <br/>and track work"]
end
subgraph PostMigration["Post-migration steps"]
direction TB
F["Create or translate <br/>ADO pipelines to GitLab CI"]
F --> G["Migrate other assets<br/>packages and container images"]
G --> H["Introduce <a href="https://docs.gitlab.com/user/application_security/secure_your_application/">security</a> and<br/>SDLC improvements"]
end
Prerequisites --> MigrationPhase
MigrationPhase --> PostMigration
style A fill:#FC6D26
style B fill:#FC6D26
style I fill:#FC6D26
style C fill:#8C929D
style D fill:#8C929D
style E fill:#8C929D
style F fill:#FFA500
style G fill:#FFA500
style H fill:#FFA500
To plan your migration, ask these questions:
Determine your timeline, as it will largely dictate your migration approach. Identify champions or groups familiar with both ADO and GitLab platforms (such as early adopters) to help drive adoption and provide guidance.
Inventory what you need to migrate:
Use GitLab Professional Services's Evaluate tool to produce a complete inventory of your entire Azure DevOps organization, including repositories, PR counts, contributor lists, number of pipelines, work items, CI/CD variables and more. If you're working with the GitLab Professional Services team, share this report with your engagement manager or technical architect to help plan the migration.
Migration timing is primarily driven by pull request count, repository size, and amount of contributions (e.g. comments in PR, work items, etc). For example, 1,000 small repositories with few PRs and limited contributors can migrate much faster than a smaller set of repositories containing tens of thousands of PRs and thousands of contributors. Use your inventory data to estimate effort and plan test runs before proceeding with production migrations.
Compare inventory against your desired timeline and decide whether to migrate all repositories at once or in batches. If teams cannot migrate simultaneously, batch and stagger migrations to align with team schedules. For example, in Professional Services engagements, we organize migrations into waves of 200-300 projects to manage complexity and respect API rate limits, both in GitLab and ADO.
GitLab's built-in repository importer migrates Git repositories (commits, branches, and tags) one-by-one. Congregate is designed to preserve pull requests (known in GitLab as merge requests), comments, and related metadata where possible; the simple built-in repository import focuses only on the Git data (history, branches, and tags).
Items that typically require separate migration or manual recreation:
Review what each tool (Congregate vs. built-in import) will migrate and choose the one that fits your needs. Make a list of any data or integrations that must be migrated or recreated manually.
Who will run the migration?
Migrations are typically run by a GitLab group owner or instance administrator, or by a designated migrator who has been granted the necessary permissions on the destination group/project. Congregate and the GitLab import APIs require valid authentication tokens for both Azure DevOps and GitLab.
Note: Congregate leverages file-based import functionality for ADO migrations and requires instance administrator permissions to run (see our documentation). If you are migrating to GitLab.com, consider engaging Professional Services. For more information, see the Professional Services Full Catalog. Non-admin account cannot preserve contribution attribution!
What organizational structure do we want in GitLab?
While it's possible to map ADO structure directly to GitLab structure, it's recommended to rationalize and simplify the structure during migration. Consider how teams will work in GitLab and design the structure to facilitate collaboration and access management. Here is a way to think about mapping ADO structure to GitLab structure:
graph TD
subgraph GitLab
direction TB
A["Top-level Group"]
B["Subgroup (optional)"]
C["Projects"]
A --> B
A --> C
B --> C
end
subgraph AzureDevOps["Azure DevOps"]
direction TB
F["Organizations"]
G["Projects"]
H["Repositories"]
F --> G
G --> H
end
style A fill:#FC6D26
style B fill:#FC6D26
style C fill:#FC6D26
style F fill:#8C929D
style G fill:#8C929D
style H fill:#8C929D
Recommended approach:
ADO Boards and work items: State of migration
It's important to understand how work items migrate from ADO into GitLab Plan (issues, epics, and boards).
Example: Migration of an individual work item to a GitLab Issue, including field accuracy and relationships:

Batching guidance:
Pipelines migration
Congregate recently introduced AI-powered conversion for multi-stage YAML pipelines from Azure DevOps to GitLab CI/CD. This automated conversion works best for simple, single-file pipelines and is designed to provide a working starting point rather than a production-ready .gitlab-ci.yml file. The tool generates a functionally equivalent GitLab pipeline that you can then refine and optimize for your specific needs.
.gitlab-ci.yml format automatically.Repository owners should review the GitLab CI/CD documentation to further optimize and enhance their pipelines after the initial conversion.
Example of converted pipelines:
# azure-pipelines.yml
trigger:
- main
variables:
imageName: myapp
stages:
- stage: Build
jobs:
- job: Build
pool:
vmImage: 'ubuntu-latest'
steps:
- checkout: self
- task: Docker@2
displayName: Build Docker image
inputs:
command: build
repository: $(imageName)
Dockerfile: '**/Dockerfile'
tags: |
$(Build.BuildId)
- stage: Test
jobs:
- job: Test
pool:
vmImage: 'ubuntu-latest'
steps:
- checkout: self
# Example: run tests inside the container
- script: |
docker run --rm $(imageName):$(Build.BuildId) npm test
displayName: Run tests
- stage: Push
jobs:
- job: Push
pool:
vmImage: 'ubuntu-latest'
steps:
- checkout: self
- task: Docker@2
displayName: Login to ACR
inputs:
command: login
containerRegistry: '<your-acr-service-connection>'
- task: Docker@2
displayName: Push image to ACR
inputs:
command: push
repository: $(imageName)
tags: |
$(Build.BuildId)
# .gitlab-ci.yml
variables:
imageName: myapp
stages:
- build
- test
- push
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $imageName:$CI_PIPELINE_ID -f $(find . -name Dockerfile) .
only:
- main
test:
stage: test
image: docker:latest
services:
- docker:dind
script:
- docker run --rm $imageName:$CI_PIPELINE_ID npm test
only:
- main
push:
stage: push
image: docker:latest
services:
- docker:dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker tag $imageName:$CI_PIPELINE_ID $CI_REGISTRY/$CI_PROJECT_PATH/$imageName:$CI_PIPELINE_ID
- docker push $CI_REGISTRY/$CI_PROJECT_PATH/$imageName:$CI_PIPELINE_ID
only:
- main
Final checklist:
After planning, execute migrations in stages, starting with trial runs. Trial migrations help surface org-specific issues early and let you measure duration, validate outcomes, and fine-tune your approach before production.
What trial migrations validate:
Downtime guidance:
Batching guidance:
Recommended steps:
.gitlab-ci.yml) or converted pipelines where applicable.| GitLab | Azure DevOps | Similarities & Key Differences |
|---|---|---|
| Group | Organization | Top-level namespace, membership, policies. ADO org contains Projects; GitLab Group contains Subgroups and Projects. |
| Group or Subgroup | Project | Logical container, permissions boundary. ADO Project holds many repos; GitLab Groups/Subgroups organize many Projects. |
| Project (includes a Git repo) | Repository (inside a Project) | Git history, branches, tags. In GitLab, a "Project" is the repo plus issues, CI/CD, wiki, etc. One repo per Project. |
| Merge Request (MR) | Pull Request (PR) | Code review, discussions, approvals. MR rules include approvals, required pipelines, code owners. |
| Protected Branches, MR Approval Rules, Status Checks | Branch Policies | Enforce reviews and checks. GitLab combines protections + approval rules + required status checks. |
| GitLab CI/CD | Azure Pipelines | YAML pipelines, stages/jobs, logs. ADO also has classic UI pipelines; GitLab centers on .gitlab-ci.yml. |
| .gitlab-ci.yml | azure-pipelines.yml | Defines stages/jobs/triggers. Syntax/features differ; map jobs, variables, artifacts, and triggers. |
| Runners (shared/specific) | Agents / Agent Pools | Execute jobs on machines/containers. Target via demands (ADO) vs tags (GitLab). Registration/scoping differs. |
| CI/CD Variables (project/group/instance), Protected/Masked | Pipeline Variables, Variable Groups, Library | Pass config/secrets to jobs. GitLab supports group inheritance and masking/protection flags. |
| Integrations, CI/CD Variables, Deploy Keys | Service Connections | External auth to services/clouds. Map to integrations or variables; cloud-specific helpers available. |
| Environments & Deployments (protected envs) | Environments (with approvals) | Track deploy targets/history. Approvals via protected envs and manual jobs in GitLab. |
| Releases (tag + notes) | Releases (classic or pipelines) | Versioned notes/artifacts. GitLab Release ties to tags; deployments tracked separately. |
| Job Artifacts | Pipeline Artifacts | Persist job outputs. Retention/expiry configured per job or project. |
| Package Registry (NuGet/npm/Maven/PyPI/Composer, etc.) | Azure Artifacts (NuGet/npm/Maven, etc.) | Package hosting. Auth/namespace differ; migrate per package type. |
| GitLab Container Registry | Azure Container Registry (ACR) or others | OCI images. GitLab provides per-project/group registries. |
| Issue Boards | Boards | Visualize work by columns. GitLab boards are label-driven; multiple boards per project/group. |
| Issues (types/labels), Epics | Work Items (User Story/Bug/Task) | Track units of work. Map ADO types/fields to labels/custom fields; epics at group level. |
| Epics, Parent/Child Issues | Epics/Features | Hierarchy of work. Schema differs; use epics + issue relationships. |
| Milestones and Iterations | Iteration Paths | Time-boxing. GitLab Iterations (group feature) or Milestones per project/group. |
| Labels (scoped labels) | Area Paths | Categorization/ownership. Replace hierarchical areas with scoped labels. |
| Project/Group Wiki | Project Wiki | Markdown wiki. Backed by repos in both; layout/auth differ slightly. |
| Test reports via CI, Requirements/Test Management, integrations | Test Plans/Cases/Runs | QA evidence/traceability. No 1:1 with ADO Test Plans; often use CI reports + issues/requirements. |
| Roles (Owner/Maintainer/Developer/Reporter/Guest) + custom roles | Access levels + granular permissions | Control read/write/admin. Models differ; leverage group inheritance and protected resources. |
| Webhooks | Service Hooks | Event-driven integrations. Event names/payloads differ; reconfigure endpoints. |
| Advanced Search | Code Search | Full-text repo search. Self-managed GitLab may need Elasticsearch/OpenSearch for advanced features. |
Enjoyed reading this blog post or have questions or feedback? Share your thoughts by creating a new topic in the GitLab community forum.
Share your feedback