Building Argo OS: 100 Days of Creating a Custom Linux Distribution

Building Argo OS: 100 Days Into Custom Linux

Sometimes you look at a perfectly functional openSUSE installation and think: โ€œThis is too easy. I should make my life harder.โ€

Thatโ€™s not exactly how it started, but close.

Iโ€™ve been running a homelab for over 12 years now. Started with seedbox scripts, moved through ESXi (2016-2019), landed on Proxmox, and accumulated massive storage across Unraid and Synology units. My main workstation is a 12-year-old i7-4790K that I built on the high end back in the dayโ€”32GB RAM, NVMe storage, and an RTX 4070 Ti that I upgraded to recently.

The machine still runs great. Linux gave it new life compared to Windows. But Gentoo compilation times? Those were killing me. An update could take anywhere from a few hours to a few days depending on what needed rebuilding. That takes away the fun of having a computer, period.

I wanted the optimization of Gentoo (-march=native is a hell of a drug) but I didnโ€™t want to spend my life watching packages compile. I wanted to design and customize my own systemโ€”make it mineโ€”without the time tax.

This is the story of Argo OS: a Gentoo-based distribution I built over nights and weekends starting October 2025. Itโ€™s part technical documentation, part disaster log, and part therapy session.


The Journey at a Glance

This is a 4-part series documenting the entire build:

PartWhat HappenedKey Lesson
Part 1: First Principles (this page)Started with ext4. Lost everything. Rebuilt with Btrfs.Never trust a filesystem without snapshots.
Part 2: The Qt6 CrisisA routine update broke KDE. Rolled back in 2 minutes.Snapshots arenโ€™t just backupsโ€”theyโ€™re undo buttons.
Part 3: Cloud & CodeLocal backups donโ€™t survive house fires. Built encrypted cloud sync. Accidentally wrote a package manager.Scope creep is real, but sometimes useful.
Part 4: The Hybrid VisionRealized Gentoo is great for hardware, Nix is great for dotfiles. Combined them.Use the right tool for each layer.

Supporting technical guides:


Part 1: First Principles & The Foundation

October 22, 2025

The Architecture Vision

Before I ran a single emerge, I mapped out the ecosystem. This wasnโ€™t going to be just a desktop install. It was going to be a distribution.

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    THE ARGO OS ECOSYSTEM                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                              โ”‚
โ”‚  BINHOST (Compilation)      DRIVER (Desktop)                 โ”‚
โ”‚  โ”œโ”€โ”€ Compiles packages      โ”œโ”€โ”€ Never compiles               โ”‚
โ”‚  โ”œโ”€โ”€ Hosts binary repo      โ”œโ”€โ”€ Pulls binaries               โ”‚
โ”‚  โ””โ”€โ”€ Borrows idle time      โ””โ”€โ”€ Daily use                    โ”‚
โ”‚         โ”‚                          โ–ฒ                         โ”‚
โ”‚         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ binpkgs โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                         โ”‚
โ”‚                                                              โ”‚
โ”‚  GOLDEN IMAGE (VM)          BACKUP (Cloud)                   โ”‚
โ”‚  โ”œโ”€โ”€ Bare-bones Stage 3     โ”œโ”€โ”€ Encrypted Btrfs sends        โ”‚
โ”‚  โ”œโ”€โ”€ SSH-ready baseline     โ”œโ”€โ”€ Google Drive via rclone      โ”‚
โ”‚  โ””โ”€โ”€ Clone in 30 seconds    โ””โ”€โ”€ Off-site protection          โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The idea: make compilation every other computerโ€™s problem. Borrow idle time from machines that would otherwise be doing nothing. Build once, deploy everywhere.

First Contact: The VM Foundation

I started safely. Spin up a VM, install Gentoo. Standard procedure.

For the uninitiated, installing Gentoo involves booting a minimal live environment, formatting disks manually, unpacking a โ€œStage 3โ€ tarball (a minimal root filesystem), and then compiling the kernel and bootloader yourself. It takes me about 5 hours to get to a working TTY with SSH.

The Initial Specs:

  • Host: openSUSE Tumbleweed with KVM/QEMU
  • VM: gentoo-desktop
  • Storage: qcow2 disk image
  • Target: x86-64-v3 (Haswell optimized)
  • Init System: OpenRC (not systemdโ€”I understand OpenRC)
  • Filesystem: ext4

Wait. ext4?

Yes. And that was Mistake #1.


The make.conf That Started It All

Every Gentoo system lives and dies by /etc/portage/make.conf. This file controls how every package gets compiled.

# /etc/portage/make.conf (October 2025 - VM version)

# CPU optimization - compile for this exact machine
COMMON_FLAGS="-O2 -pipe -march=native"
CFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${COMMON_FLAGS}"

# Parallel compilation - use all 8 vCPUs
MAKEOPTS="-j8"

# What we want in our packages
USE="X gtk qt5 qt6 kde plasma wayland -systemd -gnome pulseaudio pipewire"

