Migrating from CentOS 8 to AlmaLinux 9: Comparing 3 Methods with Step-by-Step Instructions

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

CentOS 8 officially reached EOL on December 31, 2021 — yet I still come across production servers running CentOS 8 to this day. When the official EOL announcement dropped, I had to migrate 5 servers in a single week. During that process I tried both Rocky Linux and AlmaLinux, and ultimately went with AlmaLinux for most of the servers — for reasons I’ll explain below.

This article gets straight to the technical side: safely migrating from CentOS 8 to AlmaLinux 9 without losing data or enduring excessive downtime. This is not a guide for a clean install from scratch.

3 Migration Methods — The Big Picture

At a high level, there are 3 paths to migrate:

Method 1: Fresh Install + Data Migration

Install a fresh AlmaLinux 9 on a separate server (or new VM), then transfer data, configs, and services from the CentOS 8 server.

Method 2: In-place Migration (same major version)

Convert CentOS 8 → AlmaLinux 8 on the running server using AlmaLinux’s official migration script, then upgrade AlmaLinux 8 → AlmaLinux 9 using ELevate.

Method 3: Direct Upgrade via ELevate

Use the ELevate tool (a wrapper around Red Hat’s Leapp) to perform a major version upgrade in place. AlmaLinux 8 is still required as an intermediate stop, but the process is more automated.

Analyzing the Trade-offs of Each Method

Fresh Install — Clean, but Budget a Full Day

  • Pros: Completely clean environment, no leftover packages from CentOS 8, easier to troubleshoot down the line.
  • Cons: You have to reinstall and reconfigure everything from scratch. A server running a LAMP stack with dozens of custom configs can easily eat up 1–2 full days.
  • Best when: The server is simple (1–2 services), or you already have Ansible/Terraform to rebuild the infrastructure in minutes.

In-place Migration — Least Downtime, Production-Friendly

  • Pros: Preserves packages, configs, and data. Actual downtime is only around 10–20 minutes per reboot.
  • Cons: Risk of package conflicts, especially if the server has many third-party repos (EPEL, Remi, vendor-specific repos).
  • Best when: Complex production servers with lots of custom config that you don’t want to rebuild from scratch.

ELevate — Fastest Path to v9

  • Pros: Official tool with a detailed pre-upgrade report, well-supported by the community.
  • Cons: Some older packages don’t have EL9-compatible versions and you’ll need to handle inhibitors manually before running.
  • Best when: The server doesn’t have too many custom packages and you need to reach AlmaLinux 9 quickly.

The Optimal Choice for Production Servers

After trying all 3 approaches, I found the 2-step process (CentOS 8 → AlmaLinux 8 → AlmaLinux 9) to be the most reliable:

  1. Step 1 (CentOS 8 → AlmaLinux 8) is very stable since it’s the same major version with no significant breaking changes.
  2. Step 2 (AlmaLinux 8 → AlmaLinux 9 via ELevate) is well-documented and the tooling has been thoroughly tested.

As for why AlmaLinux over Rocky Linux: AlmaLinux typically releases new versions within 1–3 days of a RHEL release, while Rocky Linux takes around 1–2 weeks. For production servers that need security patches fast, that gap matters more than most people realize.

Practical Walkthrough: CentOS 8 → AlmaLinux 9

Step 0: Backup — Non-Negotiable

Back up first, figure out everything else after:

# Back up /etc (all config files)
tar -czf /backup/etc-backup-$(date +%Y%m%d).tar.gz /etc/

# Dump the database if applicable
mysqldump -u root -p --all-databases > /backup/all-db-$(date +%Y%m%d).sql

# Record the list of currently installed packages
rpm -qa > /backup/packages-$(date +%Y%m%d).txt

# If this is a VM: take a snapshot right now

Step 1: Prepare the CentOS 8 Server

# Update all packages first
dnf update -y

# Check the current version
cat /etc/centos-release

# Temporarily disable SELinux (re-enable after migration)
setenforce 0
sed -i 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config

Step 2: Migrate CentOS 8 → AlmaLinux 8

# Download AlmaLinux's official migration script
curl -O https://raw.githubusercontent.com/AlmaLinux/almalinux-deploy/master/almalinux-deploy.sh

# Verify the checksum (don't skip this)
sha256sum almalinux-deploy.sh

