How to Build and Boot a Standalone Initramfs-Embedded Linux Course In Hyderabad

PREV_LEC | NEXT_LEC

Build and Boot a Standalone Initramfs
A hands-on lecture in EmbeddedPathashala’s free embedded Linux course — building, booting, and debugging a standalone initramfs the way real embedded teams do it today.
Free Embedded Linux Course
Chapter 5: Root Filesystem
Lecture 10

If you’ve followed this free embedded Linux course so far, your root filesystem staging directory already has BusyBox, its symlinks, and the shared libraries it needs. The question this lecture answers is simple: how does that staging directory actually turn into something the kernel can boot from? This is one of the most practical skills in any free embedded systems course, because every custom board bring-up starts here.

initramfs
cpio archive
rdinit
QEMU boot
free linux kernel development course
free linux device drivers course

What You Will Learn

Packing a rootfs into a cpio archive
Why ownership matters in a cpio file
The rdinit= kernel parameter
Booting an initramfs under QEMU
Writing a real init script instead of a bare shell
Mounting proc and sysfs at boot

Prerequisites

A staged rootfs directory with BusyBox built (previous lecture)
A built kernel Image for your target architecture
QEMU installed on your Linux host
Comfort with basic shell commands

What a Standalone Initramfs Actually Is

An initramfs is nothing more exotic than a directory tree serialized into a single stream of bytes, using the cpio archive format, and handed to the kernel at boot time. The kernel unpacks that stream directly into a RAM-backed filesystem before any real block device driver has even been probed. That’s the whole trick — the initramfs exists entirely in memory, so the kernel doesn’t need a disk, an SD card, or a network stack ready in order to boot into a usable shell.

There are three broad ways to get an initramfs into a running kernel:

Three ways to build an initramfs

1. A standalone cpio file, loaded by the bootloader alongside the kernel
2. A cpio file baked directly into the kernel image at build time
3. A device table processed by the kernel build system into a cpio archive

This lecture covers option one — the standalone archive — because it’s the fastest way to iterate: rebuild the rootfs, regenerate the archive, and reboot, without ever touching the kernel image itself. The next lecture in this free embedded Linux course covers baking initramfs directly into the kernel, which matters when your bootloader can’t load a second file at all.

Standalone Initramfs Boot Flow
Bootloader
→ loads Kernel Image
→ loads initramfs.cpio.gz
Kernel decompresses cpio
→ populates RAM-backed rootfs
→ runs rdinit program

Packing the Rootfs Into a Cpio Archive

Assuming your staged rootfs lives in ~/ep-rootfs, here is the sequence to pack, compress, and prepare it for booting:

$ cd ~/ep-rootfs
$ find . | cpio -H newc -ov --owner root:root > ../ep-initramfs.cpio
$ cd ..
$ gzip ep-initramfs.cpio
$ ls -lh ep-initramfs.cpio.gz

Two details here matter more than they look:

  • -H newc selects the “new ASCII” cpio format, which is what the kernel’s initramfs unpacker expects. Older cpio variants will simply fail to extract.
  • –owner root:root forces every file in the archive to UID/GID 0, regardless of who actually owns the files on your build host. Without this flag, files you staged as your own non-root user would end up owned by your host UID inside the booted rootfs — which is rarely what you want, and can break setuid tooling like BusyBox’s login applets.

If your bootloader needs a wrapped image (common with U-Boot-based boards), add a header on top of the gzipped archive:

$ mkimage -A arm64 -O linux -T ramdisk -d ep-initramfs.cpio.gz ep-uRamdisk

If you’re only testing under QEMU, you can skip mkimage entirely and hand the gzipped cpio straight to QEMU’s -initrd option, which we’ll do below.

Sizing the Archive

A minimal BusyBox-based rootfs with no kernel modules typically compresses down to a few megabytes. Once you add a real kernel Image and a bootloader on top, total flash or storage requirements climb quickly — which matters a lot on resource-constrained boards. If storage is tight, you have several practical levers:

Technique Effect
Trim unused kernel drivers/subsystems Smaller kernel Image
Configure BusyBox with only needed applets Smaller busybox binary
Use musl libc instead of glibc Much smaller C library footprint
Statically link BusyBox One self-contained binary, no runtime library lookups

Telling the Kernel What to Run First: rdinit=

Once the kernel has unpacked the archive into memory, it needs to know what program to execute as PID 1. For a standalone initramfs, that’s the job of the rdinit= kernel command-line parameter. The simplest possible value is a shell:

rdinit=/bin/sh

This drops you straight into an interactive shell running as PID 1 — extremely useful for early bring-up and for rescuing a board whose real init program is broken, but not something you’d ship in production.

Booting Under QEMU

QEMU’s ARM64 virt machine is the fastest way to test an initramfs without any real hardware. The -initrd option loads the compressed cpio archive directly into memory alongside the kernel:

$ qemu-system-aarch64 -M virt -cpu cortex-a72 -m 512M -nographic \
  -kernel Image \
  -initrd ep-initramfs.cpio.gz \
  -append "console=ttyAMA0 rdinit=/bin/sh"

Expected output on a successful boot:

[    0.912340] Run /bin/sh as init process
/ # 

