๐ŸŽ New User? Get 20% off your first purchase with code NEWUSER20 ยท โšก Instant download ยท ๐Ÿ”’ Secure checkout Register Now โ†’
Menu

Categories

Migrating Between Linux Package Managers: DNF to APT

Migrating Between Linux Package Managers: DNF to APT

You're migrating from Rocky Linux to Ubuntu LTS or consolidating a fleet of mixed Fedora and Debian servers. The OS installation is straightforward; the real challenge is translating 300 installed packages, a dozen custom repositories, and two years of automation scripts that all assume DNF. When you migrate from DNF to APT, there's no one-click converter that preserves your package inventory, configuration management hooks, and deployment scripts. You need a methodical workflow that maps package names, translates repository configs, and converts automation without breaking production.

DNF and APT solve the same dependency resolution and package management problems with different tooling, metadata formats, and naming conventions. The core issue is not the package manager commands; those translate easily. It is the package names themselves: httpd becomes apache2, cronie becomes cron, and some RHEL-specific packages have no Debian equivalent at all. This guide walks through inventorying your DNF-based system, mapping packages to APT equivalents, and converting your automation so the migration does not break existing workflows.

Why DNF and APT Differ and What That Means for Migration

DNF uses RPM packages built from .spec files; APT uses DEB packages built from debian/control metadata. An RPM contains files, scripts, and dependency declarations in a cpio archive with an RPM header; a DEB is an ar archive with control metadata and data tarballs. Dependency resolution differs under the hood: DNF uses libsolv (a SAT solver), while APT uses its own resolver that prioritizes package versions and pinning. Repository metadata is incompatible: DNF reads repomd.xml and sqlite databases, APT reads Packages.gz and Release files. You cannot point DNF at a Debian repo or vice versa.

The core package manager commands translate directly, but the syntax and behavior differ in details:

Task DNF Command APT Command
Install a package dnf install httpd apt install apache2
Search for a package dnf search keyword apt search keyword
List installed packages dnf list installed apt list --installed
Show package info dnf info package apt show package
View transaction history dnf history cat /var/log/apt/history.log
Clean cache dnf clean all apt clean

Package names rarely match between distributions. RHEL and Fedora use httpd for Apache, Debian and Ubuntu use apache2. Fedora's cronie is Debian's cron. Some packages split: a single RPM might become three DEBs (httpd becomes apache2, apache2-utils, and apache2-doc), or merge the other way. Migrations happen for vendor lock-in reduction, better upstream support for a given distro, or infrastructure consolidation after acquisitions. The package name mapping is the hardest part.

Inventorying Your DNF-Based System

Listing Installed Packages and Their Sources

Start by exporting every installed package with its source repository. This tells you which packages came from base repos, EPEL, or third-party sources:

dnf repoquery --installed --qf '%{name} %{reponame}' > installed-packages.txt

A typical Rocky Linux 9 system produces output like:

bash @System
systemd @System
httpd epel
nginx nginx-stable
postgresql15-server pgdg15

Packages marked @System were installed before repo metadata was available (often during initial install). Packages from epel, pgdg15, or nginx-stable need custom repo mapping. For a simple name-only list with versions:

rpm -qa --qf '%{NAME} %{VERSION}\n' > package-versions.txt

Check the DNF history to see what was explicitly installed versus pulled in as dependencies:

dnf history userinstalled > user-installed.txt

This lists only packages you or a script installed directly. Dependencies are omitted, so your APT migration can focus on the packages that matter.

Extracting Custom Repositories and Third-Party RPMs

List enabled repositories with full URLs:

dnf repolist -v | grep -E '(Repo-id|Repo-baseurl)'

For each third-party repo (EPEL, PostgreSQL, Nginx, Docker), note the URL and GPG key. You'll need to find the Debian equivalent: EPEL has no direct counterpart, but many EPEL packages exist in Debian's main or backports repos. PostgreSQL, Nginx, and Docker all publish official Debian/Ubuntu repos at the same upstream sources.

If you installed RPMs directly with rpm -i or dnf install ./package.rpm, find them:

dnf repoquery --installed --qf '%{name} %{vendor}' | grep -v 'Rocky\|Fedora\|Red Hat'

