TUI toolkit for maintaining a PyPI mirror on an airgapped network
Project description
pypi-pigeon
A toolkit for maintaining a PyPI mirror on an airgapped network. Packages are brought over via sneakernet (DTA — physically moving drives between networks).
Uses bandersnatch for the base mirror and a supplement workflow for packages that need full dependency resolution. Features a Textual TUI for interactive use and --plain mode for scripting.
Install
pip install pypi-pigeon
# or with uv:
uv tool install pypi-pigeon
Workflow
Internet-connected machine
pigeon setup # one-time wizard: configure pigeon.toml + generate bandersnatch.conf
pigeon dry-run # optional: estimate mirror size before committing (takes hours, resumable)
pigeon sync # run bandersnatch + fetch supplement packages
DTA the following to your airgapped server:
<mirror-dir>/web/— the base mirrorsupplement/dist/— supplement wheels (if you usedpigeon add)
Airgapped server
pigeon merge # fold supplement wheels into the mirror's simple/ index
Re-run pigeon merge after every bandersnatch sync — bandersnatch overwrites simple/<pkg>/index.html for packages it manages, wiping supplement links for those packages. Merge is idempotent and fast.
Commands
| Command | Description |
|---|---|
pigeon setup |
TUI wizard — configure pigeon.toml and generate bandersnatch.conf |
pigeon dry-run |
Fetch PyPI metadata to estimate mirror size before syncing |
pigeon sync |
Run bandersnatch + fetch supplement packages |
pigeon mirror |
Alias for sync (bandersnatch's own term) |
pigeon merge |
Fold supplement/dist/ into the mirror's simple/ index |
pigeon add <pkg> |
Append packages to the supplement list |
--plain flag — add to dry-run, sync/mirror, or merge to stream plain stdout instead of launching the TUI. Useful for scripting and CI.
--config PATH — available on all commands. By default pigeon searches up the directory tree for pigeon.toml (git-style), so you can run commands from any subdirectory of your mirror workspace.
Config
pigeon setup creates pigeon.toml:
[mirror]
dir = "/path/to/mirror" # where bandersnatch writes; nginx serves web/ from here
workers = 10 # hard max 10 (bandersnatch limit)
keep_releases = 3
diff_file = "" # path to write a changed-file list each sync; "" = disabled
[filter]
python_versions = ["3.10"]
platforms = ["linux-manylinux-x86_64"]
include_sdists = false
include_prereleases = false
allowlist_packages = [] # mirror only these packages; empty = mirror everything
blocklist_packages = []
[supplement]
dist_dir = "supplement/dist"
packages_file = "requirements.txt"
Edit directly or re-run pigeon setup anytime to reconfigure.
Supplement packages
The base mirror uses aggressive filtering (specific Python version, platform, latest N releases). For packages outside those filters — or pinned versions you need — use the supplement:
pigeon add requests numpy==1.26.0
# or edit supplement/packages.txt directly (standard requirements.txt format)
During pigeon sync, supplement packages are fetched via pip download --only-binary :all: with full transitive dependency resolution. The result is a self-contained closure of wheels — no missing dependencies on the airgapped side.
Supported platforms
The setup wizard lets you pick any combination of:
- Linux manylinux x86_64 / aarch64 / i686
- Linux musllinux x86_64 / aarch64 (Alpine)
- Windows AMD64 / x86 / ARM64
- macOS x86_64 (Intel) / ARM64 (Apple Silicon)
Bandersnatch gotchas
Plugin names changed in 7.x — blocklist_release_files and keep_only_latest_releases no longer exist and are silently ignored. pigeon generates the correct config automatically; don't hand-edit bandersnatch.conf for anything covered by pigeon.toml.
Workers hard max = 10 — bandersnatch raises an exception above 10. The setup wizard enforces this.
Test runs — to verify filters before committing to a full sync, temporarily add an [allowlist] section to bandersnatch.conf. Expected results: numpy → only cp310-manylinux-x86_64 wheels; cryptography → only abi3-manylinux-x86_64; requests → only py3-none-any. Zero .tar.gz files. Remove the allowlist before re-running pigeon setup — it regenerates bandersnatch.conf from scratch.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pypi_pigeon-0.1.0.tar.gz.
File metadata
- Download URL: pypi_pigeon-0.1.0.tar.gz
- Upload date:
- Size: 25.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86cd877309a3bf556d193a3ffc4930a50b611924bc6338c3a304e161e7cecca1
|
|
| MD5 |
09e1b9d80482068b6529691f96158327
|
|
| BLAKE2b-256 |
86682de1f560988766a3175f91eb1311d401b7bce82ccc178de463e1ad472514
|
File details
Details for the file pypi_pigeon-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pypi_pigeon-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
598c8693fda23004850c9d6a31dab4b8a95ea73b9e31d9bcba32659965c7de5c
|
|
| MD5 |
20dfd1593102a9937df70cf593988cc8
|
|
| BLAKE2b-256 |
e4050eb3334fd7639e95f838835211672e421fca6f3c057d5deb4325ecb7c59f
|