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.1or a segfault on startup. Another subtle symptom: DNS lookups for names resolvable only via/etc/hostsormDNSfail because musl does not usensswitch.conf— it goes straight to DNS. If your service discovery relies on/etc/hostsentries (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.