Package Registry
Table of Contents
- Overview
- Authentication
- NPM Packages
- Python Packages (PyPI)
- Maven Packages
- NuGet Packages
- Generic Packages
Overview
The GitLab Package Registry is an integrated feature of GitLab that allows to use GitLab as a private or public repository for various common software package managers. It enables teams to publish, share, and consume software packages and their dependencies alongside their source code within the same platform. GitLab Package Registry will replace Artifactory, as Artifactory is being phased out. The GitLab Package Registry is not meant for storing containers.
Artifactory uses a single, centralized repository to store and update the packages. In GitLab, the packages are managed directly in GitLab group or project. This means that teams publish packages to projects that stores the code. When pulling packages, teams consume packages from group registries that aggregate all packages below them or directly from the project that stores the package. This architecture offers following advantages:
- Clear ownership of packages alongside their source code
- Granular access control without additional configuration
- Simplified CI/CD integration
- Natural alignment with team structures
- Single URL for accessing all company packages through root group consumption
Migration
Each project in GitLab can serve as package repository. The packages in Artifactory are stored in centralized storage and are built by different teams and different pipelines. Platon team has no knowledge about the ownership of the package and therefore is not able to migrate the packages into the correct GitLab projects. The teams have to update the URLs and credentials (see the Authentication chapter for details) in the GitLab project that builds the package in order to publish/pull the package to the new registry.
If an application needs older package, and it is not possible (read it would take a lot of effort) to build that older version, one could use the package importer to import the package from Artifactory to GitLab Package Registry. The documentation specifically mentions Artifactory as a package source for all generally available packages. Platon team has no experience with the package importer, so support from Platon side is limited.
Supported Package Types
Only package types that are generally available will be accepted by the package registry. For full list of supported packages (also those under development) see docs.
- NPM
- PyPI (Python)
- Maven
- NuGet
- Generic packages
Roles and Permissions
Since GitLab package Registry is closely integrated into GitLab, access control is inherited from your existing GitLab permissions. Roles define which actions a user is able to perform on the package. The role types are defined by default in GitLab and are assigned per group or project. Group Owners and/or project Maintainers are able to assign or change users role.
Project permissions for package registry:
| Action | Guest | Planner | Reporter | Developer | Maintainer | Owner |
|---|---|---|---|---|---|---|
| Pull package | x | x | x | x | x | x |
| Publish package | x | x | x | |||
| Delete Package | x | x | ||||
| Delete files associated with a package | x | x |
Guest roles can only pull packages on public and internal projects. External users must be given explicit access (at least the Reporter role) even if the project is internal.
Group permissions for package registry:
| Action | Guest | Planner | Reporter | Developer | Maintainer | Owner |
|---|---|---|---|---|---|---|
| Pull package | x | x | x | x | ||
| Publish package | x | x | x | |||
| Delete Package | x | x | ||||
| Manage package settings | x |
Authentication
For Publishing Packages
One must be authenticated when publishing package to the package registry. Packages have to be published to a specific project. It is not possible to push packages to a GitLab group, one can only pull packages from group.
For Pulling Packages
To install package from private project one needs to be authenticated. The owner of the project can allow for an anonymous pull from package registry in:
project Settings → General → Visibility, project features, permissions → Package registry → Allow anyone to pull from package registry
Packages can be pulled from group or project endpoints. The group endpoint collects published packages from the projects it contains and makes them available as a single collection.
1. CI/CD Job Token
This is the preferred method for publishing packages to the registry from GitLab pipeline.
The short-lived $CI_JOB_TOKEN is managed by the GitLab instance and has permission to publish packages to the same project by default.
If the team has a need to push a package to repository in different project, one can add the source project to the CI/CD job token allowlist of the receiver project:
receiver project Settings → CI/CD → CI/CD job token allowlist
The job token is automatically provisioned to pull packages from the same project.
2. Deploy Token
For CI/CD and automated systems:
- Go to Project Settings → Repository → Deploy Tokens
- Create token with
read_package_registryand/orwrite_package_registryscope - Save the token as CI/CD variable or locally
3. Access Tokens
Create a personal/group/project access token with api scope:
- Go to Settings → Access Tokens
- Create token with required
api - Save the token securely
Useful GitLab Predefined Environment Variables
GitLab comes with number of predefined environment variables. The list below is a selection of the complete list, handy when working with packages in GitLab pipeline.
| Variable | Description |
|---|---|
| CI_API_V4_URL | The GitLab API v4 root URL |
| CI_SERVER_HOST | The host of the GitLab instance URL, without protocol or port - gitlab.sikt.no |
| CI_PROJECT_ID | The ID of the current project. This ID is unique across all projects on the GitLab instance. |
| CI_PROJECT_ROOT_NAMESPACE | The root project namespace (username or group name) of the job. For example, if CI_PROJECT_NAMESPACE is root-group/child-group/grandchild-group, CI_PROJECT_ROOT_NAMESPACE is root-group. |
| CI_PROJECT_PATH | The project namespace with the project name included. |
| CI_JOB_TOKEN | A token to authenticate with package registry. The token is valid as long as the job is running. |
| CI_COMMIT_TAG | The commit tag name. Available only in pipelines for tags. |
NPM Packages
Official documentation for npm package type with examples can be found in GitLab documentation.
Setup
1. Using .npmrc (Recommended)
Project-level:
# .npmrc
@scope:registry=https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/npm/
//gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/npm/:_authToken=${TOKEN}
Group-level:
# .npmrc
@scope:registry=https://gitlab.sikt.no/api/v4/groups/${GROUP_ID}/-/packages/npm/
//gitlab.sikt.no/api/v4/groups/${GROUP_ID}/-/packages/npm/:_authToken=${TOKEN}
When authenticating in the GitLab pipeline one can substitute the GROUP_ID env var with predefined CI_PROJECT_NAMESPACE_ID. The CI_PROJECT_NAMESPACE_ID contains the ID of the first parent group of the project.
2. Using npm config
npm config set @scope:registry https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/npm/
npm config set //gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/npm/:_authToken ${TOKEN}
where TOKEN represents deploy token, group access token, project access token, or personal access token
Push NPM Package
- Update package.json:
{
"name": "@scope/my-package",
"version": "1.0.0",
"publishConfig": {
"@scope:registry": "https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/npm/"
}
}
- Publish:
npm publish
Pull NPM Package
# Install specific package
npm install @scope/my-package
# Or add to package.json and run
npm install
Example .gitlab-ci.yml
publish:
image: node:18
script:
- echo "@scope:registry=https://${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/packages/npm/" >> .npmrc
- echo "//${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}" >> .npmrc
- npm publish
Python Packages (PyPI)
Official documentation for PyPI package type with examples can be found in GitLab documentation.
Setup
Configure pip
# ~/.pip/pip.conf (Linux/macOS) or %APPDATA%\pip\pip.ini (Windows)
[global]
index-url = https://__token__:${TOKEN}@gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/pypi/simple
Or use environment variable:
export PIP_INDEX_URL=https://__token__:${TOKEN}@gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/pypi/simple
Push Python Package
You cannot publish a package if a package of the same name and version already exists. You must delete the existing package first. If you attempt to publish the same package more than once, a 400 Bad Request error occurs.
- Install Twine
pip install twine
- Configure .pypirc:
# ~/.pypirc
[distutils]
index-servers =
gitlab
[gitlab]
repository = https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/pypi
username = ${PERSONAL_ACCESS_TOKEN_USERNAME}
password = ${PERSONAL_ACCESS_TOKEN}
- Upload:
python3 -m twine upload --repository gitlab dist/*
Or using environment variables:
TWINE_USERNAME=${PERSONAL_ACCESS_TOKEN_USERNAME}\
TWINE_PASSWORD=${PERSONAL_ACCESS_TOKEN} \
python3 -m twine upload --repository-url https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/pypi dist/*
Pull Python Package
pip install --index-url https://${PERSONAL_ACCESS_TOKEN_USERNAME}:${PERSONAL_ACCESS_TOKEN}@gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/pypi/simple --no-deps my-package
Example .gitlab-ci.yml
publish:
image: python:3.14
script:
- pip install build twine
- python -m build
- TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python -m twine upload --repository-url https://gitlab.sikt.no/api/v4/projects/${CI_PROJECT_ID}/packages/pypi dist/*
Maven Packages
Official documentation for maven package type with examples can be found in GitLab documentation.
Setup
Configure settings.xml
<!-- ~/.m2/settings.xml -->
<settings>
<servers>
<server>
<id>gitlab-maven</id>
<configuration>
<httpHeaders>
<property>
<name>Job-Token</name>
<value>${CI_JOB_TOKEN}</value>
</property>
</httpHeaders>
</configuration>
</server>
</servers>
</settings>
Or with username/password:
<settings>
<servers>
<server>
<id>gitlab-maven</id>
<username>GITLAB_USERNAME</username>
<password>PERSONAL_ACCESS_TOKEN</password>
</server>
</servers>
</settings>
Push Maven Package
- Configure pom.xml:
<project>
<!-- ... -->
<distributionManagement>
<repository>
<id>gitlab-maven</id>
<url>https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/maven</url>
</repository>
<snapshotRepository>
<id>gitlab-maven</id>
<url>https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/maven</url>
</snapshotRepository>
</distributionManagement>
<repositories>
<repository>
<id>gitlab-maven</id>
<url>https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/maven</url>
</repository>
</repositories>
</project>
- Deploy:
mvn deploy -s settings.xml
Pull Maven Package
Add repository to pom.xml (shown above) and add dependency:
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-package</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
Then:
mvn install
Example .gitlab-ci.yml
deploy:
image: maven:3.8-openjdk-11
script:
- mvn deploy -s ci_settings.xml
NuGet Packages
Official documentation for nuget package type with examples can be found in GitLab documentation.
Setup
Add source
dotnet nuget add source "https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/nuget/index.json" \
--name gitlab \
--username USERNAME \
--password ${TOKEN} \
--store-password-in-clear-text
Push NuGet Package
- Build package:
dotnet pack --configuration Release
- Push:
dotnet nuget push "bin/Release/*.nupkg" \
--source gitlab \
--api-key ${TOKEN}
Or directly to URL:
dotnet nuget push "bin/Release/*.nupkg" \
--source "https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/nuget/index.json" \
--api-key ${TOKEN}
Pull NuGet Package
dotnet add package MyPackage --version 1.0.0 --source gitlab
Or restore from nuget.config:
<!-- nuget.config -->
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="gitlab" value="https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/nuget/index.json" />
</packageSources>
<packageSourceCredentials>
<gitlab>
<add key="Username" value="USERNAME" />
<add key="ClearTextPassword" value="${TOKEN}" />
</gitlab>
</packageSourceCredentials>
</configuration>
Example .gitlab-ci.yml
publish:
image: mcr.microsoft.com/dotnet/sdk:7.0
script:
- dotnet pack --configuration Release
- dotnet nuget push "bin/Release/*.nupkg" --source "https://gitlab.sikt.no/api/v4/projects/${CI_PROJECT_ID}/packages/nuget/index.json" --api-key ${CI_JOB_TOKEN}
Generic Packages
Generic packages are useful for binary files, archives, or custom package formats. Official documentation for generic packages with examples can be found in GitLab documentation.
Push Generic Package
curl --header "PRIVATE-TOKEN: ${TOKEN}" \
--upload-file path/to/file.tar.gz \
"https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/generic/${PACKAGE_NAME}/${VERSION}/file.tar.gz"
With additional metadata:
curl --header "PRIVATE-TOKEN: ${TOKEN}" \
--upload-file artifact.zip \
"https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/generic/my-package/1.0.0/artifact.zip?status=default"
Pull Generic Package
curl --header "PRIVATE-TOKEN: ${TOKEN}" \
"https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/generic/${PACKAGE_NAME}/${VERSION}/file.tar.gz" \
--output file.tar.gz
Or using wget:
wget --header="PRIVATE-TOKEN: ${TOKEN}" \
"https://gitlab.sikt.no/api/v4/projects/${PROJECT_ID}/packages/generic/${PACKAGE_NAME}/${VERSION}/file.tar.gz"
Example .gitlab-ci.yml
upload:
script:
- 'curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file build/artifact.zip "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/my-package/${CI_COMMIT_TAG}/artifact.zip"'
only:
- tags
download:
script:
- 'curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/my-package/1.0.0/artifact.zip" -o artifact.zip'
Best Practices
1. Version Management
- Use semantic versioning (semver)
- Tag releases consistently
- Don't overwrite existing versions
2. Security
- Use deploy tokens for CI/CD (not personal tokens)
- Rotate tokens regularly
- Use scoped tokens (minimal permissions)
3. Organization
- Use group-level registries for shared packages
- Use project-level registries for project-specific packages
- Establish naming conventions (scopes, prefixes)
4. Cleanup
- Set up cleanup policies for old packages
- Remove unused packages
- Document retention policies