Integrate uv into your GitLab CI/CD pipelines for efficient Python dependency management.
Using uv Docker Images
Select a base image
Use one of the official uv Docker images. You can choose from distroless, Alpine, or Debian-based images:variables:
UV_VERSION: "0.10.8"
PYTHON_VERSION: "3.12"
BASE_LAYER: trixie-slim
# GitLab CI creates separate mountpoints, use copy mode
UV_LINK_MODE: copy
test:
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
script:
- uv sync --locked
- uv run pytest
For distroless images
When using distroless images, specify the entrypoint:test:
image:
name: ghcr.io/astral-sh/uv:0.10.8
entrypoint: [""]
script:
- uv --version
Pipeline Configuration
Here’s a complete example pipeline:
variables:
UV_VERSION: "0.10.8"
PYTHON_VERSION: "3.12"
BASE_LAYER: trixie-slim
UV_LINK_MODE: copy
stages:
- lint
- test
- build
lint:
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
script:
- uv sync --locked
- uv run ruff check .
test:
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
script:
- uv sync --locked --all-extras
- uv run pytest tests --cov
coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
build:
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
script:
- uv build
artifacts:
paths:
- dist/
Caching Dependencies
Persist the uv cache between pipeline runs to improve performance:
variables:
UV_CACHE_DIR: .uv-cache
UV_LINK_MODE: copy
test:
image: ghcr.io/astral-sh/uv:0.10.8-python3.12-trixie-slim
cache:
- key:
files:
- uv.lock
paths:
- $UV_CACHE_DIR
script:
- uv sync --locked
- uv run pytest
after_script:
- uv cache prune --ci
Advanced Caching
Cache based on both lockfile and Python version:
variables:
UV_CACHE_DIR: .uv-cache
test:
cache:
- key:
files:
- uv.lock
prefix: $PYTHON_VERSION
paths:
- $UV_CACHE_DIR
parallel:
matrix:
- PYTHON_VERSION: ["3.10", "3.11", "3.12"]
image: ghcr.io/astral-sh/uv:0.10.8-python$PYTHON_VERSION-trixie-slim
script:
- uv sync --locked
- uv run pytest
after_script:
- uv cache prune --ci
Matrix Testing
Test across multiple Python versions:
test:
parallel:
matrix:
- PYTHON_VERSION: ["3.10", "3.11", "3.12"]
BASE: ["trixie-slim", "alpine"]
image: ghcr.io/astral-sh/uv:0.10.8-python$PYTHON_VERSION-$BASE
script:
- uv sync --locked
- uv run pytest tests
Using uv pip
For workflows using the uv pip interface, enable system Python:
variables:
UV_SYSTEM_PYTHON: 1
test:
image: ghcr.io/astral-sh/uv:0.10.8-python3.12-trixie-slim
script:
- uv pip install -r requirements.txt
- python -m pytest
When using uv pip, consider using requirements.txt or pyproject.toml instead of uv.lock in your cache key.
Installing uv in Custom Images
If you need to use a custom base image:
test:
image: python:3.12-slim
before_script:
# Copy uv from official image
- apt-get update && apt-get install -y curl
- curl -LsSf https://astral.sh/uv/0.10.8/install.sh | sh
- export PATH="/root/.local/bin:$PATH"
script:
- uv sync --locked
- uv run pytest
Complete Example with Multiple Stages
variables:
UV_VERSION: "0.10.8"
PYTHON_VERSION: "3.12"
UV_CACHE_DIR: .uv-cache
UV_LINK_MODE: copy
stages:
- check
- test
- build
- deploy
.base:
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-trixie-slim
cache:
- key:
files:
- uv.lock
paths:
- $UV_CACHE_DIR
before_script:
- uv sync --locked
format:
extends: .base
stage: check
script:
- uv run ruff format --check .
lint:
extends: .base
stage: check
script:
- uv run ruff check .
type-check:
extends: .base
stage: check
script:
- uv run mypy .
test:
extends: .base
stage: test
parallel:
matrix:
- PYTHON_VERSION: ["3.10", "3.11", "3.12"]
script:
- uv run pytest tests --cov --cov-report=xml
coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
after_script:
- uv cache prune --ci
build:
extends: .base
stage: build
script:
- uv build
artifacts:
paths:
- dist/
expire_in: 1 week
only:
- tags
publish:
extends: .base
stage: deploy
script:
- uv publish --token $PYPI_TOKEN
only:
- tags
when: manual
Best Practices
- Pin versions: Always specify exact versions for
UV_VERSION and PYTHON_VERSION
- Use cache: Enable caching to speed up pipeline runs
- Set UV_LINK_MODE: Use
copy mode since GitLab creates separate mountpoints
- Prune cache: Run
uv cache prune --ci in after_script to optimize cache size
- Cache key: Use
uv.lock as the cache key for projects, requirements.txt for pip workflows