A private PyPI mirror is a server that speaks the same simple index protocol as PyPI, so pip can install from it by pointing at one URL. Run one to control what versions teams can install, keep builds working during outages, and set pip to use that single index only.
How does pip find packages?
The pip documentation says pip searches for packages over the HTTP simple interface (PEP 503). It looks on PyPI, or the index given by --index-url, in the local filesystem, and in any additional repositories named by --find-links or --extra-index-url.
The next sentence matters most: there is no priority in the locations searched. Pip checks them all and picks the best match by version number.
Why is a second index risky?
Take a constructed example. Your company has an internal package called acme-billing-utils on a private index, and developers install with the private index added as an extra index next to PyPI. If someone publishes a package with that same name and a higher version number on public PyPI, pip is free to choose it. That is the dependency confusion pattern.
A mirror fixes this by becoming the only index. Teams use --index-url to point at it and nothing else. The mirror serves your internal packages and a proxied copy of public ones, so name collisions are settled in one place, by you.
What does a mirror have to do?
Any server that implements the simple repository API from PEP 503 can act as an index. Beyond that, decide:
- Mirror everything or allow only approved packages. A full mirror is simple. An allow list is stricter.
- Proxy on demand or sync ahead. On demand fetches a package the first time somebody asks. Ahead of time sync gives you review time.
- Retention. Keep files after upstream removal so that an old build still works.
- Authentication, so only your build systems and developers reach it.
Whichever product you pick, test it against the pip version your builds use.
How do you point pip at it?
The pip configuration documentation shows that setting names follow the long option, with index-url valid in a [global] section of a config file. Put it in the global or per user config on build images, or pass the flag in CI.
Two more flags close gaps:
--no-indexforbids PyPI entirely when you also give--find-links, which suits offline builds.- Avoid
--extra-index-urlin team config.
Do hashes still matter with a mirror?
Yes. The pip secure installs documentation says to enable hash checking mode with --require-hashes and to disallow source distributions with --only-binary :all:. Hash checking uses hashes embedded in the requirements file, so a file swapped on the mirror, or in transit, fails to install. A mirror you run is a high value target, so hash checks protect you from your own mirror being tampered with.
Lock files give you the hashes. Generate them once from a trusted resolve, review the diff when they change, and let CI install only from them.
How to check or apply this
- Choose a mirror product that implements the PEP 503 simple API and test it with your pip version.
- Decide full mirror or allow list, and write down who approves additions.
- Set
index-urlto the mirror in global pip config for build images and developer laptops. - Remove every
extra-index-urlfrom repository configs. - Generate requirements with hashes and install with
--require-hashes. - Add
--only-binary :all:where you can, and handle the exceptions explicitly. - Scan the lock file for known vulnerabilities on every change.
Quick check
Does pip prefer the first index I list?
No. The pip documentation says there is no priority among the locations it searches, and it selects the best version match.
Is a mirror enough without hash checking?
No. The mirror controls what is offered, while hashes verify that each file is the one you reviewed.
Sources
- pip documentation, Secure installs
- pip documentation, pip install
- pip documentation, Configuration
- PEP 503, Simple Repository API
Last reviewed: 18 October 2026.