# Video drivers for VM
VIDEO_CARDS="vmware virtio"

# Accept ~amd64 keywords for fresh packages
ACCEPT_KEYWORDS="~amd64"

# The critical line - create binary packages automatically
FEATURES="buildpkg binpkg-multi-instance"

The key decisions:

-march=native: Compiles for your specific CPU. Maximum performance. The catch: binaries wonโ€™t work on different CPUs. Iโ€™d learn to use -march=haswell later for broader compatibility across my fleet.

MAKEOPTS="-j8": Parallel compilation. Uses all available cores. Dramatically speeds up buildsโ€”but on my 4790K, โ€œdramaticallyโ€ still meant hours for big packages.

FEATURES="buildpkg": Every time Portage compiles a package, it also creates a binary tarball in /var/cache/binpkgs/. I wasnโ€™t just installing software; I was building a repository.

-systemd: No systemd. OpenRC is simpler, I understand it, and it doesnโ€™t try to manage my life.


The Compilation Marathon

Days 1-7. October 22-27, 2025.

Day 1-2: Base system

GCC. Glibc. Binutils. The building blocks. Hours of watching text scroll by.

Day 3-4: KDE Plasma desktop

emerge --ask kde-plasma/plasma-meta

4-6 hours of compilation on a VM with 8 vCPUs. This is where I started questioning whether I should just use Arch.

Day 5-6: Essential applications

Firefox (compiled with PGO), vim, git, development tools.

Day 7: Configuration and polish

Theme tweaks, keyboard shortcuts, SSH configuration.

The Statistics:

  • Installed packages: 1,571
  • Package database files: 55,782 files in /var/db/pkg/
  • Compilation time: 40-50 hours total
  • System storage: ~35GB
  • Binary packages created: 1,124 .gpkg.tar files, 28.3GB

By October 27th, it was perfect. A lean, mean, optimized machine.


The Crash (October 27)

I shut down the VM. I went to bed.

The next morning, I booted it up.

Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

I stared at the screen. Rebooted. Same error.

The Autopsy

I mounted the qcow2 image from the host:

sudo modprobe nbd
sudo qemu-nbd --connect=/dev/nbd0 /var/lib/libvirt/images/gentoo-desktop.qcow2
sudo mount /dev/nbd0p3 /mnt/gentoo

Ran fsck:

sudo fsck.ext4 -y /dev/nbd0p3

The output was horrifying:

/dev/nbd0p3: recovering journal
Clearing orphaned inode 262245 (uid=0, gid=0, mode=0100644, size=0)
Clearing orphaned inode 262244 (uid=0, gid=0, mode=0100644, size=0)
...
/dev/nbd0p3: ***** FILE SYSTEM WAS MODIFIED *****

Some freak host-system write error had mangled the ext4 superblock. fsck โ€œrecoveredโ€ itโ€”by deleting corrupted files. Half of /var/db/pkg was gone.

What /var/db/pkg Means

In Gentoo, /var/db/pkg is everything. Itโ€™s the database of whatโ€™s installed.

The critical files at risk:

  1. /var/db/pkg/ - 55,782 files tracking every installed package
  2. /var/lib/portage/world - The list of explicitly installed packages
  3. /etc/portage/ - All configuration (weeks of USE flag optimization)

If you lose /var/db/pkg, you have a zombie OS. The system runs, but you canโ€™t update, remove, or install anything. Portage has no idea whatโ€™s on the disk.

50 hours of work. Gone.


The Backup That Saved Everything

Pure luck.

I had automated VM backups running on my NAS. Daily at 2:00 AM. 7-day retention.

There was a snapshot from October 27th at 2:00 AMโ€”taken 6 hours before the corruption.

The backup wasnโ€™t planned for this disaster. But it saved 40+ hours of work.

Backups you donโ€™t have are useless. Backups you do have are priceless.


The Pivot: Embracing Btrfs

I sat there staring at the error message, and I realized: If this was openSUSE, I would just type snapper rollback and be drinking coffee by now.

I nuked the VM.

We were starting over. But this time, we were doing it right.

The New Filesystem Layout

I adopted the openSUSE partition strategyโ€”complex, but it allows snapshotting the system without rolling back personal files.

The Partition Map:

DeviceSizeFilesystemMount Point
/dev/vda1512MBFAT32/boot/efi
/dev/vda28GBswap[swap]
/dev/vda3241.5GBBtrfs/

The Subvolume Architecture:

# Create the subvolume structure
mount /dev/vda3 /mnt/gentoo
cd /mnt/gentoo

btrfs subvolume create @
btrfs subvolume create @home
btrfs subvolume create @snapshots
btrfs subvolume create @var-cache
btrfs subvolume create @var-log
SubvolumeMountPurpose
@/Root filesystemโ€”frequently snapshotted
@home/homeUser dataโ€”separate snapshot schedule
@snapshots/.snapshotsSnapper storage (excluded from snapshots)
@var-cache/var/cachePackage cacheโ€”exclude from snapshots
@var-log/var/logLogsโ€”persist through rollback for debugging

