Pool-based content-addressable APT repository mirror sync with global deduplication via hardlinks.
Features
- Pool-based content-addressable storage: every unique file is stored once by SHA-256 hash
- Hardlink deduplication across all mirrored repositories
- HTTPS-based sync with automatic upstream failover
- Auto-discovery of distributions, architectures, and components via HTTP probing
- Snapshot/restore system for safe repository migrations
- Per-repo distribution expansion toggle (e.g.
noble→noble-updates,noble-security, etc.) - GPG key URL tracking in per-repo configuration
- Stale pool orphan detection and cleanup (
--sweep-pool)
Installation
Option 1: Debian/Ubuntu Package (Recommended)
Download the latest .deb package from GitHub Releases:
curl -fsSL https://api.github.com/repos/munger/mirror-dedupe/releases/latest \
| grep browser_download_url | grep all.deb | head -1 | cut -d'"' -f4 \
| xargs wget
sudo dpkg -i mirror-dedupe_*.deb
This includes systemd integration, man pages, and proper package management.
Option 2: PyPI (All Linux Distributions)
pip install mirror-dedupe
Then install systemd files manually:
sudo ./install.sh --pip
Option 3: From Source
git clone https://github.com/munger/mirror-dedupe.git
cd mirror-dedupe
sudo ./install.sh
Configuration
Configuration files are located in /etc/mirror-dedupe/:
mirror-dedupe.conf— Global settings (mirror_root, concurrency, etc.)repos-available/— Available repository configurationsrepos-enabled/— Enabled repositories (symlinks to repos-available)
mirror_root is the single root for all mirror data. Repositories are stored
under <mirror_root>/repos and the deduplication pool under <mirror_root>/pool.
Both always share the same logical volume, which is required for hardlinks.
Adding a Repository
Use --scan to auto-generate configuration for a repository:
mirror-dedupe --scan --name grafana --out /etc/mirror-dedupe/repos-available https://apt.grafana.com
See config/repos-available/README.md for the full scan reference and
ready-made commands for all packaged example repositories.
Then test and enable it:
mirror-dedupe --test grafana
mirror-dedupe --activate grafana
Advanced: Alternative config directory
Override the default config directory with --config-dir:
mirror-dedupe --config-dir /tmp/mirror --test grafana
Pre-configured Repositories
The package includes pre-configured repository definitions in config/repos-available/.
Usage
# Sync all enabled mirrors
mirror-dedupe --sync
# Sync a specific mirror
mirror-dedupe --sync --mirror ubuntu
# Scan a repository and generate config (single release)
mirror-dedupe --scan --name ubuntu --release noble --out /etc/mirror-dedupe/repos-available http://archive.ubuntu.com/ubuntu
# Scan with all discovered architectures and distributions
mirror-dedupe --scan --name postgresql --no-filter --out /etc/mirror-dedupe/repos-available http://apt.postgresql.org/pub/repos/apt
# Scan without JSON snapshot
mirror-dedupe --scan --name grafana --out /etc/mirror-dedupe/repos-available https://apt.grafana.com
# Pool orphan sweep
mirror-dedupe --sweep-pool
# Create a hardlink snapshot of all repos
mirror-dedupe --snapshot
Systemd Integration
If installed via Debian package, systemd is already configured. Otherwise:
sudo systemctl enable --now mirror-dedupe.timer
sudo systemctl status mirror-dedupe.timer
View logs:
journalctl -u mirror-dedupe.service
Nginx Configuration
See nginx/mirror.conf for an example nginx configuration.
License
MIT License — see LICENSE file for details.
Author
Tim Hosking tim@mungerware.com
Release files for mirror-dedupe 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mirror_dedupe-1.1.1.tar.gz | 190.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mirror_dedupe-1.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 447.7 kB
Release files / mirror_dedupe-1.1.1.tar.gz
| Download URL | mirror_dedupe-1.1.1.tar.gz |
|---|---|
| Size | 190.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
67057f516ae09b0fefb74b8877f2eb34738fb1f87bfa6d42c9bd473dac3f5b73
|
|
BLAKE2b-256 checksum How to use checksums |
6550cc82f5d8d54d9f846b654d843df0f784eabbb683d21d06a31260ff7e0cdd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Release files / mirror_dedupe-1.1.1-py3-none-any.whl
| Download URL | mirror_dedupe-1.1.1-py3-none-any.whl |
|---|---|
| Size | 257.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3f2fea2c147e4ed991666f0b8eed358452595ff2319877d4cafc7150c31b1eaa
|
|
BLAKE2b-256 checksum How to use checksums |
250596d93452a1a37fc33978139e85ec168524759863114959875abe975ff1e1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|