Packages from third-party vendors need manual conversion or sourcing from a Debian repo.

Documenting Package Groups and Weak Dependencies

DNF groups bundle related packages. List installed groups:

dnf group list --installed

Common groups like "Development Tools" or "System Tools" have rough Debian equivalents in metapackages like build-essential or task selections during install. Expand a group to see its contents:

dnf group info "Development Tools"

DNF supports weak dependencies (Recommends and Suggests). List them for a given package:

dnf repoquery --recommends httpd

APT also supports Recommends (installed by default) and Suggests (not installed). Check the Debian package management reference for how APT handles these. If you disabled weak deps on DNF with install_weak_deps=False in /etc/dnf/dnf.conf, replicate that in APT with APT::Install-Recommends "false"; in /etc/apt/apt.conf.d/.

Mapping DNF Package Names to APT Equivalents

Common Name Differences Across Distros

Most frustration comes from package name mismatches. Here are the frequent ones you'll hit:

Fedora/RHEL Package (DNF) Debian/Ubuntu Package (APT) Notes
httpd apache2 Apache web server
httpd-tools apache2-utils Utilities like ab, htpasswd
mariadb-server mariadb-server Same name
NetworkManager network-manager Lowercase, hyphenated
cronie cron Cron daemon
policycoreutils none SELinux tools; not needed on Debian (uses AppArmor)
firewalld ufw or nftables Debian prefers ufw or direct nftables
bind bind9 DNS server
mod_ssl apache2 (built-in module) Enable with a2enmod ssl

For packages specific to RHEL ecosystems (SELinux policy packages, redhat-lsb-core, or subscription-manager) there is no Debian equivalent. Remove them from your migration list or replace their functionality (SELinux with AppArmor, subscription-manager has no counterpart).

Using Package Search and Content Lookup

When a package name doesn't translate obviously, search by the binary or library it provides. On DNF:

dnf provides /usr/bin/htpasswd

Output: httpd-tools-2.4.57-5.el9.x86_64. On Debian, install apt-file first:

apt install apt-file
apt-file update
apt-file search /usr/bin/htpasswd

Output: apache2-utils: /usr/bin/htpasswd. For libraries, the same works:

apt-file search libssl.so.3

If you don't have a Debian system running yet, use packages.debian.org (search by file path in the "Search the contents of packages" box) or pkgs.org (cross-distro package search). These show package contents, dependencies, and available versions across Debian and Ubuntu releases.

Handling Split and Merged Packages

RHEL often bundles utilities into a single RPM; Debian splits them. The httpd RPM includes the server binary, apachectl, and utilities like htpasswd. On Debian, apache2 is the server, apache2-utils has the utilities, and apache2-doc has documentation. Your migration script must install all three if your automation depends on htpasswd.

Conversely, some Debian packages merge what RHEL splits. RHEL's python3 and python3-pip are separate; Debian's python3 often recommends python3-pip, installing it by default unless you disabled recommends. Check with:

apt show python3 | grep Recommends

For each package in your inventory, verify the mapping on a test Debian system with apt search and apt show, then document split/merge cases in a mapping file for your automation scripts to consume.

Translating Custom Repositories

From .repo Files to sources.list Entries

