How Do You Build a PREEMPT_RT Kernel from Source?

« Previous Lecture | Next Lecture »

Build a Real-Time Linux Kernel from Source: Step-by-Step PREEMPT_RT Guide

Part 2 of our free Linux kernel development course — configure, compile, boot, and verify a PREEMPT_RT kernel on a modern LTS release

Level
Hands-On
Kernel
6.12 LTS / newer
Time
~1–2 hours

In this hands-on lecture of our free Linux kernel development course, you will build a real-time Linux kernel from source. If you learned real-time Linux from older books or tutorials, you remember a painful ritual: hunt for an RT patch that matches your exact kernel version, download both, apply the patch, hope nothing conflicts, and only then configure and build. Since kernel 6.12, that entire ritual is gone on x86, x86_64, ARM64, and RISC-V. To build a real-time Linux kernel today you download one kernel source tree, select one preemption option, and compile. This lecture walks through every command, explains what each step does, and shows how to verify the result after boot.

This lecture continues from Part 1 (real-time fundamentals). If terms like determinism, threaded IRQ, or priority inheritance are new to you, read Part 1 first — it is part of the same free embedded systems course.

Topics covered in this lecture

Kernel Build
CONFIG_PREEMPT_RT
menuconfig
LTS Kernel
Kernel Install
GRUB
Verification

What You Will Learn

  • How to choose the right kernel version to build a real-time Linux kernel
  • Installing the toolchain and build dependencies
  • Downloading and verifying kernel source from kernel.org
  • Selecting the Fully Preemptible Kernel (Real-Time) preemption model
  • Key config options that affect real-time behaviour
  • Compiling, installing, and booting the new kernel
  • Verifying that PREEMPT_RT is genuinely active
  • Common build mistakes and how to fix them

Prerequisites

  • An x86_64 Linux machine or VM (Ubuntu/Debian commands shown; adapt for Fedora/Arch)
  • At least 30 GB free disk space and 4 GB RAM (more cores = faster build)
  • Root or sudo access
  • Comfort with the terminal; one prior kernel build is helpful but not mandatory

Step 0: The Old Way vs the New Way

A quick orientation so you understand why modern guides look so different from older ones. Before kernel 6.12, the real-time capability lived in external patches maintained by the Real-Time Linux project. Those patches only existed for certain point releases, so if your kernel was 5.4.0 but the patch targeted 5.4.69, you first had to obtain the matching source. Today that mismatch problem simply does not exist for the supported architectures — the code is already inside every kernel from 6.12 onward.

Workflow comparison: patch-based (pre-6.12) vs mainline (6.12+)
Step Old workflow (patch era) Modern workflow (6.12+)
1 Find an RT patch matching an exact kernel point release Download any 6.12+ kernel source
2 Download that exact kernel source separately — (not needed)
3 Apply the patch, resolve failures — (not needed)
4 Enable RT option, build, install Enable RT option, build, install

Step 1: Install the Build Toolchain

On Ubuntu or Debian, install everything the kernel build needs in one go:

sudo apt update
sudo apt install -y build-essential flex bison libssl-dev libelf-dev 
    libncurses-dev bc dwarves rsync kmod cpio wget xz-utils

Quick notes: flex and bison generate parsers used during the build, libncurses-dev powers the menuconfig text UI, libelf-dev and dwarves are needed for BTF debug info, and libssl-dev handles module signing.

Step 2: Download the Kernel Source to Build a Real-Time Linux Kernel

We will use the 6.12 LTS series — the first mainline line with built-in PREEMPT_RT, maintained for years to come. Always fetch the latest point release of the series from kernel.org (the exact number changes weekly; substitute whatever is current):

# Create a work area
mkdir -p ~/rtkernel && cd ~/rtkernel

# Download the latest 6.12.y source (adjust the point release as needed)
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.95.tar.xz

# Extract it
tar xf linux-6.12.95.tar.xz
cd linux-6.12.95

That is the entire acquisition step. No second download, no patch file, no version matching. If you prefer the newest features, any 6.13+ or 7.x kernel works identically for this tutorial.

Step 3: Start from Your Distro’s Config

Rather than answering thousands of config questions, seed your configuration from the kernel you are already running — it is known to boot your hardware:

# Copy the running kernel's config into the source tree
cp /boot/config-$(uname -r) .config

# Update it for the new source, accepting defaults for new options
make olddefconfig

