doc/user/packages/workflows/project_registry.md
{{< details >}}
{{< /details >}}
Organize your packages by type in dedicated projects within a top-level artifact management group. This approach provides clear ownership and type-specific policies.
Use this approach when you want to:
To effectively organize and manage your packages with this approach, you should:
The following example provides an overview of how you should structure your top-level group and projects:
company_namespace/artifact_management/ # top-level group
├── java-packages/ # Maven packages
├── node-packages/ # npm packages
├── python-packages/ # PyPI packages
├── docker-images/ # Container registry
├── terraform-modules/ # Terraform modules
├── nuget-packages/ # NuGet packages
└── generic-packages/ # Generic file packages
[!note] Some organizations prefer additional separation based on package lifecycle or stability. For example, you might create separate projects for
java-releases/andjava-snapshots/. This way, you can apply different cleanup policies, access controls, or approval workflows for stable packages and development packages.
Create a new top-level group for artifact management:
Artifact Management or similar.Create projects for each package type you need:
java-packages or node-packages.Start with the package types your organization uses most, then expand the structure as you adopt additional package formats. This approach scales naturally while maintaining security and ease of use.
Configure group settings:
Authentication varies based on your use case. Refer to the suggestions below. For more information about authentication, see Authenticate with the registry
For local development (developers):
For CI/CD pipelines:
For external systems:
Create a group deploy token for organization-wide package consumption:
package-consumption.read_package_registry.Save the token securely.
If you want to use CI/CD job tokens for publishing, configure the job token allowlist:
For each package type project, configure:
Teams should publish packages to the appropriate type-specific project registry. See the following examples for each supported package format.
{{< tabs >}}
{{< tab title="Maven" >}}
Configure your project's pom.xml to publish to the java-packages project:
<distributionManagement>
<repository>
<id>gitlab-maven</id>
<url>${CI_API_V4_URL}/projects/JAVA_PACKAGES_PROJECT_ID/packages/maven</url>
</repository>
<snapshotRepository>
<id>gitlab-maven</id>
<url>${CI_API_V4_URL}/projects/JAVA_PACKAGES_PROJECT_ID/packages/maven</url>
</snapshotRepository>
</distributionManagement>
Configure authentication in your settings.xml:
<servers>
<server>
<id>gitlab-maven</id>
<configuration>
<httpHeaders>
<property>
<name>Job-Token</name>
<value>${CI_JOB_TOKEN}</value>
</property>
</httpHeaders>
</configuration>
</server>
</servers>
Publish with:
mvn deploy
{{< /tab >}}
{{< tab title="npm" >}}
Configure your project's package.json:
{
"name": "@company/my-package",
"publishConfig": {
"registry": "${CI_API_V4_URL}/projects/NODE_PACKAGES_PROJECT_ID/packages/npm/"
}
}
For CI/CD publishing, the job token is used automatically:
publish:
script:
- npm publish
For local publishing, configure authentication:
npm config set @company:registry https://gitlab.example.com/api/v4/projects/NODE_PACKAGES_PROJECT_ID/packages/npm/
npm config set //gitlab.example.com/api/v4/projects/NODE_PACKAGES_PROJECT_ID/packages/npm/:_authToken ${PERSONAL_ACCESS_TOKEN}
{{< /tab >}}
{{< tab title="PyPI" >}}
Configure publishing in your CI/CD pipeline:
publish:
script:
- pip install build twine
- python -m build
- TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token twine upload --repository-url ${CI_API_V4_URL}/projects/PYTHON_PACKAGES_PROJECT_ID/packages/pypi dist/*
For local publishing:
twine upload --repository-url https://gitlab.example.com/api/v4/projects/PYTHON_PACKAGES_PROJECT_ID/packages/pypi --username __token__ --password ${PERSONAL_ACCESS_TOKEN} dist/*
{{< /tab >}}
{{< tab title="Container registry" >}}
Build and push Docker images:
build-image:
script:
- docker build -t $CI_REGISTRY/artifact-management/docker-images/my-app:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY/artifact-management/docker-images/my-app:$CI_COMMIT_SHA
For local development:
docker login gitlab.example.com -u ${USERNAME} -p ${PERSONAL_ACCESS_TOKEN}
docker push gitlab.example.com/artifact-management/docker-images/my-app:latest
{{< /tab >}}
{{< tab title="Terraform" >}}
Publish Terraform modules:
publish-module:
script:
- tar -czf module.tar.gz *.tf
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file module.tar.gz "${CI_API_V4_URL}/projects/TERRAFORM_PACKAGES_PROJECT_ID/packages/terraform/modules/my-module/my-provider/1.0.0/file"'
{{< /tab >}}
{{< tab title="NuGet" >}}
Configure publishing in your project file or CI/CD pipeline:
publish:
script:
- dotnet pack
- dotnet nuget push "bin/Release/*.nupkg" --source ${CI_API_V4_URL}/projects/NUGET_PACKAGES_PROJECT_ID/packages/nuget/index.json --api-key ${CI_JOB_TOKEN}
For local publishing:
dotnet nuget push package.nupkg --source https://gitlab.example.com/api/v4/projects/NUGET_PACKAGES_PROJECT_ID/packages/nuget/index.json --api-key ${PERSONAL_ACCESS_TOKEN}
{{< /tab >}}
{{< tab title="Generic" >}}
Upload generic packages:
upload-package:
script:
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file my-package.zip "${CI_API_V4_URL}/projects/GENERIC_PACKAGES_PROJECT_ID/packages/generic/my-package/1.0.0/my-package.zip"'
{{< /tab >}}
{{< /tabs >}}
For package consumption, you can either:
The Maven virtual registry can enhance your artifact management setup by aggregating packages from multiple sources. You can:
https://gitlab.example.com/api/v4/groups/artifact-management/-/packages/maven).This approach provides a single endpoint that combines internal and external dependencies with intelligent caching and upstream prioritization.
Use the Maven virtual registry when you:
Publishing is not supported by the Maven virtual registry.
For more information, see Maven virtual registry.
artifact-management group, go to Deploy > Virtual registry.java-packages project as an upstream. <mirrors>
<mirror>
<id>central-proxy</id>
<name>GitLab virtual registry</name>
<url>https://gitlab.example.com/api/v4/virtual_registries/packages/maven/<registry_id></url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
The virtual registry supports multiple token types, including personal access tokens, group deploy tokens, group access tokens, and CI/CD job tokens. Each token type uses a different HTTP header name. For more information, see Authenticate to the virtual registry.
The following example implements a personal access token:
<servers>
<server>
<id>gitlab-maven</id>
<configuration>
<httpHeaders>
<property>
<name>Private-Token</name>
<value>${PERSONAL_ACCESS_TOKEN}</value>
</property>
</httpHeaders>
</configuration>
</server>
</servers>
Configure your projects to consume packages from the top-level group endpoint. This approach provides access to all package types through a single configuration:
{{< tabs >}}
{{< tab title="Maven" >}}
Configure your pom.xml to consume from the group registry:
<repositories>
<repository>
<id>gitlab-maven</id>
<url>https://gitlab.example.com/api/v4/groups/artifact-management/-/packages/maven</url>
</repository>
</repositories>
Configure authentication in your settings.xml:
<settings>
<servers>
<server>
<id>gitlab-maven</id>
<username>deploy-token-username</username>
<password>deploy-token-password</password>
</server>
</servers>
</settings>
{{< /tab >}}
{{< tab title="npm" >}}
Configure your .npmrc file:
@company:registry=https://gitlab.example.com/api/v4/groups/artifact-management/-/packages/npm/
//gitlab.example.com/api/v4/groups/artifact-management/-/packages/npm/:_authToken=${DEPLOY_TOKEN}
{{< /tab >}}
{{< tab title="PyPI" >}}
Configure pip to use the group registry:
# pip.conf or ~/.pip/pip.conf
[global]
extra-index-url = https://deploy-token-username:[email protected]/api/v4/groups/artifact-management/-/packages/pypi/simple/
Or use environment variables:
pip install --index-url https://deploy-token-username:[email protected]/api/v4/groups/artifact-management/-/packages/pypi/simple/ --no-index my-package
{{< /tab >}}
{{< tab title="Container Registry" >}}
Pull images from the group registry:
docker login gitlab.example.com -u deploy-token-username -p deploy-token-password
docker pull gitlab.example.com/artifact-management/docker-images/my-app:latest
{{< /tab >}}
{{< tab title="Terraform" >}}
Configure Terraform to use GitLab credentials with environment variables:
export TF_TOKEN_gitlab_example_com="deploy-token-password"
Then reference modules in your Terraform configuration:
module "example" {
source = "gitlab.example.com/artifact-management/terraform-modules//my-module"
version = "1.0.0"
}
Or using the project-specific URL:
module "example" {
source = "https://gitlab.example.com/api/v4/projects/TERRAFORM_PACKAGES_PROJECT_ID/packages/terraform/modules/my-module/my-provider/1.0.0"
}
{{< /tab >}}
{{< tab title="NuGet" >}}
Configure NuGet to use the group registry:
<!-- nuget.config -->
<configuration>
<packageSources>
<add key="GitLab" value="https://gitlab.example.com/api/v4/groups/artifact-management/-/packages/nuget/index.json" />
</packageSources>
<packageSourceCredentials>
<GitLab>
<add key="Username" value="deploy-token-username" />
<add key="ClearTextPassword" value="deploy-token-password" />
</GitLab>
</packageSourceCredentials>
</configuration>
{{< /tab >}}
{{< tab title="Generic" >}}
Download generic packages:
curl --header "DEPLOY-TOKEN: ${DEPLOY_TOKEN}" "https://gitlab.example.com/api/v4/groups/artifact-management/-/packages/generic/my-package/1.0.0/my-package.zip" --output my-package.zip
{{< /tab >}}
{{< /tabs >}}
The following example shows you how a project might consume packages from multiple package types:
stages:
- build
- test
variables:
MAVEN_OPTS: "-Dmaven.repo.local=${CI_PROJECT_DIR}/.m2/repository"
before_script:
# Configure npm registry
- echo "@company:registry=${CI_API_V4_URL}/groups/artifact-management/-/packages/npm/" >> .npmrc
- echo "//${CI_SERVER_HOST}/api/v4/groups/artifact-management/-/packages/npm/:_authToken=${CI_JOB_TOKEN}" >> .npmrc
build:
stage: build
script:
# Install npm dependencies from group registry
- npm install
# Build with Maven dependencies from group registry
- mvn compile
cache:
paths:
- .m2/repository/
- node_modules/
Some organizations prefer publishing packages alongside their application source code, as described in the enterprise scale tutorial. This approach works well when:
The artifact management approach works better when:
Start with the package types your organization uses most, then expand the structure as you adopt additional package formats. This approach scales naturally while maintaining security and ease of use.