pip install падает по таймауту: настройки и зеркала PyPI
Ошибка ReadTimeoutError при pip install выглядит как поломка pip, но почти всегда дело в сети: сервер не прислал данные за отведённое время. По умолчанию pip ждёт ответа 15 секунд, и на медленном или нестабильном канале этого мало.
Ниже по шагам: как прочитать ошибку, какие таймауты поднять, как перестать скачивать одно и то же и когда стоит сменить индекс пакетов. Все команды работают с актуальным pip, поэтому начните с его обновления: python -m pip install --upgrade pip.
Как выглядит проблема
Типичный вывод при плохом соединении:
WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='pypi.org', port=443): Read timed out. (read timeout=15)")': /simple/numpy/
...
pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out.Смотрите на имя хоста. pypi.org отдаёт индекс: списки версий и ссылки на файлы. files.pythonhosted.org отдаёт сами архивы и колёса. Если падает первый, тормозят запросы метаданных. Если второй, не успевает загрузка тяжёлых файлов, например torch или tensorflow.
Больше деталей даёт подробный режим: pip install -v -r requirements.txt. По нему видно, на каком пакете установка встала и что pip делал в этот момент: искал версию, скачивал файл или собирал пакет.
Рядом с таймаутом чтения встречаются похожие ошибки, и лечатся они по-разному:
ConnectTimeoutErrorилиMax retries exceeded: соединение не устанавливается вообще. Проверьте DNS и доступность хоста, увеличение таймаута чтения тут не поможет.Connection broken: IncompleteRead: соединение рвётся посреди файла. Это случай для--resume-retries, о нём ниже.SSLErrorсCERTIFICATE_VERIFY_FAILED: чаще всего корпоративный прокси подменяет сертификат. Укажите корневой сертификат компании через--certилиPIP_CERT, а не отключайте проверку через--trusted-host.
Таймаут и повторы
Два главных параметра. --timeout (у него есть старое имя --default-timeout) задаёт, сколько секунд ждать ответа сокета, по умолчанию 15. --retries ограничивает число попыток установить новое соединение, по умолчанию 5.
pip install --timeout 60 --retries 10 -r requirements.txtУчтите разницу. --retries помогает, когда соединение не устанавливается. Если же большой файл оборвался посреди загрузки, работает другой параметр, --resume-retries: он докачивает или перезапускает незавершённую загрузку. В актуальной документации pip у него значение по умолчанию 5. В старых версиях этой опции нет или она выключена, это ещё одна причина обновить pip.
pip install --timeout 60 --retries 10 --resume-retries 10 -r requirements.txtСохраните настройки один раз
Флаги в командной строке забываются, особенно в скриптах коллег. Запишите значения в конфигурацию pip:
pip config set global.timeout 60
pip config set global.retries 10
pip config listКоманда пишет в пользовательский файл: на Linux это обычно ~/.config/pip/pip.conf, на Windows %APPDATA%\pip\pip.ini. Какие файлы pip читает на вашей машине и в каком порядке, покажет pip config debug.
В контейнерах и CI удобнее переменные окружения. Любой параметр задаётся как PIP_ плюс длинное имя опции заглавными буквами, дефисы меняются на подчёркивания:
export PIP_DEFAULT_TIMEOUT=60
export PIP_RETRIES=10PIP_DEFAULT_TIMEOUT и PIP_TIMEOUT равнозначны, работают оба. Переменная окружения переопределяет значение из конфигурационного файла.
Если в вашей сети выход в интернет идёт только через HTTP-прокси, pip берёт его из переменной HTTPS_PROXY или из параметра --proxy. Без этого запросы уходят напрямую и висят до таймаута, что легко принять за медленный PyPI.
Кэш: не качайте одно и то же дважды
pip кэширует скачанные файлы и собранные колёса. Посмотреть, где лежит кэш и сколько он занимает:
pip cache dir
pip cache info
pip cache list numpyЧастая ошибка в Dockerfile: флаг --no-cache-dir ставят ради размера образа, и каждая сборка качает всё заново. Если нужен и маленький образ, и быстрая пересборка, используйте кэш BuildKit:
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txtЕсли подозреваете, что в кэш попал битый файл, удалите записи одного пакета через pip cache remove numpy или очистите кэш целиком через pip cache purge.
Такой кэш живёт на машине сборки и не попадает в слои образа. Путь /root/.cache/pip верен для сборки от root на Linux. Если в образе другой пользователь, проверьте путь командой pip cache dir внутри контейнера.
В GitHub Actions кэш pip включается одной строкой в actions/setup-python:
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"Ключ кэша строится по содержимому файлов зависимостей, поэтому при неизменном requirements.txt пакеты берутся из кэша. В GitLab CI и других системах кэшируйте каталог из pip cache dir или задайте его явно через PIP_CACHE_DIR.
Когда дело не в сети: сборка и перебор версий
Иногда «pip долго устанавливает пакеты» вообще не про сеть. Если для вашей версии Python или платформы нет готового колеса (wheel), pip скачивает исходники и собирает пакет на месте. В выводе это строка Building wheel for ... (pyproject.toml). Сборке нужны компилятор и заголовочные файлы, и она бывает дольше самой загрузки.
--prefer-binaryвыбирает готовое колесо, даже если уже вышли исходники более новой версии.--only-binary=:all:запрещает сборку из исходников совсем: если колеса нет, установка упадёт сразу, а не после долгой компиляции.- Проверьте версию Python. Для только что вышедшего интерпретатора колёса популярных библиотек появляются не в первый день.
- На Alpine используется musl, и готовых колёс для неё меньше, чем для обычных дистрибутивов с glibc. Образ
python:3.12-slimчасто ставит зависимости быстрее, чемpython:3.12-alpine.
Вторая причина долгой установки без единой сетевой ошибки: перебор версий. Если в логе много строк INFO: pip is looking at multiple versions of ..., резолвер ищет набор версий, совместимых между собой, и скачивает метаданные пакет за пакетом. Помогают точные версии в requirements.txt или файл ограничений: pip install -r requirements.txt -c constraints.txt.
Если колёса нужны на нескольких шагах CI или на многих машинах, соберите их один раз через pip wheel -r requirements.txt -w ./wheels и раздавайте папку как артефакт сборки. Дальше установка из неё не требует ни сети, ни компилятора.
Свой индекс и зеркала
По умолчанию pip ходит в https://pypi.org/simple. Адрес меняется параметром --index-url (коротко -i) или в конфиге:
pip config set global.index-url https://pypi.example.com/simple
# или разово для одной команды
pip install -i https://pypi.example.com/simple -r requirements.txtКакой индекс реально используется, видно в начале вывода: при нестандартном адресе pip печатает строку Looking in indexes: со списком. Если там не то, что вы ожидали, проверьте pip config list и переменную PIP_INDEX_URL.
В компаниях обычно поднимают прокси-репозиторий: Nexus, Artifactory или devpi. Он один раз скачивает пакет с PyPI и дальше раздаёт его из локальной сети. Внешний канал разгружается, когда десятки сборок ставят одни и те же зависимости.
Публичные зеркала PyPI тоже существуют, но выбирайте только те, которым доверяете: индекс отдаёт код, который потом выполняется на вашей машине.
Осторожно с --extra-index-url. pip ищет пакет во всех указанных индексах сразу и берёт подходящую версию из любого. Если внутренний пакет называется так же, как чужой пакет на PyPI, можно получить не тот код. Документация pip прямо называет это атакой dependency confusion. Для приватных пакетов надёжнее один прокси-индекс, который сам объединяет внутренние и публичные пакеты.
Хэши и установка без сети
Для повторяемых сборок фиксируйте не только версии, но и хэши файлов. Утилита pip-compile из пакета pip-tools запишет их в requirements.txt:
pip install pip-tools
pip-compile --generate-hashes requirements.in -o requirements.txtКогда у требования в файле есть хотя бы один --hash, pip сам включает режим --require-hashes: файл с другим содержимым не установится. Это защищает и от подмены, и от тихо повреждённой загрузки.
Если сеть на целевой машине совсем плохая, скачайте пакеты заранее там, где она хорошая, и ставьте из папки:
pip download -r requirements.txt -d ./wheels
# перенесите папку на целевую машину, затем
pip install --no-index --find-links ./wheels -r requirements.txtСкачивайте под ту же платформу и версию Python, что на целевой машине, иначе pip не найдёт подходящие колёса.
uv как более быстрый установщик
uv от Astral умеет работать как замена pip: uv pip install -r requirements.txt. Он качает пакеты параллельно и активно использует собственный кэш. Сеть он не чинит, но настраивается похоже:
| Переменная | Что задаёт | По умолчанию |
|---|---|---|
| UV_HTTP_TIMEOUT | таймаут чтения HTTP в секундах | 30 |
| UV_HTTP_CONNECT_TIMEOUT | таймаут установки соединения в секундах | 10 |
| UV_HTTP_RETRIES | число повторов HTTP-запроса | 3 |
Число одновременных загрузок ограничивает UV_CONCURRENT_DOWNLOADS. Есть и разница в поведении: если указано несколько индексов, uv по умолчанию берёт пакет из первого индекса, где он нашёлся, и не смешивает версии из разных источников. Это защищает от подмены, но может удивить при переходе со схемы с --extra-index-url.
Если таймауты не помогают
Когда даже с большими таймаутами pip рвётся на одних и тех же файлах, а с другой сети всё ставится, дело в маршруте до CDN, с которого PyPI раздаёт файлы. Проверьте скорость отдельно от pip: curl -o /dev/null -w '%{speed_download}\n' <ссылка на колесо>. Если проблема в маршруте до репозитория, помогает защищённое подключение через ближайшую к нему локацию.
Коротко
- Поднимите
--timeoutдо 60 и--retriesдо 10, сохраните черезpip config set. - Обновите pip и используйте
--resume-retriesдля обрывов на больших файлах. - В Docker не отключайте кэш, а подключайте его через
--mount=type=cache. - Ищите в логе
Building wheel: долгую сборку лечат готовые колёса, а не таймауты. - Для команды поднимите прокси-индекс вместо набора
--extra-index-url. - Фиксируйте хэши через
pip-compile --generate-hashes. - Для изолированных машин готовьте папку колёс через
pip download.