Two options commonly block builds on Ubuntu-derived configs because they reference distro signing keys you do not have. Clear them now:

scripts/config --disable SYSTEM_TRUSTED_KEYS
scripts/config --disable SYSTEM_REVOCATION_KEYS

Step 4: Enable the Fully Preemptible Kernel (PREEMPT_RT)

Now the heart of this lecture. Open the configuration menu:

make menuconfig

Navigate to:

General setup
  ---> Preemption Model
        ( ) No Forced Preemption (Server)
        ( ) Voluntary Kernel Preemption (Desktop)
        ( ) Preemptible Kernel (Low-Latency Desktop)
        (X) Fully Preemptible Kernel (Real-Time)   <-- select this

Select Fully Preemptible Kernel (Real-Time), save, and exit. If the Real-Time entry does not appear, the usual cause is CONFIG_EXPERT being required on your kernel version — enable General setup → Configure standard kernel features (expert users) and look again.

You can also set it non-interactively, which is handy for scripts and CI:

scripts/config --disable PREEMPT_NONE
scripts/config --disable PREEMPT_VOLUNTARY
scripts/config --disable PREEMPT
scripts/config --enable  PREEMPT_RT

# Confirm the result
grep -E "PREEMPT(_RT)?=" .config

Expected output includes CONFIG_PREEMPT_RT=y.

Recommended supporting options

Config options that matter for a real-time Linux kernel
Option Recommended Why
CONFIG_PREEMPT_RT y The real-time preemption model itself
CONFIG_HIGH_RES_TIMERS y Microsecond-resolution timers; RT is pointless without them (default on)
CONFIG_NO_HZ_FULL y (optional) Lets isolated CPUs run tick-free for lower jitter
CONFIG_DEBUG_PREEMPT n (production) Useful while developing, adds overhead in deployment
CONFIG_LOCKUP_DETECTOR y (dev) Catches runaway RT tasks starving the system

Step 5: Compile the Kernel

# Build using all CPU cores; add LOCALVERSION so you can identify it later
make -j$(nproc) LOCALVERSION=-ep-rt

# Build time: roughly 20-90 minutes depending on your machine and config

Tip: to shrink build time and disk usage dramatically, trim the config to only the modules your machine actually uses before building:

# Optional: keep only modules currently loaded (run before make)
make localmodconfig

Step 6: Install and Boot

# Install modules, then the kernel image + initramfs + GRUB entry
sudo make modules_install
sudo make install

# Refresh the bootloader menu (usually done automatically by make install)
sudo update-grub

# Reboot and pick the new kernel from the GRUB menu
sudo reboot

On the GRUB screen, open Advanced options and choose the entry ending in -ep-rt. Keeping your old kernel installed is your safety net — if anything misbehaves, boot the previous entry.

Step 7: Verify PREEMPT_RT Is Active

Never assume — verify. Three independent checks:

# 1. The version string must contain PREEMPT_RT
uname -v
# Example: #1 SMP PREEMPT_RT Wed Jul  8 10:15:03 IST 2026

# 2. This file exists only on RT kernels and must read 1
cat /sys/kernel/realtime

# 3. Interrupt handlers now run as threads
ps -e -o pid,cls,rtprio,comm | grep irq/ | head

If uname -v shows PREEMPT_RT, /sys/kernel/realtime prints 1, and you see [irq/...] kernel threads with real-time priority (class FF, priority 50 by default), congratulations — you are running a real-time Linux kernel that you built yourself.

A First Real-Time Test Program

Let us prove the scheduler behaves differently. This small program requests SCHED_FIFO real-time priority and locks its memory — the two most fundamental habits of real-time application code:

#include <stdio.h>
#include <sched.h>
#include <sys/mman.h>
#include <string.h>

int main(void)
{
    struct sched_param sp;
    memset(&sp, 0, sizeof(sp));
    sp.sched_priority = 80;   /* 1..99 for SCHED_FIFO */

    /* Lock all current and future pages in RAM - avoids page-fault latency */
    if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
        perror("mlockall");
        return 1;
    }

    /* Ask for real-time FIFO scheduling */
    if (sched_setscheduler(0, SCHED_FIFO, &sp) != 0) {
        perror("sched_setscheduler (run with sudo?)");
        return 1;
    }

    printf("Running with SCHED_FIFO priority %dn", sp.sched_priority);
    return 0;
}
gcc -O2 -Wall rt_hello.c -o rt_hello
sudo ./rt_hello