You now have an interactive root shell running entirely out of RAM. Type exit or press Ctrl-A then X to leave QEMU.

Booting on Real Hardware via U-Boot

On a board running U-Boot, the equivalent sequence loads the kernel, device tree, and the wrapped ramdisk into RAM addresses appropriate for your board, then boots:

=> fatload mmc 0:1 0x40200000 Image
=> fatload mmc 0:1 0x43000000 ep-board.dtb
=> fatload mmc 0:1 0x44000000 ep-uRamdisk
=> setenv bootargs console=ttyS0,115200 rdinit=/bin/sh
=> booti 0x40200000 0x44000000 0x43000000

The exact load addresses and console name depend entirely on your board’s memory map and UART — always check your board’s reference manual rather than copying addresses from someone else’s board.

Mounting proc: Why ps Doesn’t Work Yet

The first thing most people try in a fresh initramfs shell is ps, and the first thing that happens is it fails or prints nothing useful. That’s because /proc is a pseudo-filesystem that the kernel populates on demand — it has to be mounted explicitly, it isn’t there automatically just because the rootfs booted.

/ # ps
ps: can't open '/proc': No such file or directory
/ # mount -t proc proc /proc
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh

Writing a Real Init Script

Booting straight to a bare shell is fine for a five-minute experiment, but any serious board bring-up should run a small init script instead — it’s the natural next step before you eventually adopt a real init system. Point rdinit= at a script rather than at /bin/sh directly:

#!/bin/sh
# ep_init.sh — minimal early-boot script
mount -t proc proc /proc
mount -t sysfs sysfs /sys
echo "ep-rootfs: early mounts complete"
exec /bin/sh
$ chmod +x ~/ep-rootfs/ep_init.sh
$ qemu-system-aarch64 -M virt -cpu cortex-a72 -m 512M -nographic \
  -kernel Image \
  -initrd ep-initramfs.cpio.gz \
  -append "console=ttyAMA0 rdinit=/ep_init.sh"

This one small change — mounting proc and sysfs before dropping to a shell — is exactly the kind of habit that separates a quick hack from a bring-up environment you can actually debug in.

Common Mistakes and Troubleshooting

Symptom Likely Cause Fix
Kernel panics with “no init found” Wrong cpio format, or rdinit path doesn’t exist inside the archive Regenerate with -H newc; double-check the path is relative to the rootfs root
Files show your host UID instead of root Forgot --owner root:root when creating the cpio Rebuild the archive with the ownership flag
mkimage-wrapped ramdisk won’t boot Wrapped the wrong file (compressed cpio needs wrapping, not the raw one) Confirm you passed the .gz file to mkimage
ps / mount show nothing useful /proc and /sys were never mounted Mount them explicitly in your init script

Best Practices

Always pin cpio to newc format
Always force root ownership at archive time
Test under QEMU before touching real hardware
Use a small init script, not a bare shell, once past initial bring-up
Keep the uncompressed cpio around for the embedded-into-kernel workflow

Summary and Key Takeaways

A standalone initramfs is just a cpio archive of your staged rootfs, gzipped, and handed to the kernel via the bootloader. The rdinit= parameter tells the kernel what to run as PID 1 once that archive is unpacked into RAM. This workflow is deliberately the fastest to iterate on in any free embedded Linux course, because rebuilding the rootfs never requires rebuilding the kernel — you only regenerate the archive. In the next lecture of this free linux kernel development course, we bake the same content directly into the kernel image for bootloaders that can’t load a second file.

Frequently Asked Questions

What’s the difference between initramfs and initrd?

initramfs is the modern cpio-based approach used by every current Linux kernel. initrd is an older, block-device-based ramdisk format that predates Linux 2.6 and is now only relevant for MMU-less kernel variants.

Do I need mkimage if I’m only testing under QEMU?

No. QEMU’s -initrd option accepts the gzipped cpio archive directly. mkimage’s U-Boot header is only needed when a real U-Boot bootloader is loading the ramdisk from flash or an SD card.

Why does the cpio archive need –owner root:root?

Without it, every file keeps the UID/GID of whichever user built the archive on your development host, which almost never matches the UID/GID scheme your booted system expects.

Can rdinit= point to a script instead of a shell?

Yes, and it should for anything beyond a five-minute experiment. Any executable script with a valid shebang works, as long as it’s present in the archive and marked executable.

Why doesn’t ps work right after boot?

ps reads process information from /proc, which is a pseudo-filesystem the kernel doesn’t mount automatically. Your init script has to mount it explicitly with mount -t proc proc /proc.

Is a standalone initramfs suitable for production devices?

Sometimes, especially for install/rescue images or diskless systems, but most production embedded Linux devices eventually move to a persistent root filesystem on flash or eMMC, with the initramfs used only for early setup or as a fallback.

What happens if the cpio format is wrong?

The kernel’s initramfs unpacker will fail to extract the archive, and you’ll typically see a kernel panic reporting that no init program could be found, even though your rootfs directory looks correct on disk.

 

Continue the Free Embedded Linux Course

Next up: embedding the initramfs directly into the kernel image and using a device table for reproducible builds.

PREV_LEC | NEXT_LEC

Leave a Reply

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