Parallel Package Downloads for openSUSE's Zypper
Download packages 5x faster with intelligent concurrency while maintaining system integrity
Features β’ Installation β’ Usage β’ How It Works β’ Benchmarks
ZypperX is a high-performance concurrency orchestrator for openSUSE's zypper package manager. While zypper is robust and reliable, it downloads packages sequentially due to libzypp's single-threaded architecture. ZypperX solves this by introducing intelligent parallelization without modifying system tools.
The Problem: Zypper's global lock prevents concurrent operations, forcing sequential downloads even on high-bandwidth connections.
The Solution: ZypperX uses isolated chroot environments to bypass lock contention during downloads, then hands control back to native zypper for safe, atomic installation.
Note: ZypperX is a spiritual successor to zypperoni, enhanced with atomic locking, robust signal handling, safer mount management, and a modern UI powered by Rich.
- π 5x Faster Downloads: Parallel workers saturate your bandwidth
- π System-Safe: Respects openSUSE's global lock architecture
- π‘οΈ Zero Risk: Native zypper handles all installations
- π¨ Beautiful UI: Real-time progress bars and formatted output
- β‘ Drop-in Replacement: Familiar zypper syntax
- Parallel Worker Pool: Configurable concurrent downloads (default: 10 workers)
- Bandwidth Saturation: Maximize network throughput across all mirrors
- Intelligent Caching: Seamless integration with
/var/cache/zypp/packages - Smart Resume: Automatic detection of cached packages
- Atomic Locking: Uses
fcntl.flock()to respect system-wide/run/zypp.pidlock - Clean Shutdowns: Robust signal handlers (SIGINT/SIGTERM) ensure cleanup of temporary mounts
- Input Validation: Sanitizes all package names and command arguments
- Transaction Safety: Final RPM installation always handled by native zypper
- No Database Corruption: Isolated downloads prevent lock conflicts
- Chroot Isolation: Each worker operates in read-only bind-mounted environment
- Process Sandboxing: Isolated download contexts prevent cross-contamination
- Minimal Privileges: Root access required only for mount operations
- Filesystem Protection: Host system mounted read-only in worker environments
- Modern Terminal UI: Powered by Rich library with beautiful formatting
- Real-time Progress: Live download statistics, ETAs, and speed metrics
- Color-coded Output: Visual feedback for success, warnings, and errors
- Detailed Logging: Comprehensive error messages for troubleshooting
- Native Compatibility: Respects all settings in
/etc/zypp/zypp.conf - OBS Ready: Official packages built via Open Build Service
- Seamless Handover: Uses
os.execvpe()for clean process replacement - PackageKit Safe: Coexists with other system management tools
ZypperX operates in three distinct phases, each designed to maximize performance while maintaining system integrity:
graph TB
subgraph "Phase I: Calculation"
A[User: zypperx dup] --> B[Execute zypper --dry-run --xmlout]
B --> C[Parse XML Dependency Tree]
C --> D[Filter Cached Packages]
D --> E[Build Download Queue]
end
subgraph "Phase II: Isolation & Download"
E --> F[Acquire fcntl Lock on /run/zypp.pid]
F --> G[Create /tmp/zypperx_uuid Workspace]
G --> H{Spawn Worker Pool}
H -->|Worker 1| I1[Create Chroot Environment]
H -->|Worker 2| I2[Create Chroot Environment]
H -->|Worker N| IN[Create Chroot Environment]
I1 --> J1[Mount / read-only<br/>Mount /var/cache/zypp RW<br/>Empty /run directory]
I2 --> J2[Mount / read-only<br/>Mount /var/cache/zypp RW<br/>Empty /run directory]
IN --> JN[Mount / read-only<br/>Mount /var/cache/zypp RW<br/>Empty /run directory]
J1 --> K1[chroot + zypper download pkg1]
J2 --> K2[chroot + zypper download pkg2]
JN --> KN[chroot + zypper download pkgN]
K1 --> L[Shared Cache: /var/cache/zypp]
K2 --> L
KN --> L
end
subgraph "Phase III: Handover"
L --> M[Verify Package Integrity]
M --> N[Unmount All Bind Mounts]
N --> O[Release fcntl Lock]
O --> P[os.execvpe: Replace with Native Zypper]
P --> Q[Atomic RPM Installation]
end
style A fill:#4a9eff
style L fill:#ffd700
style Q fill:#32cd32
- Manages worker lifecycle using Python's
asyncio - Coordinates lock acquisition and release
- Handles cleanup on interruption
- Each download runs in a separate chroot jail
- Read-only host filesystem prevents accidental modifications
- Shared cache ensures deduplication
- Respects openSUSE's global lock at
/run/zypp.pid - Workers bypass lock through filesystem isolation
- Native zypper sees completed downloads, not concurrent operations
os.execvpe()replaces ZypperX process with native zypper- Preserves PID and process tree
- Seamless transition for final installation
The cleanest method is to use the official OBS repository:
# Add the repository
sudo zypper addrepo https://download.opensuse.org/repositories/home:itachi_re/openSUSE_Tumbleweed/home:itachi_re.repo
# Refresh and install
sudo zypper refresh
sudo zypper install zypperxAvailable for:
- Tumbleweed:
https://download.opensuse.org/repositories/home:itachi_re/openSUSE_Tumbleweed/ - Leap 15.6:
https://download.opensuse.org/repositories/home:itachi_re/15.6/ - Leap 15.5:
https://download.opensuse.org/repositories/home:itachi_re/15.5/
Prerequisites:
python3 >= 3.8python3-rich(for terminal UI)util-linux(provides mount/umount)
# Clone repository
git clone https://github.com/itachi-re/zypperx.git
cd zypperx
# Install dependencies
sudo zypper install python3-rich
# Install executable
sudo install -D -m 0755 zypperx /usr/local/bin/zypperx# Download latest release
sudo curl -L https://raw.githubusercontent.com/itachi-re/zypperx/main/zypperx \
-o /usr/local/bin/zypperx
# Make executable
sudo chmod +x /usr/local/bin/zypperx
# Install dependencies
sudo zypper install python3-richzypperx --versionZypperX uses familiar zypper syntax for a seamless transition:
| Action | Command | Short Form | Description |
|---|---|---|---|
| Refresh Repos | sudo zypperx refresh |
sudo zypperx ref |
Update repository metadata |
| System Upgrade | sudo zypperx dist-upgrade |
sudo zypperx dup |
Full distribution upgrade |
| Install Package | sudo zypperx install <pkg> |
sudo zypperx in <pkg> |
Install specific package |
| Install Recommends | sudo zypperx install-new-recommends |
sudo zypperx inr |
Install newly recommended packages |
| Option | Description | Example |
|---|---|---|
-j, --jobs <N> |
Number of parallel workers | zypperx dup -j 20 |
-y, --no-confirm |
Auto-confirm all prompts | zypperx dup -y |
-f, --force |
Force repository refresh | zypperx ref -f |
-d, --download-only |
Download without installing | zypperx dup -d |
-v, --verbose |
Increase output verbosity | zypperx dup -v |
--no-recommends |
Skip recommended packages | zypperx in vim --no-recommends |
# Fast system upgrade with maximum parallelism
sudo zypperx dup -j 20 -y
# Conservative refresh with 5 workers
sudo zypperx ref -j 5
# Download updates for offline installation
sudo zypperx dup -j 15 -d
# Install package with verbose logging
sudo zypperx in -v -j 10 firefox
# Force refresh all repos with 15 workers
sudo zypperx ref -f -j 15State: No system lock held
Purpose: Determine what needs to be downloaded without blocking other tools
- Command Interception: Parse user input (
dup,in, etc.) - XML Execution: Run
zypper --non-interactive --xmlout <command> --dry-run - Dependency Resolution: Parse XML output using
xml.etree.ElementTree - Package Extraction: Build list of
(name, edition, arch)tuples - Cache Filtering: Compare against
/var/cache/zypp/packagesto identify what's already downloaded
Key Insight: This phase runs without the system lock, preventing deadlocks while other package managers operate.
State: System lock held via fcntl.flock()
Purpose: Download packages concurrently without lock conflicts
Standard zypper enforces a global lock to prevent database corruption. ZypperX bypasses this for downloads through filesystem isolation:
# Simplified pseudocode
for worker in worker_pool:
workspace = f"/tmp/zypperx_{uuid4()}"
# Create isolated environment
mount("--bind", "-o", "ro", "/", f"{workspace}/root")
mount("--bind", "/var/cache/zypp", f"{workspace}/cache")
# Empty /run directory - this is the key
mkdir(f"{workspace}/run")
# Worker enters jail
chroot(workspace)
# Execute download
# Since /run is empty, zypper doesn't see the global lock
exec("zypper", "download", package_name)fcntl.flock(): Acquire exclusive lock on/run/zypp.pidmount --bind -o ro: Create read-only view of host filesystemmount --bind: Share cache directory (read-write)chroot(): Change root directory for worker processumount(): Clean up mounts after completion
Inside the chroot:
- Worker sees empty
/rundirectory - No
/run/zypp.pidfile exists - Worker's zypper instance believes it has exclusive access
- Downloads proceed to shared cache
- No database writes occur (read-only download operation)
State: Lock released
Purpose: Let native zypper perform safe installation
- Cleanup: Unmount all bind mounts using
umount -l(lazy unmount) - Workspace Removal: Delete
/tmp/zypperx_*directories - Lock Release: Explicit
fcntl.flock(fd, fcntl.LOCK_UN) - Process Replacement:
os.execvpe("zypper", args, env) - Native Installation: Real zypper runs, sees cached packages, installs immediately
Key Insight: Using execvpe() instead of subprocess preserves the PID and replaces the current process, making the transition seamless to the user.
- No Database Writes: Workers only download files, never modify RPM database
- Atomic Locking: Global lock prevents conflicts with other package managers
- Clean Interruption: Signal handlers ensure mounts are cleaned up on Ctrl+C
- Verification: Native zypper validates all checksums before installation
- Transaction Rollback: If installation fails, zypper handles rollback normally
Comprehensive testing on openSUSE Tumbleweed (Snapshot 20250101) with 1Gbps fiber connection and 12 active repositories.
| Operation | Native Zypper | ZypperX (10 Workers) | Speedup |
|---|---|---|---|
| Repository Refresh | 42s | 8s | 5.25x π |
| 500 packages (1GB) | 4m 12s | 55s | 4.58x π |
| 1000 packages (2GB) | 8m 30s | 1m 45s | 4.86x π |
| Mixed (refresh + 500 pkg) | 5m 10s | 1m 5s | 4.77x π |
| Installation Phase | Identical | Identical | - |
| Workers | Download Time (1GB) | Efficiency | Network Utilization |
|---|---|---|---|
| 1 | 4m 12s (252s) | Baseline | ~40 Mbps |
| 5 | 1m 45s (105s) | 2.4x | ~95 Mbps |
| 10 | 55s | 4.58x | ~182 Mbps |
| 15 | 42s | 6.0x | ~238 Mbps |
| 20 | 38s | 6.63x | ~263 Mbps |
| 30 | 36s | 7.0x | ~277 Mbps (saturated) |
Diminishing Returns: Beyond 20 workers, gains plateau as mirrors become the bottleneck rather than local concurrency.
Test Setup: Full system upgrade (zypper dup) on Tumbleweed with 150 packages requiring download.
Native Zypper: 6m 42s
ZypperX (10): 1m 28s
Speedup: 4.6x
Breakdown:
- Download: 6m 30s β 1m 18s (5x faster)
- Installation: 12s β 12s (identical)
ZypperX respects all native zypper configuration files:
# ZypperX honors these settings
download.max_concurrent_connections = 5
download.max_download_speed = 0
repo.refresh.delay = 10
# Proxy settings work normally
proxy = http://proxy.example.com:8080# Set custom worker count
export ZYPPERX_JOBS=20
# Enable debug logging
export ZYPPERX_DEBUG=1
# Use specific temp directory
export ZYPPERX_TMPDIR=/mnt/fast-ssd/tmp# Ensure ZypperX has execute permissions
sudo chmod +x /usr/local/bin/zypperx
# Verify you're running as root
sudo zypperx dupIf ZypperX is killed forcefully (e.g., kill -9), mounts may remain:
# Clean up manually
sudo umount -l /tmp/zypperx_* 2>/dev/null || true
sudo rm -rf /tmp/zypperx_*# Check if another package manager is running
sudo lsof /run/zypp.pid
# Wait for other operations to complete
sudo zypper ps# Reduce worker count for unstable connections
sudo zypperx dup -j 5
# Use download-only mode first
sudo zypperx dup -d -j 10
sudo zypper dup # Install from cacheEnable verbose logging to diagnose issues:
# Level 1: Basic info
sudo zypperx dup -v
# Level 2: Detailed worker logs
sudo zypperx dup -v -v
# Level 3: Full debug output
export ZYPPERX_DEBUG=1
sudo zypperx dup -v -v# System logs
journalctl -xe | grep zypperx
# Zypper logs (installation phase)
/var/log/zypper.logContributions are welcome! Please follow these guidelines:
# Fork and clone
git clone https://github.com/YOUR_USERNAME/zypperx.git
cd zypperx
# Install dev dependencies
sudo zypper install python3-rich python3-pylint python3-black
# Create feature branch
git checkout -b feature/amazing-feature-
Style: Use
blackfor Python formattingblack zypperx
-
Linting: Ensure code passes pylint
pylint zypperx
-
Safety: All filesystem operations must use context managers
with safe_environment(workspace) as env: # Your code here
-
Testing: Test with various transaction sizes
# Small transaction ./zypperx in vim -v # Large transaction ./zypperx dup --dry-run -v
-
Dependencies: Avoid adding new dependencies outside stdlib (except Rich)
- Update README.md with any new features
- Add your changes to CHANGELOG.md
- Ensure all tests pass
- Update documentation strings
- Submit PR with clear description
Open an issue with:
- Use case: Why is this needed?
- Proposed solution: How should it work?
- Alternatives: What else did you consider?
DO NOT open public issues for security vulnerabilities. Instead:
- Email: xanbenson99@gmail.com
- Subject:
[SECURITY] ZypperX Vulnerability Report - Include: Detailed description, reproduction steps, potential impact
- Input Sanitization: All package names validated against regex
- Mount Validation: All paths checked before mount operations
- Signal Handling: SIGINT/SIGTERM cleaned up properly
- Lock Verification: fcntl lock prevents race conditions
- Read-only Mounts: Host filesystem protected from worker modifications
Distributed under the GPLv3 License. See LICENSE for full text.
GPLv3 License
Copyright (c) 2025 itachi_re
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
[Full license text in LICENSE file]
- Inspiration: zypperoni by Pavin Joseph - pioneered parallel zypper downloads
- UI Framework: Rich by Will McGugan - beautiful terminal formatting
- Community: openSUSE Contributors - testing, feedback, and repository hosting
- OBS: Open Build Service - automated package building and distribution
- GitHub Issues: Report bugs or request features
- OBS Package: home:itachi_re/zypperx
- Email: itachi_re xanbenson99@gmail.com
- Discussion: Use GitHub Discussions for questions
- Progress persistence across interruptions
- Configurable retry logic for failed downloads
- Delta RPM support
- Mirror health monitoring
- Download scheduling (off-peak hours)
- Bandwidth throttling per worker
- Integration with systemd timers
- Web-based monitoring dashboard
- Stable API for third-party tools
- Plugin system for custom mirrors
- Advanced caching strategies
- Enterprise deployment guides
Made with β€οΈ for the openSUSE Community
Accelerating package management, one parallel download at a time