# Run the script (requires root)
chmod +x almalinux-deploy.sh
sudo bash almalinux-deploy.sh

# The script automatically:
# - Replaces CentOS repos with AlmaLinux repos
# - Swaps core packages (centos-release, gpg-keys, etc.)
# - Does NOT delete your data or config files

The script completes in about 5–10 minutes. Reboot and verify:

reboot

# After rebooting
cat /etc/almalinux-release
# AlmaLinux release 8.x (...)

# Confirm AlmaLinux packages are installed
rpm -qa | grep almalinux

Step 3: Upgrade AlmaLinux 8 → AlmaLinux 9 with ELevate

# Install the ELevate repo
sudo yum install -y https://repo.almalinux.org/elevate/elevate-release-latest-el8.noarch.rpm

# Install leapp-upgrade and AlmaLinux data files
sudo yum install -y leapp-upgrade leapp-data-almalinux

# Run the pre-upgrade check BEFORE doing anything else
sudo leapp preupgrade

# Read the report carefully — this step determines success or failure
cat /var/log/leapp/leapp-report.txt

The report surfaces two types of issues: inhibitors (block the upgrade entirely and must be resolved) and warnings (advisory notices that may be acceptable depending on the situation). You must clear all inhibitors before proceeding. Some common ones:

# Inhibitor: packages with no EL9 version available
# Fix: remove the package or find a replacement
dnf remove <package-name>

# Inhibitor: GRUB config out of sync
grub2-mkconfig -o /boot/grub2/grub.cfg

# Inhibitor: pam_pkcs11 module
leapp answer --section remove_pam_pkcs11_module_check.confirm=True

# Inhibitor: VDO devices (if not in use)
leapp answer --section check_vdo.confirm=True

Once all inhibitors are resolved, run the actual upgrade:

# Run the upgrade
sudo leapp upgrade

# Reboot into the upgrade environment
# The upgrade process takes roughly 20–40 minutes depending on the server
reboot

Step 4: Post-Migration Verification

# Confirm AlmaLinux 9 version
cat /etc/almalinux-release
# AlmaLinux release 9.x (Seafoam Ocelot)

uname -r
# New EL9 kernel

# Check critical services
systemctl status nginx
systemctl status mysqld
systemctl status sshd

# Find packages not belonging to any repo (review these)
dnf list extras

Re-enable SELinux

# Relabel the entire filesystem (required after a major version upgrade)
touch /.autorelabel
reboot

# After reboot, switch back to enforcing mode
sed -i 's/SELINUX=permissive/SELINUX=enforcing/' /etc/selinux/config
reboot

Real Issues Encountered Across 5 Server Migrations

  • PHP version: EL9 ships with PHP 8.1 by default. Apps requiring PHP 7.4 or 8.0 need the Remi repo added — budget an extra 30 minutes for setup.
  • MySQL 5.7: EL9 does not include MySQL 5.7 in its official repos. Add the official MySQL repo or upgrade to MySQL 8.0 before migrating.
  • Python 2: EL9 drops Python 2 entirely. If your codebase still uses Python 2, port it to Python 3 beforehand — don’t discover this at the last minute.
  • Crontabs: Check /etc/crontab and /var/spool/cron/ immediately after migration. I once lost an automated backup cron job after an upgrade and only noticed 3 days later.
  • Custom kernel modules: Modules compiled against the old kernel will not work with the EL9 kernel. Recompiling from source is mandatory.

Post-Migration Checklist

# Post-migration checklist — check off each item
# [ ] cat /etc/almalinux-release → correct version 9.x
# [ ] uname -r → EL9 kernel
# [ ] All critical services running
# [ ] dnf update -y → no errors
# [ ] SELinux enforcing (getenforce)
# [ ] Firewall rules intact (firewall-cmd --list-all)
# [ ] Application working normally (web, DB, API)
# [ ] No unexpected errors in logs (/var/log/messages)
# [ ] All required cron jobs present
# [ ] Backup updated after migration

Migrating CentOS 8 → AlmaLinux 9 isn’t complicated, but it’s easy to stumble if you skip leapp preupgrade. Spend an extra 30 minutes reading the report carefully and resolving each inhibitor — it’s the best investment you can make in the entire process. When the upgrade finishes cleanly with no errors, the relief you feel is very real.

Share: