Fuzzy Debian mirror picker centered in Europe
  • Python 92%
  • Shell 8%
Find a file
2026-10-11 14:35:43 +01:00
.gitignore Initial commit: lazy async Europe-first Debian mirror picker 2026-10-11 14:31:31 +01:00
apply_debian_mirrors.template.sh Add helper apply-script template for reference; README clarifies it is generated per run 2026-10-11 14:35:43 +01:00
debian_mirror_picker.py Initial commit: lazy async Europe-first Debian mirror picker 2026-10-11 14:31:31 +01:00
README.md Add helper apply-script template for reference; README clarifies it is generated per run 2026-10-11 14:35:43 +01:00

debian-mirror-picker

Lazy, async, Europe-first Debian archive mirror picker (stdlib only, no dependencies).

What it does

  1. Fetches the authoritative mirror list from https://mirror-master.debian.org/status/Mirrors.masterlist (fallback: https://www.debian.org/mirror/list-full — the same source software-properties-style tools parse). The masterlist carries a Country: code per site, which drives geographic ordering.
  2. Orders candidates by proximity: home country first (autodetected from current apt sources, e.g. ftp.es.debian.org → ES, else --home-cc), then the rest of Europe, then the rest of the world. CDN entries (deb.debian.org) probe early.
  3. Nudges each server with a tiny async ranged HTTP GET (<base>dists/stable/Release, Range: bytes=0-8191) and a deliberately short per-probe timeout (default 4 s). Slow or silent hosts surrender quickly and are reported as not good enough.
  4. Lazy early-exit (non-exhaustive by design): completions stream via asyncio.as_completed so fast responses float up. As soon as --fast-count (default 5) European mirrors answer within --fast-threshold (default 1.5 s), pending probes are cancelled and non-European mirrors are downgraded/skipped.
  5. Rewrites apt sources to $HOME/tmp, never in place: every Debian archive entry in /etc/apt/sources.list and /etc/apt/sources.list.d/ (*.list one-liners + DEB822 *.sources) gets the fastest winner. security.debian.org and third-party repos (Brave, Mint, WezTerm, …) are left untouched.
  6. Generates a helper script in $HOME/tmp that backs up current files with a timestamp and offers to install the new ones (run by hand).

Usage

python3 debian_mirror_picker.py [options]
Option Default Meaning
--fast-count 5 European mirrors ≤ threshold that trigger early-exit
--fast-threshold 1.5 seconds; "fast enough" bar
--timeout 4.0 per-probe surrender timeout (s)
--concurrency 20 max parallel probes
--top 3 winners recorded as primary + alternatives
--home-cc autodetect home country code, e.g. ES
--outdir $HOME/tmp/apt-mirrors-<ts> rewritten sources go here
--apply-script $HOME/tmp/apply_debian_mirrors.sh generated helper script
--max-mirrors 0 (no cap) debug cap on candidates

Example:

python3 debian_mirror_picker.py --timeout 4 --concurrency 20 \
    --fast-count 5 --fast-threshold 1.5 --top 5

Applying the result (you, by hand)

Each run writes a ready-to-use helper to ~/tmp/apply_debian_mirrors.sh:

bash ~/tmp/apply_debian_mirrors.sh

The helper shows a diff, asks to back up current files (/etc/apt/backups-<timestamp> plus *.bak-<timestamp> next to the originals), then asks before copying the new ones in. Follow up with:

sudo apt update

Files

  • debian_mirror_picker.py — the whole tool, stdlib only.
  • apply_debian_mirrors.template.sh — reference copy of the helper template (the picker embeds it as APPLY_SCRIPT_TEMPLATE and writes a concrete copy with your output dir to ~/tmp/apply_debian_mirrors.sh on each run).
  • README.md — this file.

Requirements

Python 3.10+ (uses asyncio, urllib, pathlib — nothing else). Network access to mirror-master.debian.org and the mirrors. Read access to /etc/apt/sources.list* (rewrites never touch them directly).