Why separate /var/log? If your system crashes and you roll back to a snapshot before the crash, you want the logs from the crash to persist. Otherwise, you canโ€™t debug what happened.


Mount Options Matter

The /etc/fstab for Btrfs:

UUID=xxx  /           btrfs  defaults,subvol=@,compress=zstd:3,noatime,discard=async,space_cache=v2  0 1
UUID=xxx  /home       btrfs  defaults,subvol=@home,compress=zstd:3,noatime  0 2
UUID=xxx  /.snapshots btrfs  defaults,subvol=@snapshots  0 2
UUID=xxx  /var/cache  btrfs  defaults,subvol=@var-cache,noatime  0 0
UUID=xxx  /var/log    btrfs  defaults,subvol=@var-log,noatime  0 0

What each option does:

  • compress=zstd:3 โ€” Transparent compression. 20-30% space savings. Negligible CPU overhead.
  • noatime โ€” Donโ€™t update file access times. Fewer writes, better SSD life.
  • discard=async โ€” SSD TRIM support without blocking operations.
  • space_cache=v2 โ€” Faster free space tracking.

Snapper: The 2-Minute Rollback

Snapper is openSUSEโ€™s snapshot management tool.

emerge sys-fs/btrfs-progs app-backup/snapper sys-boot/grub-btrfs

The OpenRC challenge: Snapper expects systemd timers. Gentoo uses OpenRC. No problemโ€”cron works fine:

# /etc/cron.hourly/snapper-timeline
#!/bin/bash
/usr/bin/snapper -c root create --cleanup-algorithm timeline --description "timeline"
# /etc/cron.daily/snapper-cleanup
#!/bin/bash
/usr/bin/snapper -c root cleanup timeline
/usr/bin/snapper -c root cleanup number

The killer feature: automatic package snapshots.

I added Portage hooks that create snapshots before and after every emerge:

# /etc/portage/bashrc.d/snapper-pre.sh
if [[ ${EBUILD_PHASE} == "setup" ]]; then
    snapper -c root create --description "Before emerge ${CATEGORY}/${PN}-${PVR}"
fi

Now every package installation is bracketed by snapshots. Break something? Roll back in 2 minutes.


GRUB Integration

grub-btrfs makes every snapshot bootable from the GRUB menu:

emerge sys-boot/grub-btrfs
grub-mkconfig -o /boot/grub/grub.cfg

Now GRUB shows:

Gentoo Linux (current system)
Advanced options
โ”œโ”€ Gentoo Linux (Snapshot 1) - Fresh installation
โ”œโ”€ Gentoo Linux (Snapshot 2) - Before emerge firefox
โ”œโ”€ Gentoo Linux (Snapshot 3) - After emerge firefox
โ””โ”€ ...

You can test a snapshot before committing. Boot in, verify everything works, then decide.


The First Stability Milestone (November 15)

By mid-November, I had the system on bare metal.

We had:

  1. KDE Plasma 6: Running on proprietary NVIDIA drivers (RTX 4070 Ti)
  2. Sound: Pipewire (after fighting with ALSA for 3 days)
  3. Browsers: Firefox (compiled with PGO)
  4. Snapshots: Hourly timeline, pre/post package, GRUB bootable

And the test that mattered:

snapper create -d "Before breaking things"
rm -rf /lib/modules
# System is now dead. No kernel modules.
reboot
# Select snapshot from GRUB
# Desktop loads.

It worked. I had achieved the dream: An indestructible rolling release.


What I Learned

After the first month:

ext4 Is Fine Until It Isnโ€™t

Traditional filesystems donโ€™t have snapshots. One corruption event, one bad update, one rm -rfโ€”and youโ€™re rebuilding from scratch.

Btrfs snapshots arenโ€™t a luxury. For a source-based distro where updates can break anything, theyโ€™re a requirement.

Binary Packages Are Essential

Nobody should compile KDE Plasma more than once. Build it on a dedicated machine, serve the binary, install in seconds.

PackageCompile TimeBinary InstallSavings
KDE Plasma12 hours45 minutes94%
LibreOffice2 hours2 minutes98%
Firefox45 minutes30 seconds99%

The Golden Image Is a Baseline, Not a Clone

My โ€œgolden imageโ€ isnโ€™t a fully-loaded system. Itโ€™s a bare-bones Stage 3 with SSH ready. Cloning takes 30 seconds. From there, I pull binaries from the binhost to customize it.

This keeps the golden image simple and the binhost as the source of truth for what packages look like.

Test Your Backups

A backup youโ€™ve never tested is not a backup. Itโ€™s a hope.

I now intentionally break things just to verify rollback still works.


What Comes Next

In Part 2, the system breaks againโ€”this time from a routine update. A Qt6/elogind mismatch brought down KDE Plasma. But this time, recovery took 2 minutes instead of 50 hours.

The difference? Snapshots.


This is Part 1 of a 4-part series. Continue to Part 2: The Qt6 Crisis.