← Back to news

Frood, an Alpine Initramfs NAS (2024)

words.filippo.io|38 points|11 comments|by ethanpil|Jun 16, 2026

Frood: An Alpine Initramfs NAS (2024)

My NAS, known as frood, utilizes a somewhat unconventional architecture. Rather than a traditional installation, it consists of one massive initramfs that houses the entire Alpine Linux system.

As long as the system firmware can locate the image, the machine boots seamlessly. The entire environment is defined declaratively within the git repository used to build the initramfs.

The Philosophy of Simplicity

One of the most important aspects of this setup is the lack of a complex Domain Specific Language (DSL). The logic is straightforward:

  • If I need a file at /etc/example.conf, I simply place it in root/etc/example.conf.
  • The heavy lifting is handled by a few hundred lines of scripts that are fully readable.
  • Configuration feels identical to managing a standard Alpine system.

Note: For those already sold on the idea, feel free to jump straight to the "How it works" section.


Evolution and Motivation

I have recently updated the setup for 2025, migrating to a 64-core Ampere Altra processor and replacing the traditional bootloader with a single-file UKI (Unified Kernel Image).

Why run from memory?

Running a system entirely from RAM offers two primary advantages:

  1. Speed: Execution is incredibly fast.
  2. Hardware Longevity: It eliminates wear and tear on system storage (often unreliable SD cards), allowing high-quality drives to be dedicated solely to the ZFS pool.

The Persistence Problem

The challenge with RAM-based systems is persisting configuration changes. Alpine typically solves this via "diskless mode", which uses an overlay file.

Comparison of Persistence Methods:

FeatureStandard Alpine DisklessFrood (Initramfs)
Mechanism.apkovl overlay filesDeclarative Git Repo
Managementlbu(1) toolGit commit \rightarrow Rebuild
ComplexityHigh (many moving parts)Low (files in folders)
ReliabilityProne to mount/apk failuresAtomic and immutable

The standard lbu process is complex: it must find the overlay, apply it, mount filesystems via fstab, and install missing apk packages. In the past year, this broke multiple times for me.

"Erase Your Darlings"

I strongly agree with Graham Christensen's argument for immutable systems. System state often hides in /etc or /var, representing undocumented "quick fixes" that are forgotten over time.

  • "Right, just run myapp-init once."
  • "Just download these ca-certificates to fix that one error."
  • "Touch /etc/ipsec.secrets or the tunnel fails."

These "oh, oops" moments are the ghosts that haunt future upgrades (like the dreaded RHEL 7 to 8 transition). I previously tried using Ansible, but that created a redundant layer: Ansible \rightarrow Deploy \rightarrow lbu \rightarrow apkovl.

While other declarative options exist, I found them lacking:

  • NixOS (Doesn't sound fun)
  • gokrazy (Not ready for ZFS)
  • buildroot or u-root (Embedded toolchains)

Ultimately, I prefer Alpine: it is lightweight, GNU-less, and simple.


How it Works

During the boot process, the Linux kernel expects an initramfs image—a cpio archive acting as the initial root filesystem. While usually used to load modules and pivot to a real rootfs, there is nothing stopping us from putting the entire OS inside it.

The Build Process

The foundation is alpine-make-rootfs, a script of roughly 500 lines designed for container images.

Build Requirements:

  • alpine-make-rootfs script
  • packages definition file
  • setup.sh configuration script
  • root/ directory for skeletal files

The Build Script:

#!/bin/sh
set -e
wget https://raw.githubusercontent.com/alpinelinux/alpine-make-rootfs/v0.7.0/alpine-make-rootfs \
  echo '91ceb95b020260832417b01e45ce02c3a250c4527835d1bdf486bf44f80287dc alpine-make-rootfs' | sha256sum -c \
  chmod +x alpine-make-rootfs

ROOTFS_DEST=$(mktemp -d)

# Prevent mkinitfs from running during apk install
mkdir -p $ROOTFS_DEST/etc/mkinitfs
echo disable_trigger=yes $ROOTFS_DEST/etc/mkinitfs/mkinitfs.conf

export ALPINE_BRANCH=edge
export SCRIPT_CHROOT=yes
export FS_SKEL_DIR=root
export FS_SKEL_CHOWN=root:root
PACKAGES=$(cat packages)
export PACKAGES

./alpine-make-rootfs $ROOTFS_DEST setup.sh

To finalize the image, we exclude the /boot directory (which contains the kernel) and package the rest: -path ./boot -prune -o -print | cpio -o -H newc | gzip $ROOTFS_DEST/boot/initramfs-lts

The mathematical logic of the boot sequence can be simplified as: FirmwareKernel+Initramfs (RAM)Full OS\text{Firmware} \rightarrow \text{Kernel} + \text{Initramfs (RAM)} \rightarrow \text{Full OS}

Packages and Configuration

I use standard server packages. alpine-base provides the essentials: apk, busybox, and openrc. To save space, I use linux-firmware-none (which provides linux-firmware-any) to avoid downloading hundreds of megabytes of unnecessary drivers.

The setup.sh script (Declarative Service Management):

#!/bin/sh
set -e
# System Initialization
rc-update add devfs sysinit
rc-update add dmesg sysinit
rc-update add hwclock boot
rc-update add modules boot
rc-update add sysctl boot
rc-update add hostname boot
rc-update add bootmisc boot
rc-update add syslog boot
rc-update add klogd boot
rc-update add networking boot
rc-update add seedrng boot

# Shutdown/Default Services
rc-update add mount-ro shutdown
rc-update add killprocs shutdown
rc-update add acpid default
rc-update add crond default
rc-update add local default
rc-update add openntpd default
rc-update add sshd default
rc-update add tailscale default

# Set root password
chpasswd -e 'EOF'
root:$6$twsDxnP.TG2M8J4l$7lte7E/ImK4UwoursD7qQCC7XMUothIDb9FTH1MncxYbGQDUQPkC/9pxleTwPxEs3nbatApszxuwc4yj6ucdX1
EOF

Visuals and Context

Filippo Valsorda

"Yeah I think all my objections to Alpine are basically its flaky init and its persistency mechanism... if I run apk at build time to make a chonky initramfs, write 300 lines to replace init, I might be golden... all of the mkinitfs complexity and flakyness is in finding the modules, loading them, finding the root, finding the apk cache, installing it... all of that goes poof."

Alpine Logic

Nature/NAS Vibe