CI/CD for the Linux Kernel
Introduction
Continuous Integration and Continuous Delivery (CI/CD) for the Linux kernel is a massive undertaking. The kernel is one of the largest and most complex open-source projects, with over 28 million lines of code, thousands of configuration options, and hundreds of target architectures. Testing it requires specialized infrastructure that can build, boot, and stress-test kernels across diverse hardware.
This chapter covers the major CI/CD systems used by the kernel community, from Intel’s 0-Day bot to the community-driven KernelCI, and practical approaches for CI/CD in kernel module development.
The Kernel CI Landscape
graph TB
subgraph "Automated Testing"
ZDAY["0-Day Bot<br>Intel"] -->|builds & tests| REPORT1[Email reports to LKML]
KCI["KernelCI<br>Community"] -->|multi-arch builds| REPORT2[Dashboard]
LAVA["LAVA<br>Linaro"] -->|hardware testing| REPORT3[Test results]
SYZKALLER["syzkaller<br>Google"] -->|fuzzing| BUGS[Bug reports]
end
subgraph "Manual Testing"
NEXT[linux-next tree] -->|developer testing| FEEDBACK[Feedback]
KTEST[ktest] -->|automated boot tests| RESULTS[Boot logs]
end
subgraph "CI Systems"
GH[GitHub Actions] -->|module CI| BUILD[Build artifacts]
GL[GitLab CI] -->|custom pipelines| DEPLOY[Deploy]
JENKINS[Jenkins] -->|enterprise| PACKAGE[Package builds]
end
style ZDAY fill:#f96,stroke:#333,stroke-width:2px
style KCI fill:#bbf,stroke:#333,stroke-width:2px
Intel’s 0-Day Bot
Overview
The 0-Day bot is Intel’s automated kernel testing system. It’s one of the most prolific contributors to the kernel—not by writing code, but by finding bugs.
0-Day Bot Statistics (2024)
───────────────────────────
Patches tested per day: ~200-400
Builds per day: ~1,000+
Configurations tested: ~200+
Architectures: x86_64, ARM64, RISC-V
Bug reports per year: ~5,000+
Performance tests: Continuous
Maintained by: Intel OSS Technology Center
How 0-Day Works
sequenceDiagram
participant Dev as Developer
participant LKML as Mailing List
participant Zero as 0-Day Bot
participant Build as Build Farm
participant Test as Test Farm
Dev->>LKML: git send-email (PATCH)
LKML->>Zero: Monitor for new patches
Zero->>Zero: Parse patch, identify subsystem
Zero->>Build: Build with relevant configs
Build->>Zero: Build success/failure
alt Build Success
Zero->>Test: Boot test (QEMU)
Zero->>Test: Static analysis (smatch, sparse)
Zero->>Test: Performance benchmarks
Test->>Zero: Test results
end
Zero->>LKML: Reply with report
Note over Zero: Reported-by, Reviewed-by tags
0-Day Report Example
From: kernel test robot <lkp@intel.com>
Subject: [net] e1000e: fix regression - build error
tree: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
head: abc123def456
commit: 7890abcdef12 ("e1000e: add new device support")
date: 3 days ago
config: x86_64-randconfig-001-20250721
compiler: gcc-13 (GCC 13.2.0)
Error (expand):
drivers/net/ethernet/intel/e1000e/netdev.c: In function 'e1000e_setup_rx_resources':
drivers/net/ethernet/intel/e1000e/netdev.c:1234:25: error: implicit declaration of function 'new_api_func'
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507210000.ABCDEF@lkp/
Static Analysis Tools Used by 0-Day
# Sparse — Semantic parser for C
$ make C=1 CHECK=sparse -j$(nproc)
# Or for specific files:
$ make C=2 drivers/net/ethernet/intel/e1000e/
# Smatch — Static analysis for kernel code
$ ./smatch_scripts/test_kernel.sh
# Coccinelle — Semantic patching
$ make coccicheck
# Or for specific semantic patches:
$ sp --sp-file scripts/coccinelle/free/kfree.cocci fs/ext4/
# GCC static analyzer
$ make KCFLAGS="-fanalyzer" -j$(nproc)
# Clang static analyzer
$ make CC=clang-analyzer -j$(nproc)
KernelCI
Overview
KernelCI is a community-driven project that tests the Linux kernel across multiple architectures and hardware platforms.
KernelCI Facts
──────────────
URL: https://kernelci.org/
Goal: Test every kernel commit on real hardware
Architectures: x86_64, ARM, ARM64, RISC-V, MIPS
Hardware: 100+ physical devices in labs
Test types: Boot, kselftest, LTP, benchmarks
Data: kernelci.org API and dashboard
Funding: Linux Foundation, Collabora, Google, ARM
KernelCI Architecture
graph LR
SRC[Kernel Source] --> BUILD[Build Service]
BUILD --> ARTIFACTS["Build Artifacts<br>kernel + rootfs + DTBs"]
ARTIFACTS --> LAVA[LAVA Lab]
ARTIFACTS --> OTHER[Other Labs]
LAVA --> HW["Physical Hardware<br>ARM, RISC-V, x86"]
HW --> RESULTS[Test Results]
RESULTS --> API[KernelCI API]
API --> DASHBOARD[Dashboard]
API --> NOTIFY[Notifications]
style BUILD fill:#f96,stroke:#333
style LAVA fill:#bbf,stroke:#333
style API fill:#9f9,stroke:#333
Using KernelCI API
# Query recent build results
$ curl -s "https://api.kernelci.org/build?kernel_branch=mainline&limit=10" | \
jq '.data[] | {kernel: .kernel.version, arch: .arch, status: .status}'
# Get test results for a specific kernel version
$ curl -s "https://api.kernelci.org/test?kernel_version=6.12&limit=10" | \
jq '.data[] | {test: .test_suite, device: .device, status: .status}'
# List available devices
$ curl -s "https://api.kernelci.org/device" | \
jq '.data[] | {name: .name, arch: .arch}'
LAVA (LAb for Validation Architecture)
Overview
LAVA is a test automation framework developed by Linaro for deploying and testing software on real hardware.
# Install LAVA (Debian/Ubuntu)
$ sudo apt-get install lava-server lava-dispatcher
# Or use the LAVA Docker setup
$ git clone https://git.lavasoftware.org/lava/lava-docker
$ cd lava-docker
$ docker-compose up
LAVA Job Definition
# Example LAVA job definition for ARM64 boot test
device_type: qemu-arm64
job_name: kernel-boot-test-arm64
timeouts:
job:
minutes: 30
action:
minutes: 10
priority: medium
visibility: public
actions:
- deploy:
to: tftp
kernel:
url: https://storage.kernelci.org/mainline/v6.12/arm64/gcc-13/Image
ramdisk:
url: https://storage.kernelci.org/rootfs/buildroot/arm64/rootfs.cpio.gz
compression: gz
os: oe
- boot:
method: qemu
media: tmpfs
prompts:
- "root@lava:~#"
- test:
timeout:
minutes: 10
definitions:
- repository: https://github.com/kernelci/test-definitions.git
from: git
path: automated/linux/boot
name: boot-test
LAVA REST API
# Submit a job
$ curl -X POST \
-H "Authorization: Token $LAVA_TOKEN" \
-H "Content-Type: application/yaml" \
-d @job.yaml \
https://lava.example.net/api/v0.2/jobs/
# Check job status
$ curl -H "Authorization: Token $LAVA_TOKEN" \
https://lava.example.net/api/v0.2/jobs/12345/
# Get job logs
$ curl -H "Authorization: Token $LAVA_TOKEN" \
https://lava.example.net/api/v0.2/jobs/12345/logs/
syzkaller: Kernel Fuzzing
Overview
syzkaller is a coverage-guided kernel fuzzer developed by Google. It has found thousands of kernel bugs.
# Install syzkaller
$ go install github.com/google/syzkaller/syz-manager@latest
# Create configuration
cat > syz-manager.cfg << 'EOF'
{
"target": "linux/amd64",
"http": "localhost:56741",
"workdir": "/syzkaller/workdir",
"kernel_obj": "/path/to/kernel/build",
"image": "/path/to/image.qcow2",
"sshkey": "/path/to/ssh/key",
"syzkaller": "/syzkaller",
"procs": 4,
"type": "qemu",
"vm": {
"count": 4,
"kernel": "/path/to/kernel/build/arch/x86/boot/bzImage",
"cpu": 2,
"mem": 2048
}
}
EOF
# Run syzkaller
$ syz-manager -config=syz-manager.cfg
# Dashboard: https://syzkaller.appspot.com/
syzkaller Bug Report Example
Title: KASAN: use-after-free in ext4_write_inline_data
syzkaller found a use-after-free bug in ext4:
BUG: KASAN: use-after-free in ext4_write_inline_data+0x234/0x300 fs/ext4/inline.c:234
Read of size 8 at addr ffff888123456789 by task syz-executor.0/1234
CPU: 0 PID: 1234 Comm: syz-executor.0 Not tainted 6.12.0-rc1 #1
Hardware name: QEMU Standard PC
Call Trace:
<TASK>
dump_stack_lvl+0x91/0xf0 lib/dump_stack.c:107
print_report+0x17a/0x4b0 mm/kasan/report.c:399
kasan_report+0xc4/0x100 mm/kasan/report.c:496
ext4_write_inline_data+0x234/0x300 fs/ext4/inline.c:234
...
GitHub Actions for Kernel Modules
Basic CI Pipeline
# .github/workflows/kernel-module-ci.yml
name: Kernel Module CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
kernel_version: ['6.1', '6.6', '6.12']
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential bc bison flex \
libelf-dev libssl-dev linux-headers-$(uname -r)
- name: Build kernel module
run: |
make KVER=${{ matrix.kernel_version }}.0-generic
- name: Check module
run: |
modinfo *.ko
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: module-${{ matrix.kernel_version }}
path: '*.ko'
Advanced CI with Cross-Compilation
# .github/workflows/cross-build.yml
name: Cross-Platform Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- arch: arm64
cross_compile: aarch64-linux-gnu-
defconfig: defconfig
- arch: arm
cross_compile: arm-linux-gnueabihf-
defconfig: multi_v7_defconfig
- arch: riscv
cross_compile: riscv64-linux-gnu-
defconfig: defconfig
- arch: x86_64
cross_compile: ""
defconfig: defconfig
steps:
- uses: actions/checkout@v4
- name: Install cross-compiler
run: |
sudo apt-get update
if [ "${{ matrix.cross_compile }}" = "aarch64-linux-gnu-" ]; then
sudo apt-get install -y gcc-aarch64-linux-gnu
elif [ "${{ matrix.cross_compile }}" = "arm-linux-gnueabihf-" ]; then
sudo apt-get install -y gcc-arm-linux-gnueabihf
elif [ "${{ matrix.cross_compile }}" = "riscv64-linux-gnu-" ]; then
sudo apt-get install -y gcc-riscv64-linux-gnu
fi
sudo apt-get install -y build-essential bc bison flex \
libelf-dev libssl-dev
- name: Get kernel source
run: |
git clone --depth=1 --branch v6.12 \
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
- name: Configure
run: |
cd linux
make ARCH=${{ matrix.arch }} \
CROSS_COMPILE=${{ matrix.cross_compile }} \
${{ matrix.defconfig }}
- name: Build
run: |
cd linux
make ARCH=${{ matrix.arch }} \
CROSS_COMPILE=${{ matrix.cross_compile }} \
-j$(nproc)
CI with QEMU Boot Test
# .github/workflows/boot-test.yml
name: Kernel Boot Test
on: [push]
jobs:
boot-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install QEMU
run: |
sudo apt-get update
sudo apt-get install -y qemu-system-x86
- name: Build kernel
run: |
make defconfig
make -j$(nproc)
- name: Create minimal initramfs
run: |
mkdir -p rootfs/{bin,sbin,etc,proc,sys,dev}
# Install busybox
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar xf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
make defconfig
sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config
make -j$(nproc)
make CONFIG_PREFIX=../rootfs install
cd ..
# Create init
cat > rootfs/init << 'INITEOF'
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
echo "Boot test passed!"
poweroff -f
INITEOF
chmod +x rootfs/init
cd rootfs && find . | cpio -o -H newc | gzip > ../initramfs.cpio.gz
- name: Boot test with QEMU
run: |
timeout 60 qemu-system-x86_64 \
-kernel arch/x86/boot/bzImage \
-initrd initramfs.cpio.gz \
-append "console=ttyS0 rdinit=/init" \
-nographic \
-no-reboot \
2>&1 | tee boot.log
grep -q "Boot test passed!" boot.log
Performance Testing in CI
Kernel Performance Benchmarks
# .github/workflows/perf-test.yml
name: Performance Regression Test
on:
pull_request:
branches: [main]
jobs:
perf:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build kernel with perf
run: |
make defconfig
scripts/config --enable CONFIG_PERF_EVENTS
make -j$(nproc)
make -C tools/perf
- name: Run benchmark
run: |
# Boot test VM and run benchmarks
# Example: network throughput test
tools/perf/bench/sched/messaging -g 4 -l 1000 > perf_results.txt
# Compare against baseline
if [ -f baseline_results.txt ]; then
python3 scripts/ci/compare_perf.py baseline_results.txt perf_results.txt
fi
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: perf-results
path: perf_results.txt
ktest: Automated Kernel Testing
ktest is a kernel testing framework by Steven Rostedt:
# ktest configuration file
cat > ktest.conf << 'EOF'
[MACHINE]
MACHINE = localhost
SSH_PORT = 2222
[BUILD]
BUILD_TYPE = make
OUTPUT_DIR = /tmp/ktest/output
MAKE_CMD = make -j$(nproc)
[TEST]
TEST_TYPE = boot
POWER_CYCLE = qemu
QEMU_CMD = qemu-system-x86_64 -kernel ${KERNEL} -append "console=ttyS0" -nographic
[DEFAULTS]
TIMEOUT = 300
EOF
# Run ktest
$ ktest.pl ktest.conf
CI Pipeline Architecture
flowchart TD
PATCH[Developer Patch] --> GATEWAY[CI Gateway]
GATEWAY --> BUILD{Build Matrix}
BUILD --> X86[x86_64 Build]
BUILD --> ARM64[ARM64 Cross Build]
BUILD --> RISCV[RISC-V Cross Build]
X86 --> STATIC[Static Analysis]
ARM64 --> STATIC
RISCV --> STATIC
STATIC --> BOOT[Boot Tests]
BOOT --> QEMU[QEMU Boot]
BOOT --> HW[Hardware Boot]
QEMU --> KSELFTEST[kselftest Suite]
HW --> LTP[LTP Tests]
KSELFTEST --> REPORT[CI Report]
LTP --> REPORT
STATIC --> REPORT
REPORT --> LKML[Email to Developer]
REPORT --> DASHBOARD[CI Dashboard]
style GATEWAY fill:#f96,stroke:#333,stroke-width:2px
style REPORT fill:#9f9,stroke:#333,stroke-width:2px
Setting Up Your Own Kernel CI
Minimal CI with Git + Make + QEMU
#!/bin/bash
# Simple kernel CI script
set -e
KERNEL_REPO="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git"
BRANCH="master"
BUILD_DIR="/tmp/kernel-ci"
# Clone/update
if [ -d "$BUILD_DIR" ]; then
cd "$BUILD_DIR"
git pull
else
git clone --depth=1 --branch "$BRANCH" "$KERNEL_REPO" "$BUILD_DIR"
cd "$BUILD_DIR"
fi
# Configure
make defconfig
scripts/config --enable CONFIG_DEBUG_INFO
scripts/config --enable CONFIG_KASAN
scripts/config --disable CONFIG_DEBUG_INFO_BTF # Faster builds
# Build
make -j$(nproc) 2>&1 | tee build.log
# Static analysis (if sparse available)
if command -v sparse &> /dev/null; then
make C=1 CHECK=sparse -j$(nproc) 2>&1 | tee sparse.log
fi
# Create initramfs and boot test
if command -v qemu-system-x86_64 &> /dev/null; then
# Create minimal initramfs (see boot-test.yml above)
make_initramfs
# Boot test
timeout 60 qemu-system-x86_64 \
-kernel arch/x86/boot/bzImage \
-initrd initramfs.cpio.gz \
-append "console=ttyS0 rdinit=/init" \
-nographic -no-reboot 2>&1 | tee boot.log
if grep -q "Boot test passed!" boot.log; then
echo "BOOT TEST: PASS"
else
echo "BOOT TEST: FAIL"
exit 1
fi
fi
echo "CI completed successfully"
References and Further Reading
-
Intel 0-Day: https://01.org/lkp/documentation/0-day
-
KernelCI: https://kernelci.org/
-
LAVA: https://lavasoftware.org/
-
syzkaller: https://github.com/google/syzkaller
-
ktest: https://git.kernel.org/pub/scm/linux/kernel/git/rostedt/ktest.git
-
kernelci-core: https://github.com/kernelci/kernelci-core
-
Linux Kernel Selftests: https://www.kernel.org/doc/html/latest/dev-tools/kselftest.html
-
LWN.net Kernel CI coverage: https://lwn.net/Kernel/
-
“Kernel Testing and CI” — Linux Plumbers Conference talks
-
GitHub Actions documentation: https://docs.github.com/en/actions
-
kernelci API: https://api.kernelci.org/
Related Topics
- Building the Kernel — the build process
- Cross-Compilation — building for multiple architectures
- Linux Kernel Development Model — how CI fits into the workflow
- Key Kernel Subsystems — what CI tests
- Building Packages — packaging CI artifacts