Installing Alpine Linux with Custom Kernel: Complete Build Guide
I’ll show you how to install Alpine Linux with your own custom kernel. This gives you complete control over what features get included and lets you optimize for your specific hardware.
Introduction
Building a custom kernel might sound scary, but it’s actually pretty straightforward once you know the steps. I’ve done this dozens of times for different projects, and it’s really useful when you need specific drivers or want to remove unnecessary features.
The main reason I build custom kernels is for embedded systems or servers where I need exact control over what’s running. Sometimes you need a driver that’s not included in the standard kernel, or you want to strip out everything unnecessary to save memory.
Why You Need This
- Remove unnecessary drivers and features to save memory
- Add specific hardware support not in standard kernel
- Enable experimental features for testing
- Optimize performance for your exact hardware
Prerequisites
You’ll need these things first:
- A working Linux system for building (can be Alpine or another distro)
- At least 4GB RAM and 10GB free disk space
- Basic knowledge of kernel configuration
- Understanding of your hardware requirements
- Internet connection for downloading kernel sources
Step 1: Set Up Build Environment
Installing Build Tools
I’ll start by setting up all the tools we need to compile a kernel.
What we’re doing: Installing the compiler toolchain and kernel build dependencies.
# Update package lists first
sudo apk update
# Install essential build tools
sudo apk add build-base linux-headers ncurses-dev
# Install kernel build dependencies
sudo apk add bc elfutils-dev openssl-dev
Code explanation:
build-base
: Provides GCC compiler and essential build toolslinux-headers
: Headers needed for kernel compilationncurses-dev
: Library for kernel configuration menu interfacebc
: Calculator used during kernel build processelfutils-dev
: Tools for handling ELF binary filesopenssl-dev
: SSL support for kernel signing
Expected Output:
(1/15) Installing binutils (2.40-r7)
(2/15) Installing gcc (12.2.1_git20220924-r10)
OK: 347 MiB in 87 packages
Creating Build Directory
What we’re doing: Setting up a dedicated workspace for kernel compilation.
# Create build directory
mkdir -p ~/kernel-build
cd ~/kernel-build
# Set up environment variables
export KBUILD_OUTPUT=$PWD/build
export ARCH=x86_64
Code explanation:
mkdir -p ~/kernel-build
: Creates directory structure if it doesn’t existKBUILD_OUTPUT
: Tells kernel build system where to put compiled filesARCH=x86_64
: Sets target architecture for compilation
Step 2: Download Kernel Source
Getting Kernel Source
What we’re doing: Downloading the Linux kernel source code from official repository.
# Download latest stable kernel
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.30.tar.xz
# Extract the source
tar -xf linux-6.6.30.tar.xz
cd linux-6.6.30
# Verify source integrity
ls -la
Code explanation:
wget
: Downloads kernel source archive from official mirrortar -xf
: Extracts compressed archivels -la
: Lists contents to verify extraction worked
Expected Output:
drwxr-xr-x 25 user user 4096 May 26 10:15 .
drwxr-xr-x 3 user user 4096 May 26 10:14 ..
-rw-r--r-- 1 user user 496 May 26 10:15 COPYING
-rw-r--r-- 1 user user 98113 May 26 10:15 CREDITS
drwxr-xr-x 4 user user 4096 May 26 10:15 Documentation
Alternative: Using Git
What we’re doing: Getting kernel source with git for easier updates and version control.
# Clone kernel repository (alternative method)
git clone --depth 1 --branch v6.6.30 \
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
# Enter source directory
cd linux
Code explanation:
--depth 1
: Only downloads latest commit to save bandwidth--branch v6.6.30
: Gets specific kernel version- Git method allows easier updates and patch management
Step 3: Configure Kernel
Starting with Base Configuration
What we’re doing: Creating initial kernel configuration based on current system.
# Copy current system config as starting point
zcat /proc/config.gz > .config
# Or use default Alpine config
make defconfig
# Start interactive configuration
make menuconfig
Code explanation:
/proc/config.gz
: Contains configuration of currently running kerneldefconfig
: Creates minimal default configurationmenuconfig
: Opens text-based configuration interface
Important Configuration Areas:
Navigate through these sections in menuconfig:
General Setup
├── Local version (add custom identifier)
├── Kernel compression (choose lz4 for faster boot)
└── Control Group support (enable if using containers)
Processor Type and Features
├── Processor family (select your CPU type)
├── Maximum number of CPUs (match your hardware)
└── Preemption Model (choose based on use case)
Device Drivers
├── Block devices (enable what you need)
├── Network device support (your network cards)
├── Graphics support (if using desktop)
└── USB support (usually needed)
Custom Configuration Example
What we’re doing: Modifying configuration for a minimal server system.
# Enable specific features programmatically
scripts/config --enable CONFIG_IKCONFIG
scripts/config --enable CONFIG_IKCONFIG_PROC
scripts/config --disable CONFIG_SOUND
scripts/config --disable CONFIG_DRM
# Set local version string
scripts/config --set-str CONFIG_LOCALVERSION "-custom"
Code explanation:
--enable CONFIG_IKCONFIG
: Makes config available in /proc--disable CONFIG_SOUND
: Removes audio support for servers--set-str CONFIG_LOCALVERSION
: Adds custom identifier to kernel version
Tip: I always enable CONFIG_IKCONFIG so I can see how the kernel was configured later.
Step 4: Compile the Kernel
Building Kernel and Modules
What we’re doing: Compiling the kernel source code into bootable image and loadable modules.
# Clean any previous builds
make clean
# Compile kernel with parallel jobs
make -j$(nproc) bzImage modules
# Check compilation progress
echo "Kernel compilation completed"
Code explanation:
make clean
: Removes any leftover build files-j$(nproc)
: Uses all available CPU cores for faster compilationbzImage
: Creates compressed bootable kernel imagemodules
: Builds loadable kernel modules
Expected Output:
CC kernel/fork.o
CC kernel/exec_domain.o
CC kernel/panic.o
...
LD [M] drivers/net/ethernet/intel/e1000e/e1000e.ko
Kernel: arch/x86/boot/bzImage is ready
Warning: Compilation can take 30 minutes to several hours depending on your hardware and configuration.
Installing Modules
What we’re doing: Installing compiled kernel modules to system directories.
# Install modules to /lib/modules
sudo make modules_install
# Verify modules were installed
ls /lib/modules/
Code explanation:
modules_install
: Copies compiled modules to standard location- Modules are needed for hardware drivers and filesystem support
Step 5: Install Custom Kernel
Installing Kernel Image
What we’re doing: Copying the compiled kernel to boot directory and updating bootloader.
# Copy kernel to boot directory
sudo cp arch/x86/boot/bzImage /boot/vmlinuz-6.6.30-custom
# Copy system map for debugging
sudo cp System.map /boot/System.map-6.6.30-custom
# Copy kernel config for reference
sudo cp .config /boot/config-6.6.30-custom
Code explanation:
vmlinuz-6.6.30-custom
: Bootable kernel image with custom nameSystem.map
: Symbol table for kernel debuggingconfig
: Configuration used to build this kernel
Creating Initial RAM Disk
What we’re doing: Building initramfs that loads before root filesystem.
# Generate initramfs for custom kernel
sudo mkinitfs -k 6.6.30-custom -o /boot/initramfs-6.6.30-custom
# Verify initramfs was created
ls -lh /boot/initramfs-6.6.30-custom
Code explanation:
mkinitfs
: Alpine tool for creating initial RAM filesystem-k 6.6.30-custom
: Specifies kernel version for module loading- Initramfs contains essential drivers needed for boot process
Step 6: Configure Bootloader
Updating GRUB Configuration
What we’re doing: Adding custom kernel entry to bootloader menu.
# Edit GRUB configuration
sudo nano /etc/default/grub
# Add custom entry
echo 'GRUB_DEFAULT=0' | sudo tee -a /etc/default/grub
# Update GRUB configuration
sudo grub-mkconfig -o /boot/grub/grub.cfg
Configuration example:
Before:
GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="Alpine"
After:
GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="Alpine"
GRUB_CMDLINE_LINUX_DEFAULT="quiet"
Manual GRUB Entry
What we’re doing: Creating specific boot entry for custom kernel.
# Add custom entry to GRUB configuration
sudo tee -a /etc/grub.d/40_custom <<EOF
menuentry 'Alpine Linux (Custom Kernel 6.6.30)' {
linux /boot/vmlinuz-6.6.30-custom root=/dev/sda1 ro
initrd /boot/initramfs-6.6.30-custom
}
EOF
# Regenerate GRUB menu
sudo grub-mkconfig -o /boot/grub/grub.cfg
Code explanation:
menuentry
: Creates bootloader menu itemlinux /boot/vmlinuz-6.6.30-custom
: Points to our custom kernelroot=/dev/sda1
: Adjust to match your root partitioninitrd
: Specifies initial RAM disk to load
Step 7: Alpine Linux Installation
Installing Alpine with Custom Kernel
What we’re doing: Setting up Alpine Linux to use our custom kernel by default.
# Mount Alpine installation media
mkdir -p /mnt/alpine
sudo mount /dev/sr0 /mnt/alpine
# Run Alpine setup with custom kernel
sudo setup-alpine
# During setup, choose custom kernel when prompted
Installation Process:
- Choose keyboard layout
- Set hostname and networking
- Configure disk and create partitions
- Select custom kernel during package selection
- Complete installation process
Post-Installation Kernel Setup
What we’re doing: Ensuring custom kernel is properly integrated after Alpine installation.
# Copy custom kernel files to new installation
sudo cp /boot/vmlinuz-6.6.30-custom /mnt/boot/
sudo cp /boot/initramfs-6.6.30-custom /mnt/boot/
sudo cp -r /lib/modules/6.6.30-custom /mnt/lib/modules/
# Update bootloader in new installation
sudo chroot /mnt update-bootloader
Code explanation:
cp /boot/vmlinuz-6.6.30-custom
: Copies kernel to new systemcp -r /lib/modules/
: Copies all kernel moduleschroot /mnt
: Changes root to new installation for commandsupdate-bootloader
: Updates Alpine’s bootloader configuration
Practical Examples
Example 1: Minimal Server Kernel
What we’re doing: Creating optimized kernel for headless server with only essential features.
# Disable unnecessary features for server
scripts/config --disable CONFIG_SOUND
scripts/config --disable CONFIG_DRM
scripts/config --disable CONFIG_FB
scripts/config --disable CONFIG_USB_HID
# Enable server-specific features
scripts/config --enable CONFIG_IKCONFIG
scripts/config --enable CONFIG_CGROUPS
scripts/config --enable CONFIG_NAMESPACES
# Set server-optimized options
scripts/config --enable CONFIG_PREEMPT_NONE
scripts/config --set-val CONFIG_HZ 1000
Code explanation:
- Disables graphics, sound, and HID devices not needed on servers
- Enables container support and process isolation features
- Sets non-preemptive scheduling for better server performance
- Increases timer frequency for better responsiveness
Example 2: Embedded System Kernel
What we’re doing: Building ultra-minimal kernel for embedded device with 512MB RAM.
# Minimize kernel size
scripts/config --enable CONFIG_CC_OPTIMIZE_FOR_SIZE
scripts/config --disable CONFIG_MODULES
scripts/config --disable CONFIG_SWAP
# Remove debugging features
scripts/config --disable CONFIG_DEBUG_KERNEL
scripts/config --disable CONFIG_PRINTK
# Enable embedded-specific features
scripts/config --enable CONFIG_EMBEDDED
scripts/config --set-val CONFIG_LOG_BUF_SHIFT 14
Code explanation:
CONFIG_CC_OPTIMIZE_FOR_SIZE
: Optimizes for smallest binary sizeCONFIG_MODULES
: Disables loadable modules to save memoryCONFIG_LOG_BUF_SHIFT 14
: Reduces kernel log buffer size- Removes debugging to reduce kernel size and improve performance
Troubleshooting
Boot Issues
Problem: System won’t boot with custom kernel Solution: Boot from rescue media and check configuration
# Boot from Alpine rescue media
# Mount your root partition
sudo mount /dev/sda1 /mnt
# Check kernel files exist
ls -la /mnt/boot/vmlinuz-6.6.30-custom
# Verify initramfs was created correctly
lsinitramfs /mnt/boot/initramfs-6.6.30-custom | head
Module Loading Problems
Problem: Hardware not working due to missing drivers Solution: Rebuild kernel with required modules
# Check what modules current system uses
lsmod | grep -i network
# Find config option for specific driver
cd linux-source
find . -name "*config*" | xargs grep -i "your_driver_name"
# Enable required driver and rebuild
scripts/config --enable CONFIG_YOUR_DRIVER
make -j$(nproc) bzImage modules
Memory Issues During Build
Problem: Not enough memory for compilation Solution: Use swap or reduce parallel jobs
# Add temporary swap file
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# Reduce parallel jobs
make -j2 bzImage modules
Best Practices
-
Always Keep Working Kernel:
# Keep old kernel as backup sudo cp /boot/vmlinuz /boot/vmlinuz.backup
-
Version Your Custom Kernels:
- Use meaningful version strings like
-server-v1
or-embedded-v2
- Keep build logs and configurations
- Use meaningful version strings like
-
Test Before Production:
- Always test custom kernels in virtual machines first
- Verify all required hardware works properly
Verification
To verify the custom kernel installation:
# Check kernel version
uname -r
# Verify custom features are enabled
zcat /proc/config.gz | grep CONFIG_LOCALVERSION
# Check available modules
find /lib/modules/$(uname -r) -name "*.ko" | wc -l
Expected Output:
6.6.30-custom
CONFIG_LOCALVERSION="-custom"
1247
Wrapping Up
You just learned how to:
- Set up kernel build environment on Alpine Linux
- Configure and compile custom Linux kernel
- Install custom kernel with proper module support
- Integrate custom kernel into Alpine Linux installation
That’s it! You now know how to build and install Alpine Linux with custom kernels. This gives you complete control over your system’s capabilities and performance. I use this technique for specialized servers and embedded systems where standard kernels are too bloated or missing specific features.