Migration guide to the v2 integration
This guide walks through the process of migrating from Port's GitLab integration to the new GitLab-v2 integration.
For installation methods, live events, and where to paste YAML in Port, use the setup and configuration sections on the GitLab v2 integration page.
Overview
GitLab-v2 introduces significant authentication and performance improvements:
- Simplified authentication: Replace multiple group tokens with a single service account token.
- Enhanced performance: Faster resync with improved API efficiency.
- Typed configuration: First-class selectors for kinds such as
merge-request,member,group-with-members,project-with-members,tag,release, andbranch. - Native search and file selectors: Use
searchQueriesandincludedFilesonprojectinstead of the deprecatedfile://andsearch://mapping prefixes. For how those selectors work in mappings, see Enrich entities with file contents and Enrich entities with search queries on the capabilities page.
Changes
Authentication model
GitLab approach: Multiple group access tokens mapped to specific paths
integration:
secrets:
tokenMapping: |
{"glpat-token1": ["**/Group1/**"], "glpat-token2": ["**/Group2/**"]}
GitLab-v2 approach: Single service account token with broad access
integration:
config:
gitlabToken: "glpat-service-account-token"
The GitLab-v2 integration uses gitlabToken (not token). When using the hosted UI or Helm installer, use the parameter name exactly as it appears in the integration spec. Parameter reference and install flows live under setup on the GitLab v2 integration page.
Blueprint mapping changes
The blueprints used in GitLab-v2 have evolved to provide cleaner data structures and better relationships between entities. Understanding these changes is essential for a successful migration.
Fields that Port adds on top of the GitLab API payload use a double underscore prefix (for example .__languages and .__includedFiles), similar to other Ocean integrations. For feature-level behavior beyond this migration page, see the capabilities page.
In the YAML below, identifier, title, and blueprint are standard Port entity mapping fields (they are not GitLab integration switches):
identifier: stable unique key for the entity in your catalog (often derived from GitLab IDs or paths).title: human-readable label shown in the UI.blueprint: slug of the Port blueprint that defines the entity's schema and relations.
Those three lines stay the same when you already map GitLab projects or MRs into the same blueprints as before. What changes in each subsection are selectors (what GitLab sends into the integration) and properties (what you store on the entity).
Kind mapping changes by resource type
1. project kind (Required update)
GitLab configuration:
- kind: project
selector:
query: "true"
includeLabels: "true" # ❌ Remove: No longer supported
port:
entity:
mappings:
identifier: .path_with_namespace | gsub(" "; "")
title: .name
blueprint: '"service"'
properties:
language: .__languages | to_entries | max_by(.value) | .key
labels: .__labels # ❌ Remove: project labels not enriched in v2
GitLab-v2 configuration:
- kind: project
selector:
query: "true"
includeLanguages: "true" # ✅ Add: New selector for language detection
port:
entity:
mappings:
identifier: .path_with_namespace | gsub(" "; "")
title: .name
blueprint: '"service"'
properties:
language: .__languages | to_entries | max_by(.value) | .key
GitLab-v2 does not offer includeLabels or a project-level .__labels field. The v1 integration optionally fetched extra project label data into the payload. That enrichment path was removed to simplify the project sync and reduce API surface. Issues, merge requests, and other kinds can still expose labels where the underlying GitLab object includes them (see the issue example later on this page). If your scorecards or queries depended on project labels, move that signal to a supported surface, for example MR/issue labels, includedFiles, or searchQueries on project.
2. merge-request kind (Configuration update)
GitLab configuration:
- kind: merge-request
port:
entity:
mappings:
identifier: .id | tostring
title: .title
blueprint: '"gitlabMergeRequest"'
GitLab-v2 configuration:
- kind: merge-request
selector:
query: "true"
states: ["opened", "merged"] # ✅ Add: control which MR states are synced
updatedAfter: 90 # ✅ Add: only applies when querying non-open states (days)
port:
entity:
mappings:
identifier: .id | tostring
title: .title
blueprint: '"gitlabMergeRequest"'
properties:
creator: .author.name
reviewers: .reviewers | map(.name)
The default merge-request behavior is not equivalent between v1 and v2:
- v1 fetched all
openedMRs andclosedMRs from roughly the last 14 days. - v2 defaults to
states: ["opened"]only. TheupdatedAftervalue (default 90 days) is applied only when you also include non-open states (merged,closed,locked).
To preserve v1's catalog coverage, explicitly set states to include merged and/or closed and tune updatedAfter to your retention needs.
3. Member handling changes (Recommended modeling change)
Both v1 and v2 support project-with-members and group-with-members. The recommended modeling in v2, however, is to use a dedicated member kind and reference it via relations, rather than embedding members as properties on projects and groups.
In the selectors below, includeVerboseMemberObject only changed how verbose each member object was in the raw payload. GitLab-v2 uses a single standardized member shape, so that toggle is gone. It has nothing to do with blueprint (which only selects which Port blueprint receives the entity).
GitLab approach - Members as embedded properties:
- kind: project-with-members
selector:
query: 'true'
includeInheritedMembers: false
includeBotMembers: false
includeVerboseMemberObject: false # ❌ Remove: not supported in v2
port:
entity:
mappings:
identifier: .path_with_namespace | gsub(" "; "")
title: .name
blueprint: '"service"'
properties:
members: .__members # Members embedded as properties
- kind: group-with-members
selector:
query: 'true'
includeInheritedMembers: false
includeBotMembers: false
includeVerboseMemberObject: false # ❌ Remove: not supported in v2
port:
entity:
mappings:
identifier: .full_path
title: .name
blueprint: '"gitlabGroup"'
properties:
members: .__members # Members embedded as properties
GitLab-v2 recommended approach - Members as separate entities with relations:
# ✅ New: Dedicated member entities
- kind: member
selector:
query: 'true'
includeBotMembers: false
port:
entity:
mappings:
identifier: .username
title: .name
blueprint: '"gitlabMember"'
properties:
url: .web_url
state: .state
email: .email
locked: .locked
# ✅ Updated: Groups with member relations (not embedded properties)
- kind: group-with-members
selector:
query: 'true'
includeBotMembers: false
port:
entity:
mappings:
identifier: .full_path
title: .name
blueprint: '"gitlabGroup"'
properties:
url: .web_url
visibility: .visibility
description: .description
relations:
gitlabMembers: .__members | map(.username) # ✅ Relations, not properties
4. file kind (Search API and repository format changes)
For file resync, GitLab-v2 now uses GitLab's Advanced Search API with its specific syntax and limitations:
GitLab format:
- kind: file
selector:
files:
path: '**/package.json' # ❌ Custom glob patterns
repos:
- "backend-service" # ❌ Simple repository name
- "frontend-app"
GitLab-v2 format:
- kind: file
selector:
files:
path: 'package.json' # ✅ GitLab Advanced Search syntax (https://docs.gitlab.com/user/search/advanced_search/#use-advanced-search)
repos:
- "my-org/backend-service" # ✅ Full namespace/project path
- "my-org/frontend-app"
skipParsing: false # ✅ New option to skip parsing and return raw content
GitLab-v2's path parameter uses GitLab's Advanced Search syntax, which differs from v1's glob patterns. The search supports simple * wildcards for filename patterns like *.tf, package.json, and test_*, as well as exact file paths like src/app/main.py and infra/terraform/main.tf.
The search doesn't support path wildcards like infra/terraform/*.tf ❌, recursive patterns like **/filename ❌, or complex patterns like *.{js,ts}, [abc]*, and !exclude ❌.
When migrating complex v1 patterns, you can split them into multiple file kinds or specify exact paths for targeted files. See the Migration Pattern Comparison table below for specific examples.
5. folder kind (Repository selector changes)
For folder resync, GitLab-v2 changes the repository specification format to support branch-specific folder mapping:
GitLab format:
- kind: folder
selector:
folders:
- path: "src"
repos:
- "backend-service" # ❌ Simple repository name
- "frontend-app"
branch: "main" # ❌ Single branch for all repos
GitLab-v2 format:
- kind: folder
selector:
folders:
- path: "src"
repos:
- name: "my-org/backend-service" # ✅ Full namespace/project path
branch: "main" # ✅ Branch specified per repository
- name: "my-org/frontend-app"
branch: "develop" # ✅ Different branches per repository
6. First-class kinds with typed selectors in GitLab-v2
pipeline, job, and issue were already syncable in v1 through the integration's resync logic, but v2 promotes them (along with group, tag, release, and branch) to first-class kinds with typed selectors in port-app-config.yml. This means cleaner configuration, validation, and consistent defaults rather than hardcoded behavior.
pipeline kind:
- kind: pipeline
selector:
query: 'true'
port:
entity:
mappings:
identifier: .id | tostring
title: .name
blueprint: '"gitlabPipeline"'
job kind:
- kind: job
selector:
query: 'true'
port:
entity:
mappings:
identifier: .id | tostring
title: .name
blueprint: '"gitlabJob"'
properties:
status: .status
stage: .stage
duration: .duration
issue kind:
- kind: issue
selector:
query: 'true'
port:
entity:
mappings:
identifier: .id | tostring
title: .title
blueprint: '"gitlabIssue"'
properties:
creator: .author.name
status: .state
labels: "[.labels[]]"
relations:
project: .references.full | gsub("#.+"; "")
7. Additional first-class kinds: group, tag, release, branch
GitLab-v2's GitlabPortAppConfig exposes typed selectors for group, tag, release, and branch. If you tracked any of these in v1 via custom mappings, you can now use the dedicated kinds for cleaner configuration. See the GitLab v2 examples for usage, and Ingest files from your repositories when you rely on the file kind.
8. Visibility and minimum access level filters
GitLab-v2 introduces a top-level visibility configuration on the integration's app config to limit which projects and groups are synced based on the requesting user's access level:
visibility:
useMinAccessLevel: true
minAccessLevel: 30 # Developer
Use this to scope ingestion to projects/groups where the service account has at least a chosen access level.
9. Replacing file:// and search:// mapping prefixes
Both v1 and v2 deprecate the file:// and search:// prefixes inside Port mappings. v2 provides first-class selectors on the project kind to replace them:
includedFiles: declaratively pull file contents into project entities (replacesfile://lookups in mappings).searchQueries: declaratively run GitLab Advanced Search queries (replacessearch://lookups).
Example:
- kind: project
selector:
query: "true"
includedFiles:
- path: "package.json"
repos:
- "my-org/backend-service"
searchQueries:
- name: "todos"
query: "TODO"
When you migrate, replace any remaining file:// or search:// references in your mappings with these selectors. Read Enrich entities with file contents and Enrich entities with search queries for concrete YAML patterns.
Configuration selector changes
GitLab-v2 introduces new selector options while removing some GitLab-specific configurations that are no longer needed.
Removed selectors
The following selectors from GitLab are no longer available in GitLab-v2:
- includeLabels: optional v1 enrichment that pulled GitLab project labels into
.__labelsfor mapping. GitLab-v2 drops this path entirely (see the Why project labels went away callout under project kind on this page). UseincludeLanguageswhen you want primary language on project entities instead. - includeVerboseMemberObject: v1-only switch for the shape of member objects on member-related kinds. GitLab-v2 always uses one canonical member shape, so the flag was removed.
New selectors in GitLab-v2
GitLab-v2 introduces several new selector options that provide better control over data resync:
- includeLanguages: Enables language detection and inclusion in project entities. When set to true, the integration will analyze repository languages and include the primary language in the project properties
- states: Available for merge requests, allows you to specify which merge request states to resync (opened, closed, merged)
- updatedAfter: For merge requests, helps limit resync to recently updated merge requests by specifying the number of days to look back
File and folder mapping updates
The way you specify repositories for file and folder resync has changed significantly between versions.
| Repository Specification | GitLab Format | GitLab-v2 Format |
|---|---|---|
| File repos | "backend-service" | "my-org/backend-service" |
| Folder repos | "frontend-app" | "my-org/frontend-app" |
The namespace path format follows GitLab's standard group/project or group/subgroup/project structure, ensuring accurate repository identification even when multiple projects share the same name across different groups.
For file and folder selectors (and similar searches scoped by repo list), treat path_with_namespace style paths as required in GitLab-v2. Short names such as "backend-service" were tied to how the legacy integration resolved repos and do not behave as reliable aliases in v2. Keeping the old strings usually breaks ingestion or targets the wrong GitLab project once you have more than one project with the same name under different groups. This change applies to file/folder configuration specifically, not to fields such as identifier that still use .path_with_namespace from the project object.
Summary of key changes
The following table summarizes all the major differences between GitLab and GitLab-v2 to help you prepare for migration:
| Kind/Feature | GitLab | GitLab-v2 | Action Required |
|---|---|---|---|
| Authentication | tokenMapping (multi-token, path-scoped) | Single gitlabToken | Replace token mapping with a single service account token |
| project kind | includeLabels selector | includeLanguages selector | Replace includeLabels: true with includeLanguages: true |
| merge-request kind | Defaults to opened + ~14 days of closed MRs | Defaults to opened only; updatedAfter (default 90d) only applies for non-open states | Add non-open states explicitly and tune updatedAfter to match v1 coverage |
| Member handling | project-with-members + group-with-members (embedded) | Recommended: dedicated member kind with relations | Optional: introduce member kind and switch groups/projects to relations |
includeVerboseMemberObject | ✅ Supported | ❌ Removed | Remove from member/project/group selectors |
| file kind | Custom glob patterns + simple repo names | GitLab Advanced Search syntax + full namespace paths + skipParsing option | Update to GitLab search syntax, update repo names to org/repo format |
| folder kind | Simple repo names + single branch | Repository objects with name/branch pairs | Update repos to objects with name and branch properties |
file:// / search:// prefixes | Deprecated | Replaced by includedFiles and searchQueries on project | Migrate mapping prefixes to selectors |
| pipeline / job / issue | Available via integration resync | Promoted to first-class kinds with typed selectors | Use the typed selectors in port-app-config.yml |
| group / tag / release / branch | Custom mappings | First-class kinds with typed selectors | Optional: switch to dedicated kinds |
Migration steps
Step 1: Create a GitLab service account
Choose the appropriate setup method for your GitLab instance:
For GitLab.com (SaaS):
- Navigate to your top-level group settings
- Go to Access Tokens under the group settings
- Create a Group Service Account
For GitLab self-managed:
- Create a new user account (e.g.,
port-integration-bot) - Add this user to all relevant groups with Developer permissions or higher
Step 2: Generate the access token
- Sign in to your service account
- Navigate to User Settings > Access Tokens
- Create a new personal access token with:
- Name:
Port Integration Token - Scopes:
api(orread_api+read_repositoryfor read-only) - Expiration: Set according to your security policy
- Name:
- Save the generated token securely
The service account needs access to all groups and projects you want to sync. For webhook functionality, it also needs permission to create webhooks.
Step 3: Update your integration configuration
Replace your current token mapping configuration with the new single token approach. This step eliminates the complexity of managing multiple tokens while ensuring the service account has appropriate access across your GitLab instance. When you are ready to paste the full mapping into Port, use the YAML editor described under configuration on the GitLab v2 integration page.
Before: GitLab configuration with token mapping (click to expand)
integration:
secrets:
tokenMapping: |
{
"glpat-old-token-1": ["**/DevTeam/**", "**/BackendServices/**"],
"glpat-old-token-2": ["**/FrontendApps/**"]
}
After: GitLab-v2 configuration with single token (click to expand)
integration:
config:
gitlabToken: "glpat-your-new-service-account-token"
When your mapping lives in a repository
If you keep the integration mapping in a repository (for example port-app-config.yml), you will need short-lived edits in the Port UI while you validate selector changes. For each pass, copy only the relevant resources blocks into the mapping in Port, run the steps below, then copy the final YAML back to the repository. The configuration section on the GitLab v2 integration page explains how the mapping is stored and synced.
Step 4: Update resource selectors and blueprints
Review your mapping configuration for deprecated selectors and blueprint references. Cross-check the examples under Blueprint mapping changes and the Summary of key changes table before you deploy.
Update each kind mapping specifically:
-
For
projectkinds:- Remove
includeLabels: truefrom selectors - Add
includeLanguages: trueto selectors - Remove
labels: .__labelsfrom property mappings
- Remove
-
For
merge-requestkinds:- To preserve v1's coverage of closed MRs, add states such as
states: ["opened", "merged", "closed"]. Without this, v2 syncsopenedonly. - Tune
updatedAfter(in days, default 90) to bound how far back closed/merged MRs are fetched. v1's behavior was roughly a 14-day window. - Simplify reviewer mappings to
reviewers: .reviewers | map(.name).
- To preserve v1's coverage of closed MRs, add states such as
-
For member handling (recommended modeling change):
- Optional: introduce a dedicated
memberkind and switch groups/projects to use relations instead of embedded properties. - You can keep
project-with-membersandgroup-with-membersif you prefer to migrate without changing your modeling, both are still supported in v2. - Remove the deprecated
includeVerboseMemberObjectselector.includeInheritedMembersandincludeBotMembersremain supported.
- Optional: introduce a dedicated
-
For
filekinds:- Update file paths to use GitLab Advanced Search syntax (change
'**/package.json'to'*package.json') - Update all repo specifications from
"repo-name"to"org/repo-name"format - Optionally add
skipParsing: falseto selector if you want to control content parsing
- Update file paths to use GitLab Advanced Search syntax (change
-
For
folderkinds:- Update all repo specifications from simple strings to objects with
nameandbranchproperties - Change from
repos: ["repo-name"]torepos: [{"name": "org/repo-name", "branch": "main"}] - Take advantage of per-repository branch specification for more flexible folder mapping
- Update all repo specifications from simple strings to objects with
-
Adopt first-class kinds where helpful:
- Use the typed
pipeline,job, andissuekinds for CI/CD and issue tracking. - Use the typed
group,tag,release, andbranchkinds if you previously approximated these with custom mappings. - Replace any remaining
file://andsearch://mapping prefixes withincludedFilesandsearchQueriesselectors on theprojectkind.
- Use the typed
Step 5: Update file and folder mappings
Update any file or folder mappings to use GitLab's Advanced Search API syntax and repository formats required by GitLab-v2. For selector semantics and limitations, see Ingest files from your repositories on the capabilities page.
-
Update file paths to GitLab Advanced Search syntax:
- Change
path: '**/package.json'topath: 'package.json' - Change
path: '**/*.yml'topath: '*.yml' - GitLab-v2 uses GitLab's Advanced Search API which supports
*wildcards but follows GitLab's search syntax
- Change
-
Update repository specifications:
- Change repository names to include the full group path
- Review all
reposspecifications in your file and folder kinds
-
Test the updated configuration with a small subset first
Example file kind transformation:
# GitLab format
- kind: file
selector:
files:
path: '**/package.json' # ❌ Custom glob pattern
repos:
- "backend-service" # ❌ Simple name
# GitLab-v2 format
- kind: file
selector:
files:
path: '*package.json' # ✅ GitLab Advanced Search syntax
repos:
- "my-org/backend-service" # ✅ Full namespace
skipParsing: false # ✅ Optional parsing control
Example folder kind transformation:
# GitLab format
- kind: folder
selector:
folders:
- path: "src"
repos:
- "backend-service" # ❌ Simple name
branch: "main" # ❌ Single branch for all repos
# GitLab-v2 format
- kind: folder
selector:
folders:
- path: "src"
repos:
- name: "my-org/backend-service" # ✅ Full namespace with branch object
branch: "main"
- name: "my-org/frontend-app" # ✅ Different branches per repo
branch: "develop"
Migration Pattern Comparison
| V1 Pattern | V2 Pattern | What it matches | Migration Notes |
|---|---|---|---|
**/infra/**/terraform/*.{tf,tfvars} | *.tf + *.tfvars | Terraform files anywhere | Split into multiple file kinds |
**/infra/**/terraform/*.{tf,tfvars} | infra/prod/terraform/main.tf + infra/staging/terraform/main.tf | Terraform files in specific paths | Split into multiple file kinds with exact paths |
**/package.json | package.json | Files named "package.json" anywhere | Searches entire repository |
**/Dockerfile | Dockerfile | All Dockerfiles anywhere | Searches entire repository |
**/docker-compose.yml | docker-compose.yml | Docker Compose files anywhere | Searches entire repository |
GitLab-v2 follows GitLab's Advanced Search syntax which supports:
*for partial matches (e.g.,*package.jsonor*.yml)filename:prefix for filename searches- Other advanced search operators
See GitLab's documentation for the complete list of supported search patterns and limitations.
This change ensures accurate repository identification and prevents conflicts when multiple repositories share the same name across different GitLab groups.
Step 6: Deploy and verify
- Deploy the GitLab-v2 integration with your updated configuration (follow setup on the GitLab v2 integration page for hosted, self-hosted, or CI installs)
- Trigger a manual resync from Port's UI to initiate data resync
- Verify data consistency:
- Check that all expected projects are resynced
- Confirm merge requests and issues are updating correctly
- Test webhook functionality if enabled
- Monitor the logs for any authentication or permission issues
Consider running both GitLab and GitLab-v2 integrations temporarily to compare results before fully switching over.
Step 7: Clean up GitLab integration
Once GitLab-v2 is working correctly:
- Disable the GitLab integration
- Revoke the old group access tokens
- Remove GitLab configuration files
- Update any documentation referencing the old setup
This cleanup ensures security by removing unused tokens and prevents confusion from having multiple configurations.
Webhooks and self-managed differences
The GitLab and GitLab-v2 integrations expose different knobs for live events. Self-hosted installs should align base_url / networking with the guidance in setup so GitLab can reach the integration for webhook delivery:
- GitLab (v1) exposed configuration like
appHost,useSystemHook, andtokenGroupHooksOverrideMappingto control where and how webhooks were registered. - GitLab-v2 registers group webhooks automatically when the integration is configured with a reachable
base_urland a token that can create webhooks. On Port's hosted (SaaS) deployment, live events are enabled by default (liveEvents.enabled: truein the integration spec).
For self-managed setups, ensure that:
- The integration is reachable from your GitLab instance at a stable URL (the equivalent of v1's
appHost). - The service account token has permission to create and manage group webhooks for the groups you want to sync.
GitOps
The v1 integration declared a gitops feature in its spec; the v2 integration spec lists the exporter feature only. If you rely on GitOps workflows (committing entity manifests to GitLab and having Port sync them), read the GitLab v2 GitOps guide and the GitOps section on the GitLab v2 integration page before fully decommissioning v1.
New features in GitLab-v2
Pipeline jobs support
GitLab-v2 introduces comprehensive pipeline job tracking, allowing you to monitor CI/CD job status, stages, and duration. This feature provides detailed insight into your build and deployment processes.
- kind: job
selector:
query: 'true'
port:
entity:
mappings:
identifier: .id | tostring
title: .name
blueprint: '"gitlabJob"'
properties:
status: .status
stage: .stage
duration: .duration
Enhanced group management
Groups in GitLab-v2 provide cleaner member resync and better hierarchy handling. The separation of member and group concerns allows for more flexible data modeling and improved relationship management between users, groups, and projects.
Troubleshoot common issues
For integration health checks and common setup errors, also see Troubleshooting on the GitLab v2 integration page.
| Issue | Cause | Solution |
|---|---|---|
| Authentication errors | Service account lacks access to required groups | Verify service account has been added to all relevant groups with appropriate permissions |
| Missing repositories | Incorrect namespace formatting in GitLab-v2 | Ensure repository paths use the full namespace/project format |
| Missing member data | Member selectors not aligned with v2 (e.g. relying on removed includeVerboseMemberObject) | Remove unsupported selectors. Either keep project-with-members / group-with-members, or switch to the dedicated member kind with relations. |
| Member relation errors | Members modeled as embedded properties when relations were expected | Update group-with-members (or project-with-members) to use relations to a member blueprint instead of embedded member properties |
| Missing closed/merged MRs after migration | v2 default states: ["opened"] and updatedAfter window | Set states to include merged / closed and tune updatedAfter to your retention needs |
| Webhook failures | Service account lacks webhook creation permissions | Check that service account has sufficient privileges to create and manage webhooks |
| File sync issues | Incorrect search syntax | Update file paths to use GitLab Advanced Search syntax (e.g., *package.json instead of **/package.json) |
| Repository not found errors | Outdated repository names in file/folder selectors | Update repo names to use full namespace format (org/repo) |
If you encounter issues during migration:
- Check the integration logs in Port's UI for specific error messages
- Verify your service account permissions in GitLab to ensure proper access
- Contact Port support with your specific error messages and configuration details
This migration ensures your GitLab integration benefits from improved security through centralized token management, enhanced performance with optimized API calls, and new capabilities including pipeline job tracking while maintaining all your existing data resync patterns.