Repository Change Control
In production environments, updating packages directly from public repositories introduces unpredictable risk. Repository change control uses frozen repo snapshots, local mirrors, and staged testing workflows to ensure update reliability.
Repository Basics
Section titled “Repository Basics”View Currently Enabled Repositories
Section titled “View Currently Enabled Repositories”dnf repolistView All Repositories (Including Disabled)
Section titled “View All Repositories (Including Disabled)”dnf repolist allView Repository Details
Section titled “View Repository Details”dnf repoinfo baseosEnable and Disable Repositories
Section titled “Enable and Disable Repositories”dnf config-manager --disable epeldnf config-manager --enable epelmirrorlist vs baseurl Pinning
Section titled “mirrorlist vs baseurl Pinning”Understanding mirrorlist and baseurl
Section titled “Understanding mirrorlist and baseurl”- mirrorlist — Fetches a list of nearby mirrors from a mirror list service; may point to different servers and content each time
- baseurl — Points to a fixed, specific repository URL, ensuring consistency
Switching from mirrorlist to a Fixed baseurl
Section titled “Switching from mirrorlist to a Fixed baseurl”cat /etc/yum.repos.d/almalinux-baseos.repo# Back up the original configurationcp /etc/yum.repos.d/almalinux-baseos.repo /etc/yum.repos.d/almalinux-baseos.repo.bak
# Comment out mirrorlist and enable baseurlsed -i 's/^mirrorlist=/#mirrorlist=/' /etc/yum.repos.d/almalinux-baseos.reposed -i 's/^# *baseurl=/baseurl=/' /etc/yum.repos.d/almalinux-baseos.repoPinning to a Specific Version (Version Snapshots)
Section titled “Pinning to a Specific Version (Version Snapshots)”AlmaLinux and Rocky Linux vault repositories retain historical version snapshots.
cat > /etc/yum.repos.d/almalinux-baseos-frozen.repo << 'EOF'[baseos-frozen]name=AlmaLinux 9.3 - BaseOS (Frozen)baseurl=https://repo.almalinux.org/vault/9.3/BaseOS/$basearch/os/enabled=1gpgcheck=1gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux-9EOFcat > /etc/yum.repos.d/rocky-baseos-frozen.repo << 'EOF'[baseos-frozen]name=Rocky Linux 9.3 - BaseOS (Frozen)baseurl=https://dl.rockylinux.org/vault/rocky/9.3/BaseOS/$basearch/os/enabled=1gpgcheck=1gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Rocky-9EOFDisable the original repositories:
dnf config-manager --disable baseosdnf config-manager --enable baseos-frozenSetting Up a Local Repository Mirror
Section titled “Setting Up a Local Repository Mirror”A local mirror is the best practice for production repository control.
Install Required Tools
Section titled “Install Required Tools”dnf install -y createrepo_c dnf-plugins-core httpdSync Upstream Repositories Locally
Section titled “Sync Upstream Repositories Locally”mkdir -p /var/www/html/repos/{baseos,appstream,epel}dnf reposync \ --repoid=baseos \ --download-metadata \ --destdir=/var/www/html/repos/baseos \ --newest-onlydnf reposync \ --repoid=appstream \ --download-metadata \ --destdir=/var/www/html/repos/appstream \ --newest-onlyGenerate Repository Metadata
Section titled “Generate Repository Metadata”createrepo_c /var/www/html/repos/baseos/createrepo_c /var/www/html/repos/appstream/Configure the HTTP Service
Section titled “Configure the HTTP Service”cat > /etc/httpd/conf.d/repos.conf << 'EOF'<Directory /var/www/html/repos> Options Indexes FollowSymLinks AllowOverride None Require all granted</Directory>EOF
systemctl enable --now httpdfirewall-cmd --permanent --add-service=httpfirewall-cmd --reloadClient Configuration
Section titled “Client Configuration”cat > /etc/yum.repos.d/local-mirror.repo << 'EOF'[local-baseos]name=Local Mirror - BaseOSbaseurl=http://repo-server.internal/repos/baseos/enabled=1gpgcheck=1gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux-9
[local-appstream]name=Local Mirror - AppStreambaseurl=http://repo-server.internal/repos/appstream/enabled=1gpgcheck=1gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux-9EOFdnf config-manager --disable baseos appstreamScheduled Sync Script
Section titled “Scheduled Sync Script”cat > /usr/local/bin/sync-repos.sh << 'SCRIPT'#!/bin/bashset -e
LOG="/var/log/repo-sync.log"REPO_BASE="/var/www/html/repos"DATE=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$DATE] Starting repository sync" >> "$LOG"
for repo in baseos appstream; do echo "[$DATE] Syncing $repo ..." >> "$LOG" dnf reposync \ --repoid="$repo" \ --download-metadata \ --destdir="$REPO_BASE/$repo" \ --newest-only \ --delete >> "$LOG" 2>&1
createrepo_c --update "$REPO_BASE/$repo/" >> "$LOG" 2>&1done
echo "[$DATE] Repository sync completed" >> "$LOG"SCRIPT
chmod +x /usr/local/bin/sync-repos.shcat > /etc/cron.d/repo-sync << 'EOF'0 3 * * 0 root /usr/local/bin/sync-repos.shEOFStaged Update Testing
Section titled “Staged Update Testing”Environment Layering Strategy
Section titled “Environment Layering Strategy”Recommended update promotion path:
Development (Dev) -> Staging -> Pre-production -> ProductionEach layer uses a repository snapshot from a different point in time.
Creating Versioned Repository Snapshots
Section titled “Creating Versioned Repository Snapshots”SNAPSHOT_DATE=$(date +%Y%m%d)SNAPSHOT_DIR="/var/www/html/repos/snapshots/$SNAPSHOT_DATE"
mkdir -p "$SNAPSHOT_DIR"
# Use hard links to create the snapshot (saves disk space)cp -al /var/www/html/repos/baseos/ "$SNAPSHOT_DIR/baseos"cp -al /var/www/html/repos/appstream/ "$SNAPSHOT_DIR/appstream"
echo "Snapshot created: $SNAPSHOT_DIR"Assigning Repository Snapshots to Environments
Section titled “Assigning Repository Snapshots to Environments”# Dev: baseurl=http://repo-server.internal/repos/baseos/cat > /etc/yum.repos.d/production.repo << 'EOF'[prod-baseos]name=Production BaseOS (Snapshot 20260315)baseurl=http://repo-server.internal/repos/snapshots/20260315/baseos/enabled=1gpgcheck=1gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux-9EOFChange Management Workflow
Section titled “Change Management Workflow”Standard Change Process
Section titled “Standard Change Process”- Discover — Monitor upstream security advisories and updates
- Assess — Analyze the impact scope and risk of each update
- Test — Validate in development/staging environments
- Approve — Go through the change approval process
- Execute — Push to production within a maintenance window
- Verify — Confirm services and functionality are intact
- Document — Record the change details and results
Generate an Update Report
Section titled “Generate an Update Report”#!/bin/bashecho "========================================="echo " System Update Assessment Report"echo " Host: $(hostname)"echo " Date: $(date)"echo "========================================="echo ""
echo "--- Current System ---"cat /etc/redhat-releaseecho "Kernel: $(uname -r)"echo ""
echo "--- Available Updates ---"dnf check-update 2>/dev/null || trueecho ""
echo "--- Security Update Summary ---"dnf updateinfo summary 2>/dev/nullecho ""
echo "--- Security Advisory Details ---"dnf updateinfo list security 2>/dev/nullRecord Change Operations
Section titled “Record Change Operations”#!/bin/bashCHANGE_LOG="/var/log/change-management.log"TICKET_ID="${1:-UNTRACKED}"
echo "==============================" >> "$CHANGE_LOG"echo "Change ticket: $TICKET_ID" >> "$CHANGE_LOG"echo "Operator: $(whoami)" >> "$CHANGE_LOG"echo "Timestamp: $(date)" >> "$CHANGE_LOG"echo "Pre-update package state:" >> "$CHANGE_LOG"rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | sort >> "$CHANGE_LOG"echo "==============================" >> "$CHANGE_LOG"
# Perform the updatednf update -y 2>&1 | tee -a "$CHANGE_LOG"
echo "Post-update package state:" >> "$CHANGE_LOG"rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | sort >> "$CHANGE_LOG"Track Changes with dnf history
Section titled “Track Changes with dnf history”dnf history listdnf history info <transaction-ID> > /var/log/change-$(date +%Y%m%d)-txn<transaction-ID>.logGPG Key Management
Section titled “GPG Key Management”View Imported GPG Keys
Section titled “View Imported GPG Keys”rpm -qa gpg-pubkey* --qf '%{NAME}-%{VERSION}-%{RELEASE}\t%{SUMMARY}\n'Import a New GPG Key
Section titled “Import a New GPG Key”rpm --import https://example.com/RPM-GPG-KEY-exampleVerify Package Signatures
Section titled “Verify Package Signatures”rpm -K <package-file>.rpmrpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}\t%{SIGPGP:pgpsig}\n' | head -20