Skip to content

Using uv in GitHub Actions#

Installation#

For use with GitHub Actions, we recommend the official astral-sh/setup-uv action, which installs uv, adds it to PATH, (optionally) persists the cache, and more, with support for all uv-supported platforms.

To install the latest version of uv:

example.yml
name: Example

jobs:
  uv-example:
    name: python
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v2

It is considered best practice to pin to a specific uv version, e.g., with:

example.yml
name: Example

jobs:
  uv-example:
    name: python
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v2
        with:
          # Install a specific version of uv.
          version: "0.4.10"

Setting up Python#

Python can be installed with the python install command:

example.yml
name: Example

jobs:
  uv-example:
    name: python
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v2

      - name: Set up Python
        run: uv python install

This will respect the Python version pinned in the project.

Or, when using a matrix, as in:

example.yml
strategy:
  matrix:
    python-version:
      - "3.10"
      - "3.11"
      - "3.12"

Provide the version to the python install invocation:

example.yml
name: Example

jobs:
  uv-example:
    name: python
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v2

      - name: Set up Python ${{ matrix.python-version }}
        run: uv python install ${{ matrix.python-version }}

Alternatively, the official GitHub setup-python action can be used. This can be faster, because GitHub caches the Python versions alongside the runner.

Set the python-version-file option to use the pinned version for the project:

example.yml
name: Example

jobs:
  uv-example:
    name: python
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v2

      - name: "Set up Python"
        uses: actions/setup-python@v5
        with:
          python-version-file: ".python-version"

Or, specify the pyproject.toml file to ignore the pin and use the latest version compatible with the project's requires-python constraint:

example.yml
name: Example

jobs:
  uv-example:
    name: python
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v2

      - name: "Set up Python"
        uses: actions/setup-python@v5
        with:
          python-version-file: "pyproject.toml"

Syncing and running#

Once uv and Python are installed, the project can be installed with uv sync and commands can be run in the environment with uv run:

example.yml
name: Example

jobs:
  uv-example:
    name: python
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v2

      - name: Set up Python
        run: uv python install

      - name: Install the project
        run: uv sync --all-extras --dev

      - name: Run tests
        # For example, using `pytest`
        run: uv run pytest tests

Tip

The UV_PROJECT_ENVIRONMENT setting can be used to install to the system Python environment instead of creating a virtual environment.

Caching#

It may improve CI times to store uv's cache across workflow runs.

The astral-sh/setup-uv has built-in support for persisting the cache:

example.yml
- name: Enable caching
  uses: astral-sh/setup-uv@v2
  with:
    enable-cache: true

You can configure the action to use a custom cache directory on the runner:

example.yml
- name: Define a custom uv cache path
  uses: astral-sh/setup-uv@v2
  with:
    enable-cache: true
    cache-local-path: "/path/to/cache"

Or invalidate it when the lockfile changes:

example.yml
- name: Define a cache dependency glob
  uses: astral-sh/setup-uv@v2
  with:
    enable-cache: true
    cache-dependency-glob: "uv.lock"

Or when any requirements file changes:

example.yml
- name: Define a cache dependency glob
  uses: astral-sh/setup-uv@v2
  with:
    enable-cache: true
    cache-dependency-glob: "requirements**.txt"

Note that astral-sh/setup-uv will automatically use a separate cache key for each host architecture and platform.

Alternatively, you can manage the cache manually with the actions/cache action:

example.yml
jobs:
  install_job:
    env:
      # Configure a constant location for the uv cache
      UV_CACHE_DIR: /tmp/.uv-cache

    steps:
      # ... setup up Python and uv ...

      - name: Restore uv cache
        uses: actions/cache@v4
        with:
          path: /tmp/.uv-cache
          key: uv-${{ runner.os }}-${{ hashFiles('uv.lock') }}
          restore-keys: |
            uv-${{ runner.os }}-${{ hashFiles('uv.lock') }}
            uv-${{ runner.os }}

      # ... install packages, run tests, etc ...

      - name: Minimize uv cache
        run: uv cache prune --ci

The uv cache prune --ci command is used to reduce the size of the cache and is optimized for CI. Its effect on performance is dependent on the packages being installed.

Tip

If using uv pip, use requirements.txt instead of uv.lock in the cache key.

Using uv pip#

If using the uv pip interface instead of the uv project interface, uv requires a virtual environment by default. To allow installing packages into the system environment, use the --system flag on all uv invocations or set the UV_SYSTEM_PYTHON variable.

The UV_SYSTEM_PYTHON variable can be defined in at different scopes.

Opt-in for the entire workflow by defining it at the top level:

example.yml
env:
  UV_SYSTEM_PYTHON: 1

jobs: ...

Or, opt-in for a specific job in the workflow:

example.yml
jobs:
  install_job:
    env:
      UV_SYSTEM_PYTHON: 1
    ...

Or, opt-in for a specific step in a job:

example.yml
steps:
  - name: Install requirements
    run: uv pip install -r requirements.txt
    env:
      UV_SYSTEM_PYTHON: 1

To opt-out again, the --no-system flag can be used in any uv invocation.