DNF repositories live in /etc/yum.repos.d/*.repo files with INI-style syntax. APT uses /etc/apt/sources.list and /etc/apt/sources.list.d/*.list with a one-line-per-repo format. A typical DNF repo file looks like:

[nginx-stable]
name=nginx stable repo
baseurl=https://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key

The equivalent APT entry in /etc/apt/sources.list.d/nginx.list:

deb [arch=amd64 signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://nginx.org/packages/ubuntu/ jammy nginx

Key differences: DNF's baseurl becomes APT's deb line with URI, distribution codename (jammy for Ubuntu 22.04, bookworm for Debian 12), and component (nginx). The $releasever and $basearch variables don't exist in APT; you hardcode the distro name and architecture. The gpgkey URL must be downloaded and placed in /usr/share/keyrings/, then referenced with signed-by. Download and convert the key:

curl -fsSL https://nginx.org/keys/nginx_signing.key | gpg --dearmor -o /usr/share/keyrings/nginx-archive-keyring.gpg

List all DNF repo URLs to map them:

dnf config-manager --dump | grep -E '^(name|baseurl|gpgkey)'

For each third-party repo, check if the vendor publishes a Debian version. Docker, PostgreSQL, Nginx, and Node.js all maintain both RPM and DEB repos at the same upstream domain. Others don't: you'll need to find Debian alternatives or build from source.

Third-Party Repos: EPEL, RPM Fusion, and Their Debian Analogs

EPEL (Extra Packages for Enterprise Linux) has no direct Debian equivalent. Many EPEL packages already exist in Debian's main, contrib, or non-free repositories, or in Ubuntu's universe and multiverse. For example, EPEL's htop, nginx, and certbot are all in Debian main. Check with:

apt search htop
apt policy htop

If a package isn't in the default repos, Debian backports (deb http://deb.debian.org/debian bookworm-backports main) often has newer versions of software from testing. Ubuntu users can add PPAs (Personal Package Archives) with add-apt-repository, though PPAs are less vetted than official repos. For a PPA:

add-apt-repository ppa:ondrej/php
apt update

RPM Fusion (multimedia codecs, proprietary drivers) maps loosely to Debian's non-free and contrib repos, which you enable by editing /etc/apt/sources.list to include main contrib non-free non-free-firmware. Ubuntu's multiverse serves a similar role. The PostgreSQL Global Development Group (PGDG) maintains both RPM and DEB repos; convert Rocky's pgdg15 repo to Debian by following the official PostgreSQL APT instructions. Docker's official repo on RHEL (download.docker.com/linux/centos/) has a Debian counterpart at download.docker.com/linux/debian/ or download.docker.com/linux/ubuntu/. Add it with:

curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian bookworm stable" > /etc/apt/sources.list.d/docker.list
apt update

Document every custom repo in your migration plan. If a repo has no Debian equivalent, decide whether to build from source, use a Debian alternative, or skip the package entirely.

Converting Automation Scripts and Configuration Management

Shell Scripts and dnf Commands

A typical deployment script on Rocky Linux:

#!/bin/bash
dnf install -y httpd mod_ssl
systemctl enable --now httpd
firewall-cmd --add-service=http --permanent
firewall-cmd --reload

The Debian equivalent:

#!/bin/bash
apt-get install -y apache2
systemctl enable --now apache2
ufw allow 'Apache Full'

The -y flag (assume yes) works the same. DNF's --allowerasing (allow removing conflicting packages) becomes --allow-downgrades or --allow-remove-essential in APT, though you rarely need these. For non-interactive installs, prefix with DEBIAN_FRONTEND=noninteractive to suppress prompts:

DEBIAN_FRONTEND=noninteractive apt-get install -y apache2

Multi-package installs translate directly, but remember package name changes: dnf install -y httpd mariadb-server php php-mysqlnd becomes apt-get install -y apache2 mariadb-server php php-mysql. Keep a mapping file (package-map.txt) with one line per package: httpd=apache2, php-mysqlnd=php-mysql. Your script can read this file and substitute names before calling apt-get.

Ansible Playbooks: dnf vs apt Module

An Ansible task using the dnf module:

- name: Install web server
  dnf:
    name:
      - httpd
      - mod_ssl
    state: present
    update_cache: yes

The apt module equivalent:

- name: Install web server
  apt:
    name:
      - apache2
    state: present
    update_cache: yes

Both modules support state: present, state: latest, and update_cache (equivalent to dnf makecache or apt update). The apt module has cache_valid_time to avoid updating the cache on every run:

- name: Install web server
  apt:
    name: apache2
    state: present
    update_cache: yes
    cache_valid_time: 3600

This updates the cache only if it's older than 3600 seconds. For package removal, both use state: absent. The Ansible package module is distribution-agnostic, but it requires identical package names across distros, so it won't help with httpd vs apache2. Stick with dnf and apt modules and maintain separate playbooks or use variables:

vars:
  web_server_package: "{{ 'httpd' if ansible_os_family == 'RedHat' else 'apache2' }}"
tasks:
  - name: Install web server
    package:
      name: "{{ web_server_package }}"
      state: present

Handling Systemd Units and Config File Paths

Systemd unit files are usually identical across distros, but the unit names differ. RHEL's Apache is httpd.service, Debian's is apache2.service. Your scripts must change:

systemctl enable --now httpd

to:

systemctl enable --now apache2

Configuration file paths diverge significantly. Apache on RHEL uses /etc/httpd/conf/httpd.conf and /etc/httpd/conf.d/*.conf; Debian uses /etc/apache2/apache2.conf, /etc/apache2/sites-available/, and /etc/apache2/mods-available/. Network configuration differs: RHEL's /etc/sysconfig/network-scripts/ifcfg-eth0 becomes Netplan YAML in /etc/netplan/*.yaml on Ubuntu 18.04+ or /etc/network/interfaces on older Debian. Build a checklist of common paths:

Service RHEL/Fedora Path Debian/Ubuntu Path
Apache config /etc/httpd/conf/httpd.conf /etc/apache2/apache2.conf
Nginx config /etc/nginx/nginx.conf /etc/nginx/nginx.conf (same)
PHP-FPM pool /etc/php-fpm.d/www.conf /etc/php/8.2/fpm/pool.d/www.conf
Firewall firewall-cmd ufw or nftables
Network (static IP) /etc/sysconfig/network-scripts/ /etc/netplan/*.yaml or /etc/network/interfaces

For a quick path conversion in shell scripts, use sed or a lookup table. In Ansible, use ansible_os_family to branch logic. Test every config path on a Debian VM before deploying to production.

Warning: Apache module enabling differs between distros. On RHEL, modules are loaded via LoadModule directives in config files. On Debian, use a2enmod ssl and a2ensite default-ssl to enable modules and sites, which symlink files from mods-available and sites-available to mods-enabled and sites-enabled. Scripts that echo LoadModule lines into httpd.conf will break.

Handling Packages That Don't Translate Cleanly

Some packages have no Debian equivalent: vendor-supplied monitoring agents, proprietary database clients, or RHEL-specific utilities like subscription-manager and SELinux policy tools. You have three options: convert the RPM to a DEB with alien, build from upstream source, or containerize the RHEL-specific tool.

Using Alien to Convert RPMs (and Why It's a Last Resort)

The alien tool converts RPM packages to DEB format. Install it with apt install alien, then convert:

alien -d vendor-agent-1.2.3-1.x86_64.rpm
dpkg -i vendor-agent_1.2.3-2_amd64.deb

This works for simple, self-contained binaries with no complex dependencies. A vendor monitoring agent that drops a single binary in /usr/local/bin and a config file in /etc might install cleanly. Most packages fail because RPMs and DEBs handle library paths, init systems, and file layout differently. An RPM expecting /usr/lib64 breaks on Debian's /usr/lib/x86_64-linux-gnu. Pre- and post-install scripts that call systemctl or chkconfig with RHEL-specific assumptions will error out. After installing with dpkg -i, check for errors:

dpkg -l | grep vendor-agent
systemctl status vendor-agent

If the service fails to start or the binary segfaults, alien didn't work. Real example: a proprietary database backup tool ships only RPMs. Converting with alien produced a package that installed but crashed at runtime due to missing libc.so.6 symbol versions. The vendor didn't support Debian, so the solution was building from source.

Building from Source When No DEB Exists

If the upstream project provides source tarballs or a Git repository, build and install to /opt or /usr/local to avoid conflicts with APT-managed files:

wget https://example.com/vendor-tool-1.2.3.tar.gz
tar xzf vendor-tool-1.2.3.tar.gz
cd vendor-tool-1.2.3
./configure --prefix=/opt/vendor-tool
make
sudo make install

This keeps the tool isolated. Create a systemd unit in /etc/systemd/system/vendor-tool.service to manage it. For dependencies, install dev packages: if the build needs libssl, install libssl-dev. Debian's build-essential metapackage provides gcc, make, and libc headers. If the project uses CMake or Meson, install cmake or meson. Document the build steps in your migration notes so the next admin knows how to rebuild or upgrade the tool.

Containerizing RHEL-Specific Tools

For tools that absolutely require RHEL libraries or file layouts, run them in a Rocky Linux or Fedora container on your Debian host. Use Podman or Docker:

podman run -d --name rhel-tool \
  -v /data:/data:Z \
  rockylinux:9 \
  /usr/local/bin/vendor-tool --config /data/config.yaml

This avoids forcing incompatible packages onto Debian. The tool runs in its native environment, accessing host data via volume mounts. For desktop applications with no server equivalent, consider Flatpak or Snap: both are distro-agnostic and bundle dependencies. Install Flatpak with apt install flatpak, then flatpak install flathub com.example.App. Snap works similarly: snap install app-name. These solve the "vendor ships only RPMs" problem for end-user software.

Testing and Rollout Strategy

Spin up a Debian or Ubuntu VM matching your target production environment. Install the translated package list and compare the result to your original DNF inventory. Run smoke tests on every critical service before touching production.

Building a Parallel Test System

Use the same VM platform or container runtime as production. If your production Rocky servers run on KVM, deploy a Debian 12 KVM guest. If you use Docker Compose, build a Debian-based image. Install your translated packages:

apt update
apt install -y apache2 mariadb-server php php-mysql cron ufw

Compare installed packages to your original DNF inventory. Export the APT package list:

dpkg -l | awk '/^ii/ {print $2, $3}' > debian-packages.txt

Cross-reference with your installed-packages.txt from the DNF system. Missing packages indicate unmapped names or packages you decided to skip. Extra packages are dependencies pulled in by APT. For a detailed comparison, use comm or diff on sorted lists of package names.

Package Verification and Smoke Tests

After installation, verify each service starts and responds. For Apache:

systemctl status apache2
apachectl -t
curl http://localhost

The apachectl -t syntax check catches config errors. For MariaDB:

systemctl status mariadb
mysql -u root -p -e "SELECT VERSION();"

Check that your application connects to the database with the same credentials. For cron jobs, verify the crontab migrated:

crontab -l
systemctl status cron

Run a representative workload: deploy your app, hit the API endpoints, check logs in /var/log/apache2/ and /var/log/syslog. If your app uses environment-specific paths or commands, update them now. A script that calls /usr/sbin/apachectl works on both distros, but one that hardcodes /etc/httpd/conf.d/ needs fixing.

Documenting the Final Mapping

Maintain a migration log as a CSV or Markdown table. Columns: DNF package, APT package(s), notes on differences, test status. Example:

DNF Package APT Package Notes Tested
httpd apache2 Config in /etc/apache2/, use a2enmod for modules โœ“
mod_ssl apache2 (built-in) Enable with a2enmod ssl โœ“
cronie cron Same functionality โœ“
firewalld ufw Syntax differs; rewrote rules โœ“

This log serves as a reference for future migrations or rollback planning. For production rollout, deploy to dev first, then staging, then prod. If downtime is unacceptable, use a blue-green deployment: build the Debian system in parallel, migrate data, switch traffic with a load balancer cutover. One warning: database data directory migrations (PostgreSQL's /var/lib/pgsql to /var/lib/postgresql, MariaDB version upgrades) are orthogonal to package management. Handle them separately with pg_upgrade or mysql_upgrade.

Post-Migration Package Maintenance

APT's update and upgrade workflow differs from DNF in command syntax and behavior. Show upgradable packages:

apt update
apt list --upgradable

The apt update step refreshes repository metadata (like dnf makecache). Apply updates with:

apt upgrade

This installs new versions of installed packages but does not remove or install new packages to satisfy changed dependencies. For a full upgrade that handles dependency changes (like dnf upgrade or dnf distro-sync), use:

apt full-upgrade

The older apt-get dist-upgrade is equivalent to apt full-upgrade. Remove unneeded dependencies with:

apt autoremove

This is the same concept as dnf autoremove. For an inventory of installed packages, use apt list --installed or dpkg -l. The apt(8) man page documents all commands and options. Set up unattended-upgrades for automatic security patching, equivalent to dnf-automatic:

apt install unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

This enables daily checks and automatic installation of security updates. Monitor security advisories: Debian's security mailing list (debian-security-announce) and Ubuntu's USN feed publish CVE fixes. Check the APT package health auditing guide for tools to track outdated and vulnerable packages. Use apt list --upgradable in monitoring scripts to alert when patches are available. For APT history, read /var/log/apt/history.log and /var/log/dpkg.log, which record every install, upgrade, and removal with timestamps.

Migration Checklist and Next Steps

Before you declare the migration complete, verify these steps:

  • Export DNF package inventory with dnf repoquery --installed and dnf history userinstalled.
  • Map every package name to its APT equivalent using search, apt-file, and upstream documentation.
  • Translate custom repositories: download GPG keys, convert .repo files to sources.list entries.
  • Update automation scripts: replace dnf with apt-get or apt, change package names, fix config file paths.
  • Convert Ansible playbooks: swap dnf module for apt, update package lists, test on a staging host.
  • Handle packages with no DEB equivalent: try alien, build from source, or containerize.
  • Deploy to a test VM, run smoke tests, document differences in a migration log.
  • Roll out to dev, staging, then production with a phased approach or blue-green cutover.
  • Configure unattended-upgrades and monitor security advisories.

For deeper coverage of APT internals, dependency resolution, and troubleshooting, see the Debian System Administration eBook. It covers advanced topics like pinning, backports, and building custom DEBs. After migration, audit your package inventory regularly with the Linux package auditing tool to catch outdated or orphaned packages. If you're consolidating mixed infrastructure, compare Ubuntu Server vs Debian to decide on a standard distro going forward.

Frequently Asked Questions

Can I directly convert an RPM package to DEB and install it?

Yes, with the alien tool: alien -d package.rpm then dpkg -i package.deb. This works only for simple, self-contained binaries with no complex dependencies. Most packages fail because RPMs and DEBs handle library paths, init scripts, and file layouts differently. Pre/post-install scripts often assume RHEL-specific paths or commands. Use alien only as a last resort; build from source or find a Debian equivalent instead.

What happens to packages installed from EPEL when I migrate to Ubuntu?

EPEL has no direct Ubuntu equivalent. Many EPEL packages already exist in Ubuntu's universe or multiverse repositories, or in Debian's main/contrib repos. Search with apt search package-name. For packages not in default repos, check if the upstream project publishes a DEB repo (PostgreSQL, Docker, Nginx all do). If no DEB exists, build from source or find a functional replacement in Ubuntu's archives.

Do DNF package groups have an equivalent in APT?

Partially. DNF groups like "Development Tools" map loosely to Debian metapackages like build-essential or tasksel selections. List DNF group contents with dnf group info "Group Name", then install the individual packages on Debian. Ubuntu's tasksel offers task-based installations (LAMP server, Mail server), but the groupings differ. You'll need to manually map group members to APT packages.

How do I find the Debian package name for an RHEL package?

Use apt search keyword or apt-file search /path/to/binary on a Debian system. For offline lookup, search packages.debian.org by package name or file path. Cross-distro search at pkgs.org shows package names across RHEL, Debian, and Ubuntu. Common translations: httpd โ†’ apache2, cronie โ†’ cron, bind โ†’ bind9. Maintain a mapping file for your inventory.

Will my systemd service files work after migrating from Fedora to Debian?

Mostly yes. Systemd unit syntax is identical across distros, but service names differ: httpd.service becomes apache2.service. If your unit files reference absolute paths to binaries or config files, verify those paths exist on Debian. RHEL uses /etc/httpd/, Debian uses /etc/apache2/. Custom units in /etc/systemd/system/ migrate fine if they don't hardcode distro-specific paths. Test with systemctl status service-name after migration.

Share this article:
Dorian Thorne
About the Author

Dorian Thorne

Cloud Infrastructure, Cloud Architecture, Infrastructure Automation, Technical Documentation

Dorian Thorne is a cloud infrastructure specialist and technical author focused on the design, deployment, and operation of scalable cloud-based systems.

He has extensive experience working with cloud platforms and modern infrastructure practices, including virtualized environments, cloud networking, identity and acces...

Cloud Computing Cloud Networking Identity and Access Management Infrastructure as Code System Reliability

Stay Updated

Subscribe to our newsletter for the latest tutorials, tips, and exclusive offers.