Skip to main content

Find and download OS ISO images for Ubuntu, Fedora, openSUSE, Arch, Linux Mint, Rocky Linux, Windows 11, and more

Project description

os-download

PyPI Python 3.10+ uv package manager requests Rich terminal UI pytest Ruff

Support me on Ko-fi


os-download is a two-command Python CLI that finds the latest download URL for 15 popular OS ISO families and pulls them to disk — with signature-checked verification, working resume, parallel and multi-connection downloads, and automatic retries.

os-finder          # resolve latest ISO URLs for all supported OSes
os-download        # download everything that was found, verifying as it goes

Supported operating systems

OS Source Auto-version Format Verification
Ubuntu Launchpad API + releases.ubuntu.com Yes (LTS + latest) .iso Signed (pinned key)
Debian cdimage.debian.org Yes .iso Signed (pinned key)
Linux Mint kernel.org Linux Mint mirror Yes (Cinnamon x64) .iso Signed (pinned key)
Fedora fedoraproject.org mirrors Yes (Workstation + Server) .iso Signed (distro keyring)
OPNsense pkg.opnsense.org Yes .iso.bz2 (auto-extracted) SHA256 if published
pfSense CE Netgate CDN Yes .iso.gz (auto-extracted) SHA256
TrueNAS Scale GitHub Releases API Yes .iso SHA256 if published
Manjaro KDE manjaro.org/products Yes .iso SHA256 if published
MX Linux mxlinux.org / SourceForge Yes (Xfce x64) .iso SHA256 if published
Puppy Linux SourceForge CDN → ibiblio fallback Yes (fossapup64) .iso SHA256 if published
CachyOS mirror.cachyos.org Yes .iso SHA256
openSUSE Tumbleweed download.opensuse.org Yes (Current) .iso SHA256
Arch Linux geo.mirror.pkgbuild.com Yes (latest alias) .iso SHA256
Rocky Linux download.rockylinux.org Yes (latest major x86_64) .iso SHA256
Windows 11 Mido Yes .iso Handled by Mido

Signed means the checksum file itself is verified against a signing key that os-download pins — a mirror cannot forge it. SHA256 means the hash is checked, which proves the download is intact but not that the mirror was honest. See Verification.

Windows 11 — Microsoft's ISO download requires a JavaScript session-token flow that cannot be replicated with plain HTTP. os-download delegates this to Mido, which is fetched at a pinned commit on first use.


Requirements

  • Python 3.10 or newer (3.9 and earlier are not supported)
  • Linux, macOS, or Windows

Two optional binaries unlock more, and os-download works without either:

Binary Without it
gpg Signatures cannot be checked; verification falls back to hash-only and says so
aria2c Downloads use a single connection instead of splitting each file across several
# Debian / Ubuntu
sudo apt install gnupg aria2

# Fedora
sudo dnf install gnupg2 aria2

# Arch
sudo pacman -S gnupg aria2

# macOS
brew install gnupg aria2

Installation

os-download is on PyPI. Either installer puts os-finder and os-download on your PATH in an isolated environment, available from any directory.

With uv (recommended)

# 1. Install uv (skip if you already have it)
curl -LsSf https://astral.sh/uv/install.sh | sh

# 2. Install os-download
uv tool install os-download
# Upgrade to the latest version at any time
uv tool upgrade os-download

# Uninstall
uv tool uninstall os-download

With pipx

pipx install os-download

Bleeding edge

To install the current main rather than the latest release:

uv tool install git+https://github.com/dragonfoxsl/os-download

Development install

git clone https://github.com/dragonfoxsl/os-download
cd os-download
uv sync

# Run from the project directory
uv run os-finder
uv run os-download

Usage

Step 1: Find ISO URLs

# All supported OSes (runs in parallel)
os-finder

# Specific OSes
os-finder --os ubuntu fedora opensuse arch linuxmint rocky

# Save URLs to a custom path
os-finder --output ~/isos/urls.txt

# Increase timeout for slow connections
os-finder --timeout 30

# Machine-readable JSON output
os-finder --json

# Check every OS still resolves to an ISO (exits non-zero if a mirror moved)
os-finder --check

Mirrors reorganise their directory layouts, which makes a finder quietly stop resolving. --check is the same check a scheduled workflow runs weekly against the live mirrors, so a broken finder surfaces as a GitHub issue rather than a surprise at download time.

