Skip to content

Linux Distribution Comparison — Footguns & Pitfalls


1. Assuming All Linux Is the Same

Scripts written for Ubuntu will break on RHEL and vice versa: - apt doesn't exist on RHEL - dnf doesn't exist on Debian - ufw doesn't exist on RHEL - firewall-cmd doesn't exist on Ubuntu (unless installed) - /var/log/syslog (Debian) vs /var/log/messages (RHEL) - /var/log/auth.log (Debian) vs /var/log/secure (RHEL)

Always check ansible_os_family or /etc/os-release before assuming.


2. CentOS Stream ≠ CentOS Linux

CentOS Linux (7, 8) was a downstream RHEL rebuild — binary compatible. CentOS Stream is upstream of RHEL — it's the development branch. Not a drop-in RHEL replacement. Use AlmaLinux or Rocky Linux for that.


3. Alpine musl vs glibc

Alpine uses musl libc instead of glibc. This means: - Binaries compiled on Ubuntu/RHEL won't run on Alpine - Some Python packages with C extensions fail to compile - DNS resolution behaves differently (no nsswitch.conf) - Thread handling differs (can cause Go runtime issues)

If your container app breaks on Alpine but works on Debian, musl is likely the cause.

Debug clue: A telltale sign of musl incompatibility is the error error loading shared library: libc.musl-x86_64.so.1 or a segfault on startup. Another subtle symptom: DNS lookups for names resolvable only via /etc/hosts or mDNS fail because musl does not use nsswitch.conf — it goes straight to DNS. If your service discovery relies on /etc/hosts entries (common in Docker Compose), Alpine containers may fail to resolve them where Debian containers succeed.


4. Rolling Release Surprise Breakage

Arch, openSUSE Tumbleweed, and Fedora move fast: - A pacman -Syu can break your desktop or server - No pinned "stable" version to stay on - Kernel updates may break drivers - Library updates may break compiled software

Rule: Never use rolling-release distros for production servers. Use them for development/workstations only.


5. Ubuntu Snap vs Apt Confusion

# Is Firefox from apt or snap?
which firefox    # /usr/bin/firefox (could be a snap wrapper!)
snap list | grep firefox

# Snap packages:
# - Auto-update (can't pin versions easily)
# - Use loop mounts (clutter df)
# - May have different file access than apt packages
# - Slower to start (first launch)

6. RHEL Subscription Traps

# RHEL without subscription:
# - Can't run `dnf install` (repos not enabled)
# - Can't get security updates
# - Must register: subscription-manager register

# CentOS/Alma/Rocky: no subscription needed
# But also no vendor support

# Developer subscription: free for up to 16 systems
# https://developers.redhat.com/register

7. Different Default Firewall Rules

  • RHEL: firewalld enabled by default, SSH allowed, most else blocked
  • Ubuntu: ufw disabled by default — everything is open!
  • Debian: no firewall frontend by default — raw nftables
  • Alpine: no firewall by default

Don't assume your new server has a firewall running.


8. Kernel Version ≠ Feature Availability

# Ubuntu 22.04 LTS: kernel 5.15 (backported features from newer kernels)
# RHEL 9: kernel 5.14 (heavily patched, may have features from 6.x)
# Alpine 3.20: kernel 6.6 (vanilla, fewer patches)

# Same "5.15" kernel on Ubuntu vs vanilla means very different capabilities
# Ubuntu/RHEL backport security fixes without bumping version numbers

Don't compare kernel versions across distros — they're not equivalent.


9. /etc/network/interfaces Is Dead (Mostly)

# Ubuntu 17.10+ uses Netplan, NOT /etc/network/interfaces
# If you edit interfaces, nothing happens (or worse, conflicts with Netplan)

# Check what's active:
ls /etc/netplan/
# If files exist → use Netplan
# If empty AND /etc/network/interfaces has config → old-style still active

10. SELinux on Debian/Ubuntu Is Possible But Fragile

You CAN install SELinux on Debian/Ubuntu, but: - Default policies are much less complete than RHEL's - Many packages lack SELinux policy support - AppArmor and SELinux cannot run simultaneously - Community support is minimal

If you need SELinux, use RHEL. If you're on Debian/Ubuntu, use AppArmor.


11. Package Name Differences Bite in Automation

# This breaks on Ubuntu:
- ansible.builtin.dnf:
    name: httpd    # Package is called "apache2" on Debian

# This breaks on RHEL:
- ansible.builtin.apt:
    name: apache2  # Package is called "httpd" on RHEL

# Safe: use ansible.builtin.package + os_family vars
- ansible.builtin.package:
    name: "{{ web_server_pkg }}"
    state: present

Always use variables for package names in cross-distro automation.


12. LTS ≠ "Safe to Ignore for 5 Years"

LTS means you get security patches, not that you can skip updates: - Unpatched LTS is worse than updated non-LTS - LTS libraries may be too old for newer software - "LTS" end date is for the OS, not every package in it

Patch regularly regardless of LTS status.