Audience: developers and CI maintainers running Python, data, or ML pipelines on a remote Mac who need reliable cross-border batch installs of wheels and sdists. Keywords: remote Mac, uv, PyPI mirror, Python CI, lock file, batch pull. This guide is tables and checklists first—pair it with our Git & Docker pull acceleration and cross-border mirror CI patterns. Return to the blog index or MacPull home anytime—no login required to browse.

① Scenario fit: pip vs uv vs conda (when uv wins)

Use this matrix before you standardize a toolchain on shared remote Mac runners. It assumes Apple Silicon or Intel macOS build hosts and WAN pulls to PyPI or regional mirrors.

Tool Choose when Trade-off on remote Mac CI
pip (+ venv) Legacy scripts, minimal moving parts, vendor already standardized on requirements.txt Slower cold resolves; fewer first-class knobs for concurrent downloads than uv
uv Fast resolves/installs, uv.lock for apps/services, mixed wheel+sdist graphs, ML stacks with heavy parallel fetches Team must learn uv sync/uv run and lockfile policy; mirror config moves to UV_* env and pyproject
conda/mamba Binary stacks dominated by conda-forge (some scientific bundles), non-PyPI channels Larger tarballs and different cache semantics; less overlap with pure PyPI mirror tuning in this article

Rule of thumb for 2026 Python CI on MacPull-style nodes: if most dependencies are on PyPI and you want one lockfile plus aggressive parallel I/O, default to uv. Keep pip in thin wrapper jobs only when upstream docs mandate it.

② Executable parameter matrix (concurrency, timeout, cache, mirror)

These variables are read directly by uv (see the upstream environment reference). Tune them per link quality; raise timeouts before you raise concurrency on lossy cross-border paths.

Knob Typical CI value Effect
UV_CONCURRENT_DOWNLOADS 832 (start 16) Cap in-flight wheel/sdist HTTP downloads
UV_CONCURRENT_BUILDS 14 Parallel source builds (watch CPU/RAM on small Mac nodes)
UV_CONCURRENT_INSTALLS default or 48 Unzip/link parallelism into the venv
UV_HTTP_TIMEOUT / HTTP_TIMEOUT 120300 Read timeout seconds for registry and file fetches
UV_HTTP_CONNECT_TIMEOUT 1545 TCP/TLS connect phase; increase if handshake stalls
UV_HTTP_RETRIES 510 Transient 5xx/connection drops on long haul links
UV_CACHE_DIR /var/ci/uv-cache or per-runner path Persistent warm cache between jobs (isolate pools if needed)
UV_DEFAULT_INDEX https://pypi.org/simple or mirror URL Primary Simple API; preferred over legacy UV_INDEX_URL name
UV_INDEX / extra indexes space-separated Simple URLs Secondary mirrors or private indexes (combine with strategy flags carefully)

Copy-paste block for a shell step (adjust mirror host to your approved endpoint):

export UV_CACHE_DIR="${UV_CACHE_DIR:-$HOME/Library/Caches/uv-ci}"
export UV_DEFAULT_INDEX="${UV_DEFAULT_INDEX:-https://pypi.org/simple}"
# Fallback mirror (example placeholder—replace with your org mirror):
# export UV_DEFAULT_INDEX="https://your-mirror.example.com/simple"

export UV_CONCURRENT_DOWNLOADS="${UV_CONCURRENT_DOWNLOADS:-16}"
export UV_CONCURRENT_BUILDS="${UV_CONCURRENT_BUILDS:-2}"
export UV_HTTP_TIMEOUT="${UV_HTTP_TIMEOUT:-180}"
export UV_HTTP_CONNECT_TIMEOUT="${UV_HTTP_CONNECT_TIMEOUT:-30}"
export UV_HTTP_RETRIES="${UV_HTTP_RETRIES:-8}"

uv sync --frozen --no-dev

pip-style job without a pyproject workspace:

uv venv .venv
source .venv/bin/activate
uv pip install -r requirements.txt \
  --index-url "${UV_DEFAULT_INDEX:-https://pypi.org/simple}"

③ Cross-border failures: TLS, certificates, and proxies

