Migrating from CentOS 7 to CentOS 8
CentOS 7 has years of maintenance left, but I'm tired of Python 2.7 as the system interpreter, PHP 5.4 and MariaDB 5.5 in base, and a 3.10 kernel. CentOS 8 gives the RHEL 8 userland: dnf, AppStream modules for several language and database versions, a 4.18 kernel, and nftables behind firewalld. There's no supported in-place upgrade from 7 to 8, so it's a fresh install and moving services across.
Here's the procedure I use for a typical web or database host: inventory the old server, build the new one, translate what changed, cut over. It's conservative on purpose. A parallel build lets the old host keep running until the new one has earned trust. You need a second machine or VM for CentOS 8.2 (or willingness to reinstall the existing host after full backups), and backups of data and config that you've restored at least once. An untested backup isn't a backup.
Inventory first
Before touching anything, record what's actually running and installed:
rpm -qa --qf '%{NAME}\n' | sort > packages-c7.txt
systemctl list-unit-files --state=enabled --no-legend | awk '{print $1}' > enabled-units-c7.txt
ss -tlnp > listening-c7.txt
sudo firewall-cmd --list-all-zones > firewall-c7.txt
sudo semanage export > selinux-c7.txt
sudo crontab -l > root-crontab-c7.txt; ls /etc/cron.d
tar czf etc-c7.tar.gz /etc
semanage export is the line people forget. Every custom port and file-context rule you added over the years is in there. Copy all of these files to the new host or somewhere safe.
Install CentOS 8
Install minimal, then:
sudo dnf -y update
sudo dnf -y install epel-release
sudo dnf -y install policycoreutils-python-utils setroubleshoot-server tar vim-enhanced
cat /etc/centos-release
policycoreutils-python is now policycoreutils-python-utils. yum still exists as a symlink to dnf and takes the same arguments, so scripts don't break, but I use dnf for anything new.
Translating the package list
Many packages were renamed or moved into modules. See what the new host offers:
dnf module list
dnf module list php mariadb mysql nodejs python36 python38
Where CentOS 7 needed Remi or SCL for a current PHP or MariaDB, CentOS 8 has modules. Enable the streams before installing:
sudo dnf -y module enable php:7.4
sudo dnf -y module enable mariadb:10.3
sudo dnf -y install php php-fpm php-mysqlnd mariadb-server nginx
Other changes to watch for: python isn't installed by default (use python3 or dnf install python2 explicitly; scripts with #!/usr/bin/python will fail), ntp is gone (chrony only), iptables-services is replaced by nftables under firewalld, and network-scripts (the ifcfg files and network service) is deprecated in favour of NetworkManager, although dnf install network-scripts brings it back if you must.
Diff the saved package list against what's available and note the gaps:
comm -23 <(sort packages-c7.txt) <(dnf list --available -q | awk '{print $1}' | cut -d. -f1 | sort -u) | head -50
Config and data
Copy configuration selectively. Never untar the whole /etc over the new system; I've watched that go badly. For each service, diff the old file against the CentOS 8 default and merge:
diff /etc/nginx/nginx.conf /path/to/etc-c7/nginx/nginx.conf
For MariaDB, do a logical dump and restore rather than copying data files across major versions:
# on CentOS 7
mysqldump --all-databases --single-transaction --routines --events --triggers | gzip > all-dbs.sql.gz
# on CentOS 8
sudo systemctl enable --now mariadb
zcat all-dbs.sql.gz | mysql
sudo mysql_upgrade
Re-apply SELinux customisations from the export:
sudo semanage import -f selinux-c7.txt
sudo restorecon -Rv /srv /var/www
Firewall rules are the same firewalld commands as before; only the backend changed to nftables:
sudo firewall-cmd --permanent --add-service={http,https}
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
Docker
CentOS 8 ships Podman and Buildah instead of Docker, and dnf install docker gives you a podman-docker shim. If you have docker-compose files and a team used to Docker, install Docker CE from Docker's repository. With the 19.03 packages, containerd.io conflicts with runc from the container-tools module, so disable that first:
sudo dnf -y module disable container-tools
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf -y install docker-ce docker-ce-cli containerd.io
sudo systemctl enable --now docker
With firewalld on nftables, container traffic is blocked by default; add the docker0 interface to the trusted zone as on CentOS 7.
Cutting over
Lower the DNS TTL for the service names a day ahead. Bring the new host up on a temporary IP, test with /etc/hosts overrides from a workstation, then swap the DNS records or move the IP to the new machine. I keep the old host powered on with services stopped for a week before reinstalling or retiring it, because someone always remembers a forgotten cron job.
Compare against the inventory
On the new host:
systemctl list-unit-files --state=enabled --no-legend | awk '{print $1}' | diff - enabled-units-c7.txt
ss -tlnp
sudo firewall-cmd --list-all
getenforce; sudo ausearch -m avc -ts recent | wc -l
php -v; mysql --version; nginx -v
Each enabled service on the old host should be enabled on the new one or consciously dropped. Run the application's own smoke tests through the real hostname.
Snags
Error: Problems in request: missing groups or modules: php:7.4orModular dependency problems: another stream of the same module is already enabled.dnf module reset php, then enable the one you want./usr/bin/python: No such file or directoryfrom old scripts: installpython2and runalternatives --set python /usr/bin/python2, or better, port to Python 3.6, the platform default.dnf install docker-cefails withpackage containerd.io ... conflicts with runc provided by runc-...: container-tools is still enabled; disable it as above.- No
networkservice and noifup: installnetwork-scriptsfor the old behaviour or convert tonmcli connectionprofiles. - MariaDB won't start after copying
/var/lib/mysqlfrom the old host (Unknown/unsupported storage engine: InnoDBor version errors): use the dump-and-restore path above, not the data files.
A parallel rebuild takes longer than an in-place upgrade would. What you get is a clean host without seven years of accumulated packages and hand edits. Keep the inventory files; they're the checklist for the next migration too.