Skip to content

Build your first rootfs

In this tutorial we will build a small Alpine Linux rootfs and confirm it works by entering it with chroot. By the end you will have a directory containing a miniature Linux environment that you assembled yourself, without a running package manager on your host.

Before we start

Work in a fresh directory. If you don't have flatroot yet, follow Get FlatRoot.

Find a package

Before installing, we can ask flatroot what is available. Search Alpine 3.21 for packages whose names start with busybox:

./flatroot --from alpine:v3.21 search 'busybox*'

You will see several matching packages with their version numbers — busybox, busybox-binsh, busybox-extras, and more. The first run fetches the Alpine package index into a local cache; subsequent searches are instant.

See Query packages for other glob patterns and for running SQL directly against the index.

Build the rootfs

Now install busybox into a new rootfs:

./flatroot --from alpine:v3.21 install -o ./hello-alpine busybox

You will see flatroot resolve dependencies, download the packages in parallel, extract them, and run post-install scripts. When it finishes, the prompt returns. See Install packages for installing multiple packages, adding to an existing rootfs, or targeting other distributions.

Look at what we built

List the top of the new rootfs:

ls ./hello-alpine

You will see a familiar Linux directory tree — bin, etc, lib, sbin, usr, and the standard companions. This is a real root filesystem, identical in shape to an Alpine system.

Find the busybox binary we asked for:

ls -l ./hello-alpine/bin/busybox

The file is there, it is executable, and it belongs to the rootfs you just built.

Enter the rootfs

To run a program inside the rootfs we will use chroot, the standard Linux tool for changing the apparent root directory of a process. Because chroot itself is a privileged operation, this step needs sudo:

sudo chroot ./hello-alpine /bin/busybox ls /

You will see the contents of the rootfs's own / — the directories you just inspected from outside. The ls that just ran was the one baked into busybox inside the rootfs, not the one from your host.

For a deeper look at what chroot does, why it needs root, and when to reach for unprivileged alternatives, see chroot.

Clean up

When you are done exploring, remove the rootfs and the binary:

rm -rf ./hello-alpine ./flatroot

What we did

  • Downloaded the flatroot binary into the working directory.
  • Searched the Alpine package index for available busybox* packages.
  • Built a rootfs from a pinned Alpine release.
  • Entered that rootfs with chroot and ran a program from inside.
  • Tutorials / Reproducible Builds — In this tutorial we will compile a small C program twice — each time against a pinned Debian toolchain installed into its own rootfs — and confirm the resulting binaries are...
    tutorial install pinning reproducibility
  • Appendix / System / chroot — chroot(1) changes the apparent root directory (/) for the running process and its descendants. Programs launched under a chroot see the given directory as / and cannot reach...
    chroot appendix
  • Appendix / System / Rootfs — A rootfs (root filesystem) is the directory tree that appears as / to a running Linux process. It holds the minimum set of files needed for programs to run: the executables in...
    rootfs appendix