Step 2: Download

# Download everything found
os-download

# Three simultaneous downloads
os-download --parallel 3

# Skip verification (it is on by default)
os-download --no-verify

# Refuse anything not signed by a pinned distribution key
os-download --require-signature

# Faster: split each file across 16 connections (needs aria2c)
os-download --connections 16

# Force the built-in backend even if aria2c is installed
os-download --backend python

# Give up sooner on a flaky link (each retry resumes)
os-download --retries 1

# Download to a specific directory
os-download --dir /mnt/nas/isos

# Single URL
os-download --url "https://example.com/file.iso"

# Skip automatic decompression of .bz2 / .gz files
os-download --no-decompress

Interrupted downloads resume where they left off — re-run the same command. A dropped connection is retried automatically without failing the file.

Keyboard shortcuts (download dashboard)

Key Action
q Quit cleanly — partial files are saved and can be resumed
Ctrl+C Interrupt — same as q, partial files resume automatically

Download dashboard

When downloading multiple files, os-download shows a live dashboard:

  • Header — active / done / failed counts, total data downloaded, speed sparkline, elapsed time
  • Files — one progress bar per file with size, speed, and ETA
  • Footer — keyboard shortcuts and current session settings

On completion the dashboard transitions to a summary panel showing files downloaded, total data, time taken, and any failures. If downloads fail you are prompted to retry them; the retry runs only the failed files.

At startup, if partial files are detected you are prompted to resume or start from scratch. Files downloaded within the last 24 hours are listed and can be skipped.

Screenshots

os-download live dashboard showing completed, queued, and interrupted downloads

os-finder command help os-download command help


Verification

Every download is verified by default. Two things are checked, in this order:

  1. The signature on the checksum file. A hash fetched from the same mirror as the ISO only proves the download was not truncated — whoever can serve a bad ISO can serve a matching hash. Ubuntu, Debian, Linux Mint and Fedora sign their checksum files, and their signing keys are pinned in downloader/signatures.py. Keys are only ever imported by pinned fingerprint, or from a keyring published over HTTPS by the distribution itself (Fedora rotates its key every release), so a mirror can never introduce a key of its own.
  2. The SHA256 hash of the file, once the document it came from is known to be authentic.
Outcome What it means Result
Signed by a pinned key, hash matches Authentic Download succeeds
Hash matches, but nothing signed it Intact, unproven Succeeds with a warning (fails under --require-signature)
Bad signature The signing key was substituted Fails, file moved to .corrupt
Hash mismatch Corrupt or tampered Fails, file moved to .corrupt
No checksum published Nothing to check against Succeeds with a warning (fails under --require-signature)

A file that fails verification is renamed to <name>.corrupt rather than left in place, so a later resume cannot append to bad bytes. Verified files record a .verified marker, so a multi-gigabyte ISO is not re-hashed on every run.

Signature verification needs gpg on PATH; without it, verification falls back to hash-only and says so.


All flags

os-finder

Flag Default Description
--os all Space-separated list: ubuntu opnsense pfsense debian truenas windows11 manjaro mxlinux puppy cachyos fedora opensuse arch linuxmint rocky
--output ./os-links/all_os.txt Output file for resolved ISO URLs
--timeout 15 HTTP timeout in seconds
--no-interactive off Skip manual override prompt when a URL cannot be found
--json off Print JSON to stdout, suppress progress display
--check off Report which OSes still resolve to an ISO; exits non-zero if any do not
--log ./logs/os-finder.log Log file path
--version Print the version and exit

os-download

Flag Default Description
--file / -f ./os-links/all_os.txt URL list file
--url / -u Download a single URL
--dir / -d ~/Downloads/os-isos Output directory
--parallel 1 Simultaneous downloads
--backend auto auto / aria2 / python. auto uses aria2c for multi-connection downloads when installed
--connections 8 Connections per file when using aria2c
--retries 3 Attempts per file before giving up; a retry resumes
--no-verify off Skip checksum + signature verification (verification is on by default)
--require-signature off Fail unless the checksum file is signed by a pinned distribution key
--no-decompress off Keep .bz2 / .gz files compressed
--no-resume off Start downloads from the beginning even if partial file exists
--no-interactive off Fail fast on error without prompting to continue
--chunk-size 8192 Download chunk size in bytes (built-in backend)
--log ./logs/os-download.log Log file path
--version Print the version and exit

