WARNING! This script is NOT officially provided by Veeam Software Corporation or is supported by Veeam in any way.
This project automates the setup of a local Veeam VSA (Software Appliance) package mirror on RHEL-family Linux systems. The setup script (vsa_repo.sh) handles disk/filesystem prep, package management, nginx publishing, optional HTTPS, and systemd automation.
- Setup script (
vsa_repo.sh): Orchestrates the entire mirror infrastructure and runtime prompts - Reposync script (generated at
/usr/local/sbin/veeam-vsa-reposync.sh): Dynamically configured with parameters from the setup script; runs on-demand or via the systemd timer - Nginx (HTTP + optional HTTPS): Publishes the local mirror, redirects HTTP to HTTPS when certs are enabled, and exposes ACME challenges
- XFS filesystem: Optional - will partition and mount an empty dedicated data disk (by default
/dev/sdb->/mnt/data) for repository storage (~30GB initially required)
- Prerequisite checks: Verify root privileges, OS compatibility (RHEL/Rocky/Alma/CentOS 9+), and dnf availability.
- Cleanup path: When selected, remove all configs, services, timers, SELinux labels, and firewall rules created by earlier runs while leaving mirrored data intact.
- Disk setup: Partition and format
DATA_DEVICEas XFS when approved; otherwise only ensureMOUNT_POINTexists. Includes partition table synchronization with retry logic. - Package installation: Install required packages (dnf-plugins-core, curl, nginx, SELinux tools, firewalld, gnupg2). Certbot components install only if HTTPS is chosen.
- Dynamic GPG key validation: Fetch all available signing keys from
https://repository.veeam.com/keys/, validate each viarpm --import, and fail if no valid keys are found. - Upstream repo config: Create
/etc/yum.repos.d/veeam-vsa-upstream.repowith the three repo IDs and validated GPG key URLs. - Reposync script generation: Generate
/usr/local/sbin/veeam-vsa-reposync.shwith injected configuration variables (REPO_ROOT, upstream URLs, metadata signature verification logic). - Network/security: Configure nginx (HTTP baseline, HTTPS redirect when enabled), SELinux contexts (
httpd_sys_content_t), and firewalld rules (HTTP always, HTTPS when enabled). - HTTPS provisioning: Validate DNS for the supplied FQDN, request/renew Let's Encrypt certificates via webroot, and reload nginx with TLS settings (will reuse if existing certificates are accessible on the host)
- Service automation: Create the systemd service and hourly timer that run the reposync script with automated verification.
- Disk space and connectivity checks: Warn if free space is low and curl the published URLs (HTTP plus HTTPS when active).
- Full cleanup: Removes configs, scripts, services, timers, SELinux labels, and firewall rules created by this script. Does not delete repository data, installed packages, or disk partitions.
- Disk partitioning: Controls whether the script partitions
DATA_DEVICEand writes/etc/fstab. Pick "no" when reusing an existing path or custom storage. - HTTPS enable: Adds Let's Encrypt provisioning, port 443 listener, and HTTPS firewall rules. Requires a public FQDN that resolves to
REPO_HOST_IP.
Answer "no" to skip any workflow while continuing with the rest of the setup.
/mnt/data/repo/repository.veeam.com/
├── vsa/9.2/vbr/13.0/mandatory/
├── vsa/9.2/vbr/13.0/optional/
└── vsa/9.2/external-mandatory/
This mirrors the upstream path structure exactly. Config variables define OS_VERSION (9.2) and VBR_VERSION (13.0).
- Mirror host:
--nogpgcheckenabled indnf reposyncbecauseexternal-mandatorycontains packages from multiple vendors (Rocky, CIQ, PGDG, etc.) - VSA clients: Still perform full GPG verification when consuming this mirror
- Keys: Dynamically fetched and validated from
https://repository.veeam.com/keys/during setup- All non-DEB keys are downloaded and validated via
rpm --import - Invalid or failed keys are skipped with warnings
- At least one valid key must be imported or setup fails
- All non-DEB keys are downloaded and validated via
After each sync, the reposync script performs metadata signature verification:
-
Metadata Signature Verification
- Downloads
repomd.xml.ascandrepomd.xml.keyfrom upstream - Imports the signing key into a dedicated GPG keyring
- Verifies GPG signature on
repomd.xmlusing imported Veeam keys - Uses
gpg --batch --no-ttyto prevent hanging in automated runs - Filters output to show only critical verification messages
- Downloads
-
Automatic Corruption Recovery
- dnf reposync built-in: Automatically detects and re-downloads corrupted or incomplete packages
- Metadata verification failure: Takes mirror offline (stops nginx) if signature verification fails
- Manual investigation: Required if persistent metadata corruption detected
Edit the top section (lines 28-61) to customize:
DATA_DEVICE: Physical disk (default/dev/sdb)DATA_PARTITION: Resulting partition (default/dev/sdb1)MOUNT_POINT: Where to mount filesystem (default/mnt/data)REPO_ROOT: Full path to repo root (derived from MOUNT_POINT)OS_VERSION,VBR_VERSION: Repo versions (currently 9.2 / 13.0)REPO_HOSTNAME_FQDN,REPO_HOST_IP: Nginx serving addressesLE_EMAIL: Email address for Let's Encrypt certificate expiry notifications
Upstream URLs are derived from OS/VBR versions and injected into the generated reposync script, so changing these auto-updates all upstream paths without regenerating the script.
- Certificates are requested with
certbot certonly --webrootusing${LE_WEBROOT}and are stored under/etc/letsencrypt/live/<FQDN>/. - Let's Encrypt sends expiry notifications to the configured
LE_EMAILaddress. - Nginx always listens on port 80. When HTTPS is enabled it redirects traffic to port 443 and reuses the same repo root.
- Firewalld opens the HTTPS service automatically when TLS is in use.
- Existing certificates are reused when the script is rerun with the same FQDN.
# Check timer status and next run time
systemctl status veeam-vsa-reposync.timer
systemctl list-timers veeam-vsa-reposync.timer
# View recent sync logs (includes integrity verification results)
journalctl -u veeam-vsa-reposync.service -n 100
# Watch live sync progress
journalctl -u veeam-vsa-reposync.service -f# Manual verification run
sudo /usr/local/sbin/veeam-vsa-reposync.sh
# Check verification output (simplified in v3)
# Look for:
# ✓ SUCCESS: Metadata signature verified for <repo>
# ✓ ALL REPOSITORY METADATA SIGNATURES VERIFIED SUCCESSFULLYcurl -I http://veeamrepo/vsa/9.2/vbr/13.0/mandatory/
curl -I http://192.168.1.54/vsa/9.2/vbr/13.0/optional/
curl -I http://veeamrepo.test.local/vsa/9.2/external-mandatory/
curl -I https://veeamrepo.test.local/vsa/9.2/vbr/13.0/mandatory/ # only when HTTPS is enabled- Clients: Point VSA installations to
http://<REPO_HOST_IP>/vsa/...(orhttps://<FQDN>/vsa/...when TLS is enabled)
The default sync schedule is hourly To change the sync interval, edit the timer file:
/etc/systemd/system/veeam-vsa-reposync.timerModify the OnCalendar= line. Examples:
# Every hour (default)
OnCalendar=hourly
# Every 6 hours
OnCalendar=*-*-* 00/6:00:00
# Daily at 3 AM
OnCalendar=*-*-* 03:00:00Then reload and restart the timer:
sudo systemctl daemon-reload
sudo systemctl restart veeam-vsa-reposync.timer
# Verify next scheduled run
systemctl list-timers | grep veeamThe reposync script logs all activity to a persistent log file with automatic rotation Each sync tracks and logs package changes per repository
- Log file location:
/var/log/veeam-vsa-reposync.log - Dual output: All messages go to both stderr (for journalctl) and the log file
- Added packages: New RPMs downloaded during sync
- Removed packages: Old RPMs deleted by
--deleteflag
Logrotate configuration is automatically created at /etc/logrotate.d/veeam-vsa-reposync:
- Rotation: Monthly
- Retention: 3 months (rotate 3)
- Compression: Enabled with delayed compression
- Permissions: 0640 root:root
# Test logrotate configuration (dry-run / debug mode)
logrotate -d /etc/logrotate.d/veeam-vsa-reposync
# Force immediate rotation
logrotate -f /etc/logrotate.d/veeam-vsa-reposync
# Search for package changes
grep -E '\[Added\]|\[Removed\]' /var/log/veeam-vsa-reposync.logWe welcome contributions from the community! We encourage you to create issues for Bugs & Feature Requests and submit Pull Requests. For more detailed information, refer to our Contributing Guide.
If you have any questions or something is unclear, please don't hesitate to create an issue and let us know!