Skip to content
compiler.dev

Cache Poetry Dependencies in GitHub Actions

Last updated

To cache Poetry in GitHub Actions, install Poetry before actions/setup-python, then set cache: poetry. setup-python then caches Poetry's virtualenv, keyed on poetry.lock. The step order is the part people get wrong, and it is the first thing to check if the cache step errors.

Option 1: setup-python with cache: poetry

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - name: Install Poetry
        run: pipx install poetry
      - uses: actions/setup-python@v7
        with:
          python-version: "3.13"
          cache: poetry
      - run: poetry install --no-interaction
      - run: poetry run pytest

Per the setup-python README (checked October 2026), cache: poetry caches the virtualenv directories, one for each Poetry project it finds, and the default key hashes poetry.lock. The action runs Poetry to find those directories, so Poetry has to be installed first. pipx is preinstalled on GitHub-hosted Ubuntu images. On other runners, install pipx first or use pip install poetry.

Use cache-dependency-path when the lockfile is in a subdirectory:

- uses: actions/setup-python@v7
  with:
    python-version: "3.13"
    cache: poetry
    cache-dependency-path: services/api/poetry.lock

Option 2: explicit actions/cache with an in-project venv

Poetry puts virtualenvs in a shared directory by default (~/.cache/pypoetry/virtualenvs on Linux) with a hashed name. Making the venv live in the project, as .venv, gives you a fixed path to cache.

- uses: actions/checkout@v6
- run: pipx install poetry
- uses: actions/setup-python@v7
  id: py
  with:
    python-version: "3.13"
- run: poetry config virtualenvs.in-project true
- uses: actions/cache@v6
  with:
    path: .venv
    key: venv-${{ runner.os }}-py${{ steps.py.outputs.python-version }}-${{ hashFiles('poetry.lock') }}
    restore-keys: |
      venv-${{ runner.os }}-py${{ steps.py.outputs.python-version }}-
- run: poetry install --no-interaction

Three details matter here:

  • The key has the exact Python version. steps.py.outputs.python-version gives the full version, such as 3.13.1. A virtualenv contains links to the interpreter it was created with. If the runner image updates Python, a venv restored under an old key will be broken. Putting the exact version in the key forces a rebuild.
  • poetry install still runs. On a cache hit it finds everything installed and finishes in seconds. On a partial restore via restore-keys, it adds or updates only what changed.
  • Set virtualenvs.in-project before the cache step. Otherwise Poetry creates the venv somewhere else and .venv is empty.

Option 3: cache the download cache only

If you prefer not to cache a venv, cache Poetry's download cache and let poetry install rebuild the venv each time:

- uses: actions/cache@v6
  with:
    path: ~/.cache/pypoetry
    key: poetry-${{ runner.os }}-${{ hashFiles('poetry.lock') }}
    restore-keys: |
      poetry-${{ runner.os }}-

This is safer but slower, because wheels are still unpacked on every run. Use poetry config cache-dir to confirm the real path on your runner.

What to cache and what not to

ItemCache?Notes
In-project .venvYes, with Python version in keyFastest restore
~/.cache/pypoetryYesSafe, saves downloads only
The Poetry install itselfOptionalpipx install poetry takes seconds
dist/ build outputNoRebuilt per release

Pin the Poetry version (pipx install poetry==2.x) if you cache a venv. A Poetry upgrade can change how the environment is laid out, and an unpinned install makes your key incomplete.

How to verify a cache hit

  1. Look for Cache restored from key: ... in the setup-python or cache step. On a miss you see Cache not found for input keys: ....
  2. In poetry install output, a warm run prints No dependencies to install or update. A cold one lists each package with Installing ....
  3. With explicit actions/cache, branch on the output while debugging: if: steps.cache.outputs.cache-hit != 'true' runs only on a miss.
  4. gh cache list --key venv- --limit 20 shows whether entries are being saved.

Common mistakes

  1. Installing Poetry after setup-python. With cache: poetry the action fails to find Poetry.
  2. Not committing poetry.lock. The key is then built from nothing useful.
  3. Missing Python version in the venv key. Leads to confusing import errors after an image update.
  4. Using poetry install without --no-interaction. Rarely a problem in CI, but cheap insurance.
  5. Treating the cache as the source of truth. If something odd happens, delete the entry (gh cache delete <id>) and rebuild.

For how scope and eviction affect all of these, see Caching in GitHub Actions and cache limits.

FAQ

Is it better to cache .venv or the Poetry cache directory?

.venv is faster; the cache directory is safer. Use .venv with a key that has the exact Python version and poetry.lock hash.

Does cache: poetry work without pipx?

It needs poetry on the PATH. Any install method works as long as it runs before setup-python.

Should I use uv instead of Poetry in CI?

That is a project decision. If you only want faster installs, see the uv notes in caching pip.

How do I cache Poetry in a monorepo?

Give each project its own cache-dependency-path and a distinct key prefix, so changes to one lockfile do not invalidate the others.

Next steps

For pytest runtime, see pytest slow tests. To see what else to fix, read how to speed up GitHub Actions. compiler.dev gives you 200 free trial minutes to compare your job on a faster machine; see pricing.

Run this pytest suite on compiler.dev 200 free minutes

Start free