How it works

os-finder                          os-download
─────────────────────────────      ─────────────────────────────
MultiOSDownloadFinder              DownloadManager
  └─ runs all finders in             └─ reads ./os-links/all_os.txt
     parallel via                       runs downloads in parallel
     ThreadPoolExecutor                 via ThreadPoolExecutor

Each finder (BaseOSFinder          Each download
subclass) is independent:            • aria2c, or streams in chunks
  • scrapes its source               • resumes via Range header
  • verifies the URL                 • retries dropped connections
  • returns {variant: url}           • checks signature, then hash
                                     • decompresses .bz2/.gz
                                     • mido:// URIs delegated to Mido

Backends: aria2c when installed (several connections per file), the built-in one otherwise, and curl as a fallback for mirrors that reject the library outright with a 403.

Adding a new OS

  1. Add a finder module under src/os_download/finders/ that subclasses BaseOSFinder from src/os_download/finders/base.py:
class MyOSFinder(BaseOSFinder):
    def __init__(self, timeout: int = 15):
        super().__init__("My OS", timeout)

    def find_download_links(self) -> dict[str, str]:
        try:
            # fetch, scrape, return {variant: url}
            return {"amd64": "https://..."}
        except Exception as exc:
            # Mirror lookups fail routinely. Log why, or a layout change becomes an
            # unexplained "not found".
            self.log_failure(exc)
            return {}
  1. Register it in src/os_download/finders/registry.py:
'myos': MyOSFinder(timeout),
  1. Add 'myos' to OS_CHOICES so src/os_download/cli/finder.py exposes it through --os.

Releasing

Pushing a vX.Y.Z tag publishes to PyPI. The workflow authenticates with a Trusted Publisher over OIDC, so no API token is stored anywhere.

# 1. Bump the version in BOTH places — the workflow aborts if the tag does not match
#    pyproject.toml            version = "0.1.2"
#    src/os_download/__init__.py   __version__ = "0.1.2"

# 2. Update the README for anything user-facing (flags, defaults, behaviour), and
#    regenerate the screenshots if the CLI or dashboard changed
uv run python scripts/render_help_svg.py
uv run python scripts/render_dashboard_svg.py
node scripts/svg2png.mjs

# 3. Verify
uv run ruff check && uv run pytest -q && uv build

# 4. Tag and push
git tag -a v0.1.2 -m "os-download 0.1.2" && git push origin v0.1.2

Get the README right before tagging: the PyPI project page is baked in at upload time, so a later README fix will not appear there without a new release. A published version can be yanked but never re-uploaded.


Credits

Project Role
Mido by @ElliotKillick Windows 11 ISO download — Mido replicates Microsoft's JavaScript session-token flow to deliver a direct ISO. os-download clones and invokes it automatically.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

os_download-0.1.2.tar.gz (923.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

os_download-0.1.2-py3-none-any.whl (51.9 kB view details)

Uploaded Python 3

File details

Details for the file os_download-0.1.2.tar.gz.

File metadata

  • Download URL: os_download-0.1.2.tar.gz
  • Upload date:
  • Size: 923.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for os_download-0.1.2.tar.gz
Algorithm Hash digest
SHA256 4ab21cdc6df5c28c0cb387771b3ab9b67c7aaa70afd0f7023a7b13c78254303a
MD5 476b62460f76b18ef2a822420ef15a91
BLAKE2b-256 b850cfef65a02dcd22e4da05a92c96bb328823afa254c96bd4c0b8ff38ffa521

See more details on using hashes here.

Provenance

The following attestation bundles were made for os_download-0.1.2.tar.gz:

Publisher: release.yml on dragonfoxsl/os-download

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file os_download-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: os_download-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 51.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for os_download-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 747011e75daacb3e0053ed10d93e6d75c8c422eac22ec0afd9b39c0f5ab05004
MD5 6e21fedc80dc275401085546595903d2
BLAKE2b-256 22c3533ff1dd6ab851783716dec41c899002f0bf9a6fdabadf2a3e2809ff9f74

See more details on using hashes here.

Provenance

The following attestation bundles were made for os_download-0.1.2-py3-none-any.whl:

Publisher: release.yml on dragonfoxsl/os-download

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page