# pip-tools: pip-compile and pip-sync for pinned Python dependencies

> pip-tools splits dependency declaration from dependency pinning: pip-compile resolves your inputs into a locked requirements.txt, and pip-sync makes an environment match that file exactly. It is a two-command workflow for teams that already live in pip, and it inherits pip's resolver behaviour along with its limits.

**jazzband/pip-tools** — A set of tools to keep your pinned Python dependencies fresh.

- Repository: https://github.com/jazzband/pip-tools
- Website: https://pip-tools.rtfd.io
- Stars: 8,005 · Forks: 681
- Language: Python
- License: BSD-3-Clause
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/jazzband-pip-tools

## The problem pip-tools solves: declaration versus pinning

A requirements file usually has to do two jobs at once. It states what your project wants, and it records exactly what got installed. Those are different documents. The README frames the split directly: pip-tools is "a set of command line tools to help you keep your pip-based packages fresh, even when you've pinned them." The audience is anyone building a Python application or library who wants deterministic installs in production but still wants a readable list of top-level dependencies to edit by hand.

pip-compile produces the pinned side. You write loose or unpinned requirements, and it emits a requirements.txt where every package, including transitive ones, is pinned to a version, with a comment showing which dependency pulled it in. pip-sync handles the other direction: it makes an installed environment match a compiled file. The value is that the file you review in a pull request is the same file your deployment installs, and the environment you test in is the same set of distributions the file names.

## How pip-compile resolves inputs into a locked requirements.txt

pip-compile accepts several input formats. According to the README, it can read pyproject.toml, setup.cfg, setup.py or a plain requirements.in. For pyproject.toml it reads project.dependencies and project.optional-dependencies, which means it works with standards-based backends such as Setuptools, Hatch and flit rather than tying you to one packaging tool.

The output is annotated. Running pip-compile against a requirements.in containing a single line, django, produces pins for django plus its transitive dependencies, each followed by a comment naming the source. The README's example shows asgiref==3.6.0 marked "via django" and django==4.1.7 marked "via -r requirements.in". That provenance is the part worth reading in code review: it tells you why a package is present, not just that it is.

Resolution happens in the environment where you run the command. The README is explicit that pip-compile "should be run from the same virtual environment as your project so conditional dependencies that require a specific Python version, or other environment markers, resolve relative to your project's environment." That is a design constraint, not a footnote. A lock file compiled on Python 3.10 reflects markers evaluated for Python 3.10.

## Installing pip-tools and compiling your first requirements.txt

pip-tools installs like any other pip package, and the README says it must be installed in each of your project's virtual environments. Activate the environment first.

```bash
source /path/to/venv/bin/activate
python -m pip install pip-tools
```

After that, the pip-compile and pip-sync commands are available. The README also notes alternatives: python -m piptools compile, and pipx run --spec pip-tools pip-compile when pipx was installed with a suitable Python version.

Create a minimal input file. The README uses exactly this content for its Django example.

```text
# requirements.in
django
```

Then compile it. The command writes requirements.txt in the working directory.

```console
$ pip-compile requirements.in
```

You should see a header naming the pip-compile invocation and the Python version, followed by pinned entries with "via" comments. To compile from a pyproject.toml instead, point the command at the file and set the output name explicitly, as the README does for a Hatch-based Django app.

```console
$ pip-compile -o requirements.txt pyproject.toml
```

Optional dependencies are selected with --extra. The README compiles a dev extra into a second file this way.

```console
$ pip-compile --extra dev -o dev-requirements.txt pyproject.toml
```

The resulting dev-requirements.txt contains django plus pytest and pytest's own dependencies, each annotated with the package that requires it.

## Updating pins without losing the lock

The behaviour that surprises newcomers is documented plainly: if pip-compile finds an existing requirements.txt that fulfils the dependencies, it makes no changes, even when newer versions exist. The README offers two ways out. Delete the file and recompile, or use the upgrade flags.

```console
# only update the django package
$ pip-compile --upgrade-package django
```

The --upgrade flag refreshes everything in the file. The -P short form does the same job as --upgrade-package for a single package, and you can repeat it. The README shows combining them, and pinning a target version inline.

```console
$ pip-compile --upgrade-package django --upgrade-package requests==2.0.0
```

This is the practical rhythm of the tool: compile once, commit the result, and then upgrade deliberately, one package or one sweep at a time, so the diff stays reviewable. A silent no-op compile is not a bug, but it does mean a CI job that only runs pip-compile will happily report success while your pins age.

## pip-sync and the limits of an environment-derived lock

pip-sync is the enforcement half. It makes the active environment match a compiled file, which is useful in CI and on developer machines where stale packages accumulate. The trade-off is that it is destructive by design: the environment ends up matching the file, so anything installed outside it is not part of the contract. Keep pip-tools itself, and any tooling you install ad hoc, out of the set you sync against.

The larger limitation is portability. Because resolution depends on the environment it runs in, a requirements.txt compiled on one Python version carries markers resolved for that version. The README does not document a cross-platform lock mode, and it does not document rollback. If your deployment targets Linux in production but developers work on macOS or Windows, or if you support several Python versions from one lock file, pip-compile alone will not give you a single artefact that covers all of them. You compile per environment, or you accept that the file encodes one.