Symptoms cluster into three buckets: handshake failures, MITM corporate roots, and proxy routing loops. Fix certificate trust first; only then chase higher UV_HTTP_RETRIES.

1
Custom CA bundle. Point tools at a merged PEM: export SSL_CERT_FILE=/path/to/org-trust.pem. uv also honors SSL_CERT_DIR for hashed certificate folders.
2
Native macOS trust store. If keys live in Keychain, try export UV_NATIVE_TLS=true or export UV_SYSTEM_CERTS=true (newer uv) so the platform store participates in verification—still document the exact macOS image you bake into CI.
3
HTTP(S) proxy variables. Align HTTPS_PROXY, HTTP_PROXY, and ALL_PROXY with your egress policy. Use NO_PROXY for on-prem indexes, metadata endpoints, or link-local health checks.
export HTTPS_PROXY="http://proxy.example.com:8080"
export NO_PROXY="localhost,127.0.0.1,.internal,private-index.example.com"
# Optional: enterprise PEM
export SSL_CERT_FILE="/etc/ssl/certs/org-bundle.pem"

For shared-runner contention patterns (not Python-specific), see pull stability FAQ and the Rust mirror/retry matrix—the same retry mindset applies across ecosystems.

④ Lockfiles, pyproject, and reproducible CI steps

Reproducibility is a contract between resolver inputs and the committed graph. On remote Mac CI, separate resolve/fetch from test so flaky WAN shows up early.

1
Commit uv.lock for applications. Run uv lock on a developer machine or a lock refresh job, then commit the diff intentionally—never silently during unrelated PRs.
2
CI install discipline. Prefer uv sync --frozen --no-dev (or UV_FROZEN=1) so the job cannot rewrite the lock. Use uv sync --locked when you want uv to verify the lock matches pyproject.toml and fail if drift exists.
3
requirements-style exports. When downstream tools still read requirements.txt, export from the lock: uv export --format requirements.txt --frozen --output-file requirements.lock (filename is organizational; --frozen keeps the export aligned with the committed uv.lock without re-resolving).
4
Hash pinning (supply chain). For audited pipelines, generate requirements with hashes and set UV_REQUIRE_HASHES=true during install so any tampered artifact fails closed.
# Job A: fetch + install only
uv sync --frozen --no-dev
# Job B: tests assume env is warm
uv run pytest -q

⑤ FAQ: cache pollution, platform wheels, private indexes

Cache pollution. Share one UV_CACHE_DIR per stable runner image key, or namespace by team. Schedule uv cache prune when disk crosses your watermark; combine with the concurrency guidance in build pool disk FAQ.

Platform wheels. Apple Silicon resolves macosx_11_0_arm64 tags; Intel runners need different wheels. If you generate locks on the wrong arch, CI rebuilds sdists or fails. Align runner architecture or use explicit platform flags when locking for heterogeneous pools.

Private + public PyPI. Put the private Simple API on UV_DEFAULT_INDEX when it should win, add PyPI through UV_INDEX or tool.uv.index entries, and avoid unsafe-best-match unless you understand cross-index version competition. Authenticate with UV_INDEX_{NAME}_USERNAME / UV_INDEX_{NAME}_PASSWORD for named indexes.

Structured FAQ for search engines mirrors the questions above (cache isolation, wheel tags, mixed indexes).

Summary

Pick uv when PyPI throughput and lockfile speed matter on a remote Mac; drive mirrors through UV_DEFAULT_INDEX and friends; raise UV_HTTP_TIMEOUT and UV_HTTP_RETRIES before you chase ever-higher UV_CONCURRENT_DOWNLOADS. Commit uv.lock and run uv sync --frozen in CI for deterministic installs.

When you need Apple Silicon close to your users with stable egress for large pulls, browse MacPull pricing and help & getting started—both are readable without logging in. Choose a node size that matches your parallel build+download footprint; oversized concurrency on a small VM only amplifies timeouts.

Continue the dependency story with Git, npm & CI cache strategy, then compare plans on the MacPull homepage and proceed to purchase when you are ready for a dedicated remote Mac.

Remote Mac for Python & ML CI

Dedicated Mac Mini nodes, SSH access, and room for heavy uv caches. View pricing, purchase, and help without signing in.