Skip to main content

Updated Sep 3, 2026

Distributions and What Actually Differs

Same kernel, different packaging: what genuinely varies between distributions and the much longer list of things that do not.

The interesting question about distributions is not what they differ on — that list is short and mostly cosmetic. It is the far longer list of what they cannot differ on, because every distribution ships the same kernel interface: the same syscalls, the same /proc, the same ELF format. That constancy is not an accident of convention; it is why a statically linked binary built on any one distribution runs, unmodified, on every other one. Anything that varies has to vary somewhere other than the kernel interface — and finding where is what this page is about.

What genuinely differs​

DebianFedoraArchAlpineAndroid
Init systemsystemdsystemdsystemdOpenRCAndroid init (its own init.rc language, not systemd)
C libraryglibcglibcglibcmuslBionic
Package managerAPT / dpkgDNF / RPMpacmanapkPackage Manager Service (APK files, no general-purpose package manager)
Kernel config & patch setDebian's own .config + conservative patch setFedora's own .config, tracks upstream closelyArch's own .config, minimal patchingAlpine's own .config, hardening-leaningVendor/OEM .config + vendor patches, often heavily forked
Default filesystemext4Btrfsext4 (installer default; user-chosen)ext4ext4 or F2FS, device-dependent
Security moduleAppArmorSELinux (enforcing by default)None by defaultNone by defaultSELinux (mandatory, enforcing since Android 5.0)
Release modelFixed release (stable/testing/unstable)Fixed release, ~6 monthsRolling releaseFixed release, ~6 months, plus a rolling edge branchTied to AOSP + vendor image releases, OEM-controlled

What five representative Linux distributions actually vary: packaging, defaults, and policy — never the kernel interface itself.

The kernel config is the biggest real difference​

Every distribution in that table builds from the same upstream source, but not the same .config. A feature that is compiled out of a kernel is not merely disabled by default — it is genuinely absent: no code for it exists in the running kernel image, and no runtime setting brings it back. This is a bigger source of real behavioral difference between distributions than any userland tool, because it determines which subsystems, filesystems, and security features can possibly exist on that machine at all. It is also the reason two people running "the same kernel version" from two different distributions can observe genuinely different capabilities. How a Kconfig symbol becomes a compiled object — and how to read a kernel's actual configuration back out — is covered in Kconfig and Kbuild; the running kernel's own config is queryable directly:

$ zcat /proc/config.gz | grep CONFIG_IKCONFIG
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y

That /proc/config.gz file only exists when the kernel was built with kernel/configs.c:CONFIG_IKCONFIG_PROC — it is itself a config option, and a distribution that omits it makes its own kernel's configuration unqueryable from the running system.

libc is the second​

The C library is the second-biggest real difference, and the one most often blamed on the wrong layer. glibc and musl both implement the C standard library and the userland side of the syscall ABI, but they are not interchangeable at the binary level: a binary dynamically linked against glibc does not run on Alpine without extra work, because Alpine ships musl and no glibc-compatible dynamic loader. People routinely describe this as "Alpine's kernel doesn't support X" — it is not a kernel problem at all. The kernel underneath both distributions accepts the identical set of syscalls; the failure happens entirely in userland, when the dynamic loader can't find symbols the binary expects.

What does not differ​

This is the section that makes the rest of the page worth reading, because it is the longer list:

  • Syscall numbers and semantics — the same syscall number means the same operation, with the same arguments and the same error codes, on every distribution running the same architecture.
  • /proc and /sys layout — both are kernel-generated, not distribution-generated; their structure comes from the kernel's own code, not a distribution's packaging choices.
  • The ELF ABI — the executable and shared-library format, and how the dynamic loader resolves symbols, is a kernel-and-toolchain contract, not a distribution one.
  • Signal semantics — signal numbers, default dispositions, and delivery semantics are kernel behavior, identical everywhere.
  • The VFS interface — the system calls a userland program uses to interact with any filesystem are the same regardless of which filesystem a distribution defaults to.
  • Page size and address-space layout, on the same architecture — these come from the kernel and the hardware it runs on, not from packaging decisions.

What actually happens​

"The package manager installed a kernel" hides a sequence that is worth having in mind, because the last step of it is the one people forget:

  1. The package unpacks a compressed kernel image and a matching tree of modules to /boot and /lib/modules/$(uname -r).
  2. depmod runs, rebuilding the module dependency index against the newly installed module tree.
  3. An initramfs is generated fresh, against this specific machine's hardware and installed modules — not shipped pre-built, because a generic one couldn't know in advance which storage controller or filesystem driver this machine's boot needs.
  4. A boot-loader entry is added pointing at the new kernel, and the previous kernel is normally left installed as a fallback rather than removed.

None of that changes what is actually running. The kernel currently executing is whatever was loaded at the last boot, and it stays that way regardless of what a package manager just wrote to /boot — which is exactly why uname -r can disagree with the newest kernel sitting in /boot: the update took effect on disk, not in memory, and nothing takes effect in memory until the next reboot.

Android is Linux​

The one distribution that surprises people the most: Android runs the Linux kernel, full stop — the same kernel this section is about, with vendor and Android-specific patches layered on top. But it carries almost none of the userland most people associate with "Linux." There is no GNU userland: the C library is Bionic, not glibc. There is no systemd, or any conventional Linux init system — Android's init process reads its own init.rc configuration language and has no relationship to the init systems in the table above. There is no general-purpose package manager in the APT/DNF/pacman sense; applications install as signed APK bundles through the Package Manager Service. And its security model goes further than any desktop distribution's default: SELinux has been mandatory and fully enforcing since Android 5.0, applied per-app through a much more granular policy than a typical Linux desktop uses. Same kernel; almost nothing else in common with Debian.

Misconceptions​

  1. "Distributions ship different kernels." They ship different configurations and patch sets of the same upstream kernel, not different kernels in any architectural sense. The syscall interface, the VFS, and the rest of the mechanism described in this section are the same code everywhere.
  2. "Alpine is small because its kernel is small." Alpine's install footprint is small because of its userland choices — musl instead of glibc, BusyBox instead of GNU coreutils — not because its kernel is a stripped-down or different kernel. The kernel itself is ordinary upstream Linux with Alpine's own config.
  3. "The distribution decides how memory management works." A distribution decides defaults — the sysctl values a fresh install ships with, things like swappiness or overcommit policy — not the underlying mechanism. The memory-management code itself is the same kernel code, unaffected by which distribution is running it.

References​