A second constraint is scope. pip-tools operates on pip requirements. Projects that need a full package-and-environment manager, or that want the lock file to describe the interpreter and system libraries too, are outside what these two commands do.

## pip-tools compared with uv and Poetry

The alternatives people ask about most are uv and Poetry, and the difference is architectural rather than cosmetic.

Poetry is a project manager. It owns pyproject.toml, resolves dependencies, manages virtual environments, and writes its own lock file format. pip-tools does not manage environments or packaging. It reads the dependency declarations you already have, including ones written for Setuptools or Hatch, and writes a plain requirements.txt that pip itself can install. If your build already goes through pyproject.toml and setuptools, pip-compile slots in without changing how the package is built.

uv is a resolver and installer written in Rust, and it has its own lock file and its own compile command. The practical divergence is the one described above: pip-tools resolves in the current environment and emits pip-format files, while uv's lock is designed to be resolved once and used across platforms and Python versions. If cross-platform reproducibility from a single lock file is your requirement, pip-tools is the wrong tool and uv is the one to evaluate. If you want to stay on pip, review a human-readable requirements.txt in pull requests, and use the same file in CI, pip-tools does that with two commands and no new project format.

## Maintenance, licensing and upgrade cost

The repository is not archived, and the last push was on 2026-09-19. Releases are frequent: v7.6.1 on 2026-08-11, v7.6.0 on 2026-07-18, and v7.5.3 on 2026-02-11. The project is developed under the Jazzband organisation, which the README badges reference, and it ships a CHANGELOG.md plus a changelog.d directory with towncrier.toml, so release notes are generated from fragment files rather than hand-written at release time.

pip-tools is BSD-3-Clause licensed, and the pyproject.toml metadata declares the BSD licence classifier. That is a permissive licence, but it governs pip-tools itself, not the packages you resolve with it. Nothing about the tool changes the licences of what ends up in your requirements.txt, and the README does not offer licence scanning. If transitive licence compliance matters, that check belongs somewhere else in your pipeline.

Upgrade cost is shaped by the Python floor. The pyproject.toml sets requires-python to ">= 3.9" and the classifiers list 3.9 through 3.14 plus PyPy, so the supported range is wide. The runtime dependencies are small and mostly first-party: build, click, pip, pyproject_hooks, plus tomli and typing_extensions on Python below 3.11, and setuptools and wheel because pip-tools invokes setup.py in some paths. Upgrading pip-tools therefore mostly means tracking pip's own resolver behaviour, since pip is a direct dependency with a floor of 22.2.

## Conclusion

Adopt pip-tools if your project already declares dependencies in pyproject.toml, setup.py, setup.cfg or a requirements.in and you want a committed lock file plus an environment that matches it exactly. Skip it if you need cross-platform lock files covering several Python versions at once, or if pip-compile's environment-dependent resolution markers do not fit your deployment matrix. Before rolling it out, run pip-compile on a copy of your existing requirements.txt and diff the output, because pip-compile leaves a file untouched when the existing pins already fulfil your inputs, and that silence can hide available upgrades.

## FAQ

### What are pip-tools?

It is a set of command line tools, pip-compile and pip-sync, that keep pip-based dependencies pinned and fresh. pip-compile writes a pinned requirements.txt from your declared dependencies, and pip-sync makes an environment match that file.

### How do I install pip-tools?

Install it into each project virtual environment with python -m pip install pip-tools after activating that environment. The README notes you can also run it via python -m piptools compile or pipx run --spec pip-tools pip-compile.

### How to use pip-tools?

Declare your dependencies in pyproject.toml, setup.py, setup.cfg or a requirements.in file, then run pip-compile to generate a pinned requirements.txt. Use pip-sync to make the active environment match that compiled file.

### What does pip-tools do?

pip-compile resolves your declared dependencies, including transitive ones, into a requirements.txt where every package is pinned and annotated with the dependency that requires it. pip-sync then brings an environment in line with a compiled file.

### What is the difference between pip-tools and pip?

pip installs packages, while pip-tools compiles a pinned file from your declared dependencies and can sync an environment to that file. pip-tools depends on pip and uses it for resolution, and the README says pip-compile should run from the same virtual environment as your project.

### What is a pip-tools alternative?

Poetry and uv are the alternatives the README's ecosystem implies, and they differ in kind: Poetry manages the project and its environments with its own lock format, and uv resolves and installs with a lock designed to be used across platforms. pip-tools instead reads your existing declarations and emits a plain requirements.txt that pip installs.

## Sources

- [jazzband/pip-tools on GitHub](https://github.com/jazzband/pip-tools)
- [License: BSD-3-Clause](https://github.com/jazzband/pip-tools/blob/main/LICENSE)
- [Project website](https://pip-tools.rtfd.io)
- [README](https://github.com/jazzband/pip-tools/blob/main/README.md)
- [Releases](https://github.com/jazzband/pip-tools/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jazzband-pip-tools