One caution: a runaway SCHED_FIFO task can starve the whole system. The kernel protects you by default — /proc/sys/kernel/sched_rt_runtime_us reserves a slice of each second for non-RT tasks. Leave that protection on while learning.

Common Mistakes and Troubleshooting

  • Build fails mentioning canonical certs / trusted keys: you skipped the SYSTEM_TRUSTED_KEYS disable step in Step 3.
  • No Real-Time entry in the Preemption Model menu: enable expert mode (CONFIG_EXPERT) if your version requires it, and confirm you are on kernel 6.12 or newer for a supported architecture.
  • /sys/kernel/realtime missing after reboot: you probably booted the old kernel. Check uname -r against your LOCALVERSION suffix.
  • Out of disk space mid-build: a full build with debug info can exceed 25 GB. Use make localmodconfig or disable CONFIG_DEBUG_INFO.
  • sched_setscheduler returns EPERM: real-time priorities need root or the CAP_SYS_NICE capability, or an rtprio limit configured in /etc/security/limits.conf.

Best Practices

  • Always build from an LTS series for anything long-lived; point releases within the series are drop-in rebuilds.
  • Keep the previous kernel installed until the new one has proven itself.
  • Tag every experimental build with a distinct LOCALVERSION so uname -r tells you exactly what is running.
  • Treat the RT config as version-controlled source: keep your .config in git alongside notes on why each option was chosen.
  • Verification (Step 7) belongs in your deployment checklist, not just your tutorial notes.

Key Takeaways

  • To build a real-time Linux kernel on 6.12+ you need exactly one special step: select the Fully Preemptible Kernel (Real-Time) model.
  • Seed the config from your running distro kernel, then olddefconfig, then enable PREEMPT_RT.
  • Verify with uname -v, /sys/kernel/realtime, and the presence of threaded IRQ processes.
  • Real-time applications should use SCHED_FIFO/SCHED_RR and mlockall() from day one.

Conclusion

You have now configured, compiled, booted, and verified your own real-time Linux kernel — without touching a single external patch. This is the payoff of two decades of upstreaming work: what used to be a fragile expert workflow is now a standard kernel build with one extra menu choice. But a real-time kernel without measurements is only a promise. In the next lecture of this free Linux kernel development course we quantify the promise: you will run cyclictest and the kernel’s built-in rtla/timerlat tooling, stress the system, read latency histograms, and learn the tuning knobs (CPU isolation, IRQ affinity, tick-free operation) that separate a demo from a deployable system.

Frequently Asked Questions

Which architectures support mainline PREEMPT_RT?

x86 (32/64-bit), ARM64, and RISC-V have mainline support from kernel 6.12. Other architectures may still rely on external RT patches.

Can I enable PREEMPT_RT on my distro’s kernel without rebuilding?

No — the preemption model is a compile-time choice. However, several distros ship prebuilt real-time kernel packages (for example Ubuntu’s real-time kernel and Debian’s linux-image-rt), which you can install instead of building.

How long does the build take?

From about 20 minutes on a modern 12+ core desktop with a trimmed config, up to a few hours on a 2-core VM with a full distro config.

Does PREEMPT_RT work inside a virtual machine?

It builds and boots fine in a VM and is perfect for learning, but latency numbers inside a VM are dominated by the host and are not representative. Measure on real hardware.

Do I need to rebuild out-of-tree drivers?

Yes. Any out-of-tree module must be rebuilt against the new kernel, and some vendor drivers misbehave under RT because they assume non-threaded interrupt context. Prefer in-tree drivers on real-time systems.

What is LOCALVERSION for?

It appends a suffix to the kernel release string so multiple installed kernels are distinguishable in GRUB and in uname -r. It has no functional effect.

Is there a difference between CONFIG_PREEMPT_RT and the old RT patch?

The mainline code is the same lineage. The external patch series still exists and carries a few optimisations not yet upstreamed, but for learning and most production uses the mainline option is the right choice.

Keep Going — This Course Is 100% Free

Next: measure your kernel’s latency with cyclictest and rtla, and learn real tuning.

Next: Measuring Latency »
Course Index

« Previous Lecture | Next Lecture »

Leave a Reply

Your email address will not be published. Required fields are marked *