GitLab Package Registry
The package registry is in test phase.The test instance is running in a sandbox aws account and is available under https://gitlab-test.sandbox.platon.sikt.no url.
All users interested in testing the package registry please contact Artur Ferfecki on Slack.
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.
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.
Supported Package Types
- NPM
- PyPI (Python)
- Maven
- NuGet
- Generic packages
Roles and Permissions
Roles define which actions a